quicsilver 0.5.0-x86_64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +49 -0
- data/.github/workflows/cibuildgem.yaml +117 -0
- data/.gitignore +21 -0
- data/.gitmodules +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +175 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +67 -0
- data/LICENSE +21 -0
- data/README.md +261 -0
- data/Rakefile +130 -0
- data/benchmarks/baseline.rb +175 -0
- data/benchmarks/compare.rb +398 -0
- data/benchmarks/components.rb +191 -0
- data/benchmarks/helpers.rb +162 -0
- data/benchmarks/latency.rb +128 -0
- data/benchmarks/loss.rb +193 -0
- data/benchmarks/mobile.rb +125 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docs/connection_identity.md +141 -0
- data/examples/README.md +58 -0
- data/examples/benchmark.rb +191 -0
- data/examples/connection_pool_demo.rb +47 -0
- data/examples/example_helper.rb +69 -0
- data/examples/falcon_middleware.rb +44 -0
- data/examples/feature_demo.rb +125 -0
- data/examples/grpc_style.rb +97 -0
- data/examples/latency_aware_routing.rb +382 -0
- data/examples/lifecycle_drain.rb +264 -0
- data/examples/minimal_http3_server.rb +14 -0
- data/examples/priorities.rb +60 -0
- data/examples/protocol_http_server.rb +31 -0
- data/examples/rack_http3_server.rb +42 -0
- data/examples/rails_feature_test.rb +280 -0
- data/examples/server_stats.rb +117 -0
- data/examples/simple_client_test.rb +26 -0
- data/examples/streaming_sse.rb +33 -0
- data/examples/trailers.rb +69 -0
- data/examples/webtransport_smoke.rb +546 -0
- data/examples/worker_load_balancing.rb +425 -0
- data/ext/quicsilver/extconf.rb +124 -0
- data/ext/quicsilver/include/msquic.h +2226 -0
- data/ext/quicsilver/include/msquic_posix.h +696 -0
- data/ext/quicsilver/include/quic_sal_stub.h +314 -0
- data/ext/quicsilver/msquic_patch.rb +68 -0
- data/ext/quicsilver/patches/reliable-reset.patch +723 -0
- data/ext/quicsilver/patches/stream-final-size.patch +47 -0
- data/ext/quicsilver/quicsilver.c +2741 -0
- data/lib/quicsilver/3.4/quicsilver.so +0 -0
- data/lib/quicsilver/4.0/quicsilver.so +0 -0
- data/lib/quicsilver/client/client.rb +708 -0
- data/lib/quicsilver/client/connection_pool.rb +202 -0
- data/lib/quicsilver/client/request.rb +190 -0
- data/lib/quicsilver/errors.rb +39 -0
- data/lib/quicsilver/libmsquic.so.2 +0 -0
- data/lib/quicsilver/protocol/adapter.rb +196 -0
- data/lib/quicsilver/protocol/capsule.rb +38 -0
- data/lib/quicsilver/protocol/connect_stream_decoder.rb +75 -0
- data/lib/quicsilver/protocol/control_stream_parser.rb +117 -0
- data/lib/quicsilver/protocol/datagram.rb +40 -0
- data/lib/quicsilver/protocol/frame_parser.rb +141 -0
- data/lib/quicsilver/protocol/frame_reader.rb +84 -0
- data/lib/quicsilver/protocol/frames.rb +454 -0
- data/lib/quicsilver/protocol/priority.rb +65 -0
- data/lib/quicsilver/protocol/qpack/decoder.rb +127 -0
- data/lib/quicsilver/protocol/qpack/encoder.rb +217 -0
- data/lib/quicsilver/protocol/qpack/header_block_decoder.rb +140 -0
- data/lib/quicsilver/protocol/qpack/huffman.rb +459 -0
- data/lib/quicsilver/protocol/request_encoder.rb +50 -0
- data/lib/quicsilver/protocol/request_parser.rb +289 -0
- data/lib/quicsilver/protocol/response_encoder.rb +107 -0
- data/lib/quicsilver/protocol/response_parser.rb +147 -0
- data/lib/quicsilver/protocol/stream_input.rb +98 -0
- data/lib/quicsilver/protocol/stream_output.rb +59 -0
- data/lib/quicsilver/protocol/unidirectional_stream.rb +12 -0
- data/lib/quicsilver/protocol/web_transport.rb +214 -0
- data/lib/quicsilver/rack/context.rb +33 -0
- data/lib/quicsilver/response.rb +40 -0
- data/lib/quicsilver/server/listener_data.rb +14 -0
- data/lib/quicsilver/server/rack_adapter.rb +77 -0
- data/lib/quicsilver/server/receive_queue.rb +106 -0
- data/lib/quicsilver/server/request_handler.rb +149 -0
- data/lib/quicsilver/server/request_registry.rb +63 -0
- data/lib/quicsilver/server/scheduler.rb +50 -0
- data/lib/quicsilver/server/schedulers/fiber_scheduler.rb +79 -0
- data/lib/quicsilver/server/schedulers/thread_scheduler.rb +76 -0
- data/lib/quicsilver/server/server.rb +1080 -0
- data/lib/quicsilver/server/web_transport_flow_control.rb +270 -0
- data/lib/quicsilver/server/web_transport_manager.rb +281 -0
- data/lib/quicsilver/server/web_transport_registry.rb +40 -0
- data/lib/quicsilver/server/web_transport_session.rb +812 -0
- data/lib/quicsilver/server/web_transport_stream.rb +326 -0
- data/lib/quicsilver/transport/configuration.rb +234 -0
- data/lib/quicsilver/transport/connection.rb +594 -0
- data/lib/quicsilver/transport/connection_stats.rb +53 -0
- data/lib/quicsilver/transport/event_loop.rb +74 -0
- data/lib/quicsilver/transport/inbound_stream.rb +53 -0
- data/lib/quicsilver/transport/stream.rb +43 -0
- data/lib/quicsilver/transport/stream_event.rb +49 -0
- data/lib/quicsilver/transport/stream_id.rb +26 -0
- data/lib/quicsilver/version.rb +3 -0
- data/lib/quicsilver.rb +96 -0
- data/lib/rackup/handler/quicsilver.rb +96 -0
- data/quicsilver.gemspec +66 -0
- metadata +362 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
diff --git c/src/core/api.c i/src/core/api.c
|
|
2
|
+
index 2281da55a..c88b0b641 100644
|
|
3
|
+
--- c/src/core/api.c
|
|
4
|
+
+++ i/src/core/api.c
|
|
5
|
+
@@ -1259,6 +1259,7 @@ MsQuicStreamSend(
|
|
6
|
+
QueueOper = FALSE; // Not necessary if the previous send hasn't been flushed yet.
|
|
7
|
+
}
|
|
8
|
+
*ApiSendRequestsTail = SendRequest;
|
|
9
|
+
+ Stream->ApiSubmittedOffset += SendRequest->TotalLength;
|
|
10
|
+
Status = QUIC_STATUS_SUCCESS;
|
|
11
|
+
|
|
12
|
+
if (!SendInline && QueueOper) {
|
|
13
|
+
diff --git c/src/core/connection.c i/src/core/connection.c
|
|
14
|
+
index d2194a749..4bf4fb3d6 100644
|
|
15
|
+
--- c/src/core/connection.c
|
|
16
|
+
+++ i/src/core/connection.c
|
|
17
|
+
@@ -2203,7 +2203,9 @@ QuicConnRecvResumptionTicket(
|
|
18
|
+
//
|
|
19
|
+
// Validate resumed TP are <= current settings
|
|
20
|
+
//
|
|
21
|
+
- if (ResumedTP.ActiveConnectionIdLimit > QUIC_ACTIVE_CONNECTION_ID_LIMIT ||
|
|
22
|
+
+ if (((ResumedTP.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED) &&
|
|
23
|
+
+ !Connection->Settings.ReliableResetEnabled) ||
|
|
24
|
+
+ ResumedTP.ActiveConnectionIdLimit > QUIC_ACTIVE_CONNECTION_ID_LIMIT ||
|
|
25
|
+
ResumedTP.InitialMaxData > Connection->Send.MaxData ||
|
|
26
|
+
ResumedTP.InitialMaxStreamDataBidiLocal > Connection->Settings.StreamRecvWindowBidiLocalDefault ||
|
|
27
|
+
ResumedTP.InitialMaxStreamDataBidiRemote > Connection->Settings.StreamRecvWindowBidiRemoteDefault ||
|
|
28
|
+
@@ -2288,6 +2290,9 @@ QuicConnRecvResumptionTicket(
|
|
29
|
+
|
|
30
|
+
Error:
|
|
31
|
+
|
|
32
|
+
+ if (!ResumptionAccepted) {
|
|
33
|
+
+ Connection->Crypto.TicketValidationPending = FALSE;
|
|
34
|
+
+ }
|
|
35
|
+
QuicCryptoTlsCleanupTransportParameters(&ResumedTP);
|
|
36
|
+
return ResumptionAccepted;
|
|
37
|
+
}
|
|
38
|
+
@@ -3001,6 +3006,23 @@ QuicConnProcessPeerTransportParameters(
|
|
39
|
+
Connection->SourceCidLimit = QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT_DEFAULT;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
+ // Restore the cached capability before 0-RTT sends, then replace it with
|
|
43
|
+
+ // the peer's actual capability when the new handshake supplies parameters.
|
|
44
|
+
+ if (Connection->Settings.ReliableResetEnabled) {
|
|
45
|
+
+ Connection->State.ReliableResetStreamNegotiated =
|
|
46
|
+
+ !!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);
|
|
47
|
+
+
|
|
48
|
+
+ QUIC_CONNECTION_EVENT Event;
|
|
49
|
+
+ Event.Type = QUIC_CONNECTION_EVENT_RELIABLE_RESET_NEGOTIATED;
|
|
50
|
+
+ Event.RELIABLE_RESET_NEGOTIATED.IsNegotiated = Connection->State.ReliableResetStreamNegotiated;
|
|
51
|
+
+ QuicTraceLogConnVerbose(
|
|
52
|
+
+ IndicateReliableResetNegotiated,
|
|
53
|
+
+ Connection,
|
|
54
|
+
+ "Indicating QUIC_CONNECTION_EVENT_RELIABLE_RESET_NEGOTIATED [IsNegotiated=%hhu]",
|
|
55
|
+
+ Event.RELIABLE_RESET_NEGOTIATED.IsNegotiated);
|
|
56
|
+
+ QuicConnIndicateEvent(Connection, &Event);
|
|
57
|
+
+ }
|
|
58
|
+
+
|
|
59
|
+
if (!FromResumptionTicket) {
|
|
60
|
+
if (Connection->Settings.VersionNegotiationExtEnabled &&
|
|
61
|
+
Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_VERSION_NEGOTIATION) {
|
|
62
|
+
@@ -3065,25 +3087,6 @@ QuicConnProcessPeerTransportParameters(
|
|
63
|
+
Connection->Stats.GreaseBitNegotiated = TRUE;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
- if (Connection->Settings.ReliableResetEnabled) {
|
|
67
|
+
- Connection->State.ReliableResetStreamNegotiated =
|
|
68
|
+
- !!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);
|
|
69
|
+
-
|
|
70
|
+
- //
|
|
71
|
+
- // Send event to app to indicate result of negotiation if app cares.
|
|
72
|
+
- //
|
|
73
|
+
- QUIC_CONNECTION_EVENT Event;
|
|
74
|
+
- Event.Type = QUIC_CONNECTION_EVENT_RELIABLE_RESET_NEGOTIATED;
|
|
75
|
+
- Event.RELIABLE_RESET_NEGOTIATED.IsNegotiated = Connection->State.ReliableResetStreamNegotiated;
|
|
76
|
+
-
|
|
77
|
+
- QuicTraceLogConnVerbose(
|
|
78
|
+
- IndicateReliableResetNegotiated,
|
|
79
|
+
- Connection,
|
|
80
|
+
- "Indicating QUIC_CONNECTION_EVENT_RELIABLE_RESET_NEGOTIATED [IsNegotiated=%hhu]",
|
|
81
|
+
- Event.RELIABLE_RESET_NEGOTIATED.IsNegotiated);
|
|
82
|
+
- QuicConnIndicateEvent(Connection, &Event);
|
|
83
|
+
- }
|
|
84
|
+
-
|
|
85
|
+
if (Connection->Settings.OneWayDelayEnabled) {
|
|
86
|
+
Connection->State.TimestampSendNegotiated = // Peer wants to recv, so we can send
|
|
87
|
+
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_TIMESTAMP_RECV_ENABLED);
|
|
88
|
+
@@ -7732,7 +7735,8 @@ QuicConnApplyNewSettings(
|
|
89
|
+
Connection->Stats.GreaseBitNegotiated = TRUE;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- if (QuicConnIsServer(Connection) && Connection->Settings.ReliableResetEnabled) {
|
|
93
|
+
+ if (Connection->Settings.ReliableResetEnabled &&
|
|
94
|
+
+ (QuicConnIsServer(Connection) || Connection->State.PeerTransportParameterValid)) {
|
|
95
|
+
Connection->State.ReliableResetStreamNegotiated =
|
|
96
|
+
!!(Connection->PeerTransportParams.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);
|
|
97
|
+
|
|
98
|
+
diff --git c/src/core/crypto.c i/src/core/crypto.c
|
|
99
|
+
index 91235bbc9..2dab61879 100644
|
|
100
|
+
--- c/src/core/crypto.c
|
|
101
|
+
+++ i/src/core/crypto.c
|
|
102
|
+
@@ -2578,6 +2578,7 @@ QuicCryptoEncodeServerTicket(
|
|
103
|
+
//
|
|
104
|
+
QUIC_TRANSPORT_PARAMETERS HSTPCopy = *HandshakeTP;
|
|
105
|
+
HSTPCopy.Flags &= (
|
|
106
|
+
+ QUIC_TP_FLAG_RELIABLE_RESET_ENABLED |
|
|
107
|
+
QUIC_TP_FLAG_ACTIVE_CONNECTION_ID_LIMIT |
|
|
108
|
+
QUIC_TP_FLAG_INITIAL_MAX_DATA |
|
|
109
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_LOCAL |
|
|
110
|
+
diff --git c/src/core/crypto_tls.c i/src/core/crypto_tls.c
|
|
111
|
+
index 4fa6b8cc7..a905e624f 100644
|
|
112
|
+
--- c/src/core/crypto_tls.c
|
|
113
|
+
+++ i/src/core/crypto_tls.c
|
|
114
|
+
@@ -66,7 +66,7 @@ typedef enum eSniNameType {
|
|
115
|
+
#define QUIC_TP_ID_MIN_ACK_DELAY 0xFF04DE1BULL // varint
|
|
116
|
+
#define QUIC_TP_ID_CIBIR_ENCODING 0x1000 // {varint, varint}
|
|
117
|
+
#define QUIC_TP_ID_GREASE_QUIC_BIT 0x2AB2 // N/A
|
|
118
|
+
-#define QUIC_TP_ID_RELIABLE_RESET_ENABLED 0x17f7586d2cb570 // varint
|
|
119
|
+
+#define QUIC_TP_ID_RELIABLE_RESET_ENABLED 0x1d // empty reset_stream_at
|
|
120
|
+
#define QUIC_TP_ID_ENABLE_TIMESTAMP 0x7158 // varint
|
|
121
|
+
|
|
122
|
+
BOOLEAN
|
|
123
|
+
diff --git c/src/core/frame.c i/src/core/frame.c
|
|
124
|
+
index c5afcc628..fd2465fb0 100644
|
|
125
|
+
--- c/src/core/frame.c
|
|
126
|
+
+++ i/src/core/frame.c
|
|
127
|
+
@@ -457,7 +457,8 @@ QuicReliableResetFrameDecode(
|
|
128
|
+
if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID) ||
|
|
129
|
+
!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ErrorCode) ||
|
|
130
|
+
!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FinalSize) ||
|
|
131
|
+
- !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ReliableSize)) {
|
|
132
|
+
+ !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ReliableSize) ||
|
|
133
|
+
+ Frame->ReliableSize > Frame->FinalSize) {
|
|
134
|
+
return FALSE;
|
|
135
|
+
}
|
|
136
|
+
return TRUE;
|
|
137
|
+
diff --git c/src/core/frame.h i/src/core/frame.h
|
|
138
|
+
index 563332740..7719f1301 100644
|
|
139
|
+
--- c/src/core/frame.h
|
|
140
|
+
+++ i/src/core/frame.h
|
|
141
|
+
@@ -149,9 +149,9 @@ typedef enum QUIC_FRAME_TYPE {
|
|
142
|
+
QUIC_FRAME_CONNECTION_CLOSE = 0x1cULL, // to 0x1d
|
|
143
|
+
QUIC_FRAME_CONNECTION_CLOSE_1 = 0x1dULL,
|
|
144
|
+
QUIC_FRAME_HANDSHAKE_DONE = 0x1eULL,
|
|
145
|
+
- /* 0x1f to 0x20 are unused currently */
|
|
146
|
+
- QUIC_FRAME_RELIABLE_RESET_STREAM = 0x21ULL, // intentionally ignore type 0x20 of QUIC_RESET_STREAM cause it's likely to be removed from RFC.
|
|
147
|
+
- /* 0x22 to 0x2f are unused currently */
|
|
148
|
+
+ /* 0x20 to 0x23 are unused currently */
|
|
149
|
+
+ QUIC_FRAME_RELIABLE_RESET_STREAM = 0x24ULL, // RESET_STREAM_AT
|
|
150
|
+
+ /* 0x25 to 0x2f are unused currently */
|
|
151
|
+
QUIC_FRAME_DATAGRAM = 0x30ULL, // to 0x31
|
|
152
|
+
QUIC_FRAME_DATAGRAM_1 = 0x31ULL,
|
|
153
|
+
/* 0x32 to 0xad are unused currently */
|
|
154
|
+
diff --git c/src/core/stream.c i/src/core/stream.c
|
|
155
|
+
index ec426b406..f34388cb6 100644
|
|
156
|
+
--- c/src/core/stream.c
|
|
157
|
+
+++ i/src/core/stream.c
|
|
158
|
+
@@ -704,8 +704,15 @@ QuicStreamParamSet(
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
+ // StreamSend may still be queued when the application sets the boundary.
|
|
163
|
+
+ // ApiSubmittedOffset counts everything passed to StreamSend and is
|
|
164
|
+
+ // maintained under the same lock, so there is no window in which a
|
|
165
|
+
+ // flush in progress makes a submitted send invisible.
|
|
166
|
+
+ CxPlatDispatchLockAcquire(&Stream->ApiSendRequestLock);
|
|
167
|
+
+ uint64_t SubmittedOffset = Stream->ApiSubmittedOffset;
|
|
168
|
+
+ CxPlatDispatchLockRelease(&Stream->ApiSendRequestLock);
|
|
169
|
+
if (!Stream->Connection->State.ReliableResetStreamNegotiated ||
|
|
170
|
+
- *(uint64_t*)Buffer > Stream->QueuedSendOffset) {
|
|
171
|
+
+ *(uint64_t*)Buffer > SubmittedOffset) {
|
|
172
|
+
Status = QUIC_STATUS_INVALID_STATE;
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
@@ -962,7 +969,7 @@ QuicStreamParamGet(
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
- *(uint64_t*)Buffer = Stream->RecvMaxLength;
|
|
180
|
+
+ *(uint64_t*)Buffer = Stream->ReliableOffsetRecv;
|
|
181
|
+
Status = QUIC_STATUS_SUCCESS;
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
diff --git c/src/core/stream.h i/src/core/stream.h
|
|
185
|
+
index 5a985692e..c4509ae6b 100644
|
|
186
|
+
--- c/src/core/stream.h
|
|
187
|
+
+++ i/src/core/stream.h
|
|
188
|
+
@@ -125,7 +125,7 @@ typedef union QUIC_STREAM_FLAGS {
|
|
189
|
+
BOOLEAN LocalCloseReset : 1; // Locally closed (locally aborted).
|
|
190
|
+
BOOLEAN LocalCloseResetReliable : 1; // Indicates that we should shutdown the send path once we sent/ACK'd ReliableOffsetSend bytes.
|
|
191
|
+
BOOLEAN LocalCloseResetReliableAcked : 1; // Indicates the peer has acknowledged we will stop sending once we sent/ACK'd ReliableOffsetSend bytes.
|
|
192
|
+
- BOOLEAN RemoteCloseResetReliable : 1; // Indicates that the peer initiated a reliable reset. Keep Recv path available for RecvMaxLength bytes.
|
|
193
|
+
+ BOOLEAN RemoteCloseResetReliable : 1; // Indicates that the peer initiated a reliable reset. Keep Recv path available for ReliableOffsetRecv bytes.
|
|
194
|
+
BOOLEAN ReceivedStopSending : 1; // Peer sent STOP_SENDING frame.
|
|
195
|
+
BOOLEAN LocalCloseAcked : 1; // Any close acknowledged.
|
|
196
|
+
BOOLEAN FinAcked : 1; // Our FIN was acknowledged.
|
|
197
|
+
@@ -310,6 +310,16 @@ typedef struct QUIC_STREAM {
|
|
198
|
+
CXPLAT_DISPATCH_LOCK ApiSendRequestLock;
|
|
199
|
+
QUIC_SEND_REQUEST* ApiSendRequests;
|
|
200
|
+
|
|
201
|
+
+ //
|
|
202
|
+
+ // Total bytes the application has passed to StreamSend, whether or not
|
|
203
|
+
+ // the worker has flushed them yet. Updated under ApiSendRequestLock.
|
|
204
|
+
+ // QUIC_PARAM_STREAM_RELIABLE_OFFSET is bounded by this rather than by
|
|
205
|
+
+ // QueuedSendOffset plus the ApiSendRequests list: the flush clears that
|
|
206
|
+
+ // list before it advances QueuedSendOffset, so a concurrent reader of
|
|
207
|
+
+ // the pair can see neither and reject a valid offset.
|
|
208
|
+
+ //
|
|
209
|
+
+ uint64_t ApiSubmittedOffset;
|
|
210
|
+
+
|
|
211
|
+
//
|
|
212
|
+
// Queued send requests.
|
|
213
|
+
//
|
|
214
|
+
@@ -435,6 +445,10 @@ typedef struct QUIC_STREAM {
|
|
215
|
+
//
|
|
216
|
+
uint64_t RecvMaxLength;
|
|
217
|
+
|
|
218
|
+
+ // Reliable delivery boundary; RecvMaxLength remains the immutable final size.
|
|
219
|
+
+ uint64_t ReliableOffsetRecv;
|
|
220
|
+
+ QUIC_VAR_INT RecvResetErrorCode;
|
|
221
|
+
+
|
|
222
|
+
//
|
|
223
|
+
// The number of bytes that are currently outstanding up to the app.
|
|
224
|
+
//
|
|
225
|
+
diff --git c/src/core/stream_recv.c i/src/core/stream_recv.c
|
|
226
|
+
index 8d9640a9f..cd477447f 100644
|
|
227
|
+
--- c/src/core/stream_recv.c
|
|
228
|
+
+++ i/src/core/stream_recv.c
|
|
229
|
+
@@ -82,7 +82,8 @@ QuicStreamRecvShutdown(
|
|
230
|
+
// the receives to that point. Just treat the shutdown as if it was
|
|
231
|
+
// already acknowledged by a reset frame.
|
|
232
|
+
//
|
|
233
|
+
- QuicStreamProcessResetFrame(Stream, Stream->RecvMaxLength, 0);
|
|
234
|
+
+ QuicStreamProcessResetFrame(Stream, Stream->RecvMaxLength,
|
|
235
|
+
+ Stream->Flags.RemoteCloseResetReliable ? Stream->RecvResetErrorCode : 0);
|
|
236
|
+
Silent = TRUE; // To indicate we try to shutdown complete.
|
|
237
|
+
goto Exit;
|
|
238
|
+
}
|
|
239
|
+
@@ -189,6 +190,7 @@ _IRQL_requires_max_(PASSIVE_LEVEL)
|
|
240
|
+
void
|
|
241
|
+
QuicStreamProcessReliableResetFrame(
|
|
242
|
+
_In_ QUIC_STREAM* Stream,
|
|
243
|
+
+ _In_ uint64_t FinalSize,
|
|
244
|
+
_In_ QUIC_VAR_INT ErrorCode,
|
|
245
|
+
_In_ QUIC_VAR_INT ReliableOffset
|
|
246
|
+
)
|
|
247
|
+
@@ -206,35 +208,43 @@ QuicStreamProcessReliableResetFrame(
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
- if (Stream->RecvMaxLength == 0 || ReliableOffset < Stream->RecvMaxLength) {
|
|
252
|
+
- //
|
|
253
|
+
- // As outlined in the spec, if we receive multiple CLOSE_STREAM frames, we only accept strictly
|
|
254
|
+
- // decreasing offsets.
|
|
255
|
+
- //
|
|
256
|
+
- Stream->RecvMaxLength = ReliableOffset;
|
|
257
|
+
+ if (ReliableOffset > FinalSize) {
|
|
258
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
|
|
259
|
+
+ return;
|
|
260
|
+
+ }
|
|
261
|
+
+ if ((Stream->RecvMaxLength != UINT64_MAX && Stream->RecvMaxLength != FinalSize) ||
|
|
262
|
+
+ QuicRecvBufferGetTotalLength(&Stream->RecvBuffer) > FinalSize) {
|
|
263
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_FINAL_SIZE_ERROR);
|
|
264
|
+
+ return;
|
|
265
|
+
+ }
|
|
266
|
+
+ if ((Stream->Flags.RemoteCloseResetReliable || Stream->Flags.RemoteCloseReset) &&
|
|
267
|
+
+ Stream->RecvResetErrorCode != ErrorCode) {
|
|
268
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_STREAM_STATE_ERROR);
|
|
269
|
+
+ return;
|
|
270
|
+
+ }
|
|
271
|
+
+ if (Stream->Flags.RemoteCloseReset || Stream->Flags.RemoteCloseFin) {
|
|
272
|
+
+ return;
|
|
273
|
+
+ }
|
|
274
|
+
+ if (!Stream->Flags.RemoteCloseResetReliable) {
|
|
275
|
+
+ uint64_t Increase = FinalSize - QuicRecvBufferGetTotalLength(&Stream->RecvBuffer);
|
|
276
|
+
+ if (FinalSize > Stream->MaxAllowedRecvOffset ||
|
|
277
|
+
+ Increase > Stream->Connection->Send.MaxData - Stream->Connection->Send.OrderedStreamBytesReceived) {
|
|
278
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_FLOW_CONTROL_ERROR);
|
|
279
|
+
+ return;
|
|
280
|
+
+ }
|
|
281
|
+
+ // Charge the final size once, including bytes still missing from the buffer.
|
|
282
|
+
+ Stream->Connection->Send.OrderedStreamBytesReceived += Increase;
|
|
283
|
+
+ Stream->RecvMaxLength = FinalSize;
|
|
284
|
+
+ Stream->ReliableOffsetRecv = ReliableOffset;
|
|
285
|
+
+ Stream->RecvResetErrorCode = ErrorCode;
|
|
286
|
+
Stream->Flags.RemoteCloseResetReliable = TRUE;
|
|
287
|
+
-
|
|
288
|
+
- QuicTraceLogStreamInfo(
|
|
289
|
+
- ReliableRecvOffsetSet,
|
|
290
|
+
- Stream,
|
|
291
|
+
- "Reliable recv offset set to %llu",
|
|
292
|
+
- ReliableOffset);
|
|
293
|
+
+ } else if (ReliableOffset < Stream->ReliableOffsetRecv) {
|
|
294
|
+
+ Stream->ReliableOffsetRecv = ReliableOffset;
|
|
295
|
+
+ }
|
|
296
|
+
+ if (Stream->RecvBuffer.BaseOffset >= Stream->ReliableOffsetRecv || Stream->Flags.SentStopSending) {
|
|
297
|
+
+ QuicStreamProcessResetFrame(Stream, FinalSize, ErrorCode);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
- if (Stream->RecvBuffer.BaseOffset >= Stream->RecvMaxLength) {
|
|
301
|
+
- QuicTraceEvent(
|
|
302
|
+
- StreamRecvState,
|
|
303
|
+
- "[strm][%p] Recv State: %hhu",
|
|
304
|
+
- Stream,
|
|
305
|
+
- QuicStreamRecvGetState(Stream));
|
|
306
|
+
- QuicStreamIndicatePeerSendAbortedEvent(Stream, ErrorCode);
|
|
307
|
+
- QuicStreamRecvShutdown(Stream, TRUE, ErrorCode);
|
|
308
|
+
- } else {
|
|
309
|
+
- //
|
|
310
|
+
- // We still have data to deliver to the app, just cache the error code for later.
|
|
311
|
+
- //
|
|
312
|
+
- Stream->RecvShutdownErrorCode = ErrorCode;
|
|
313
|
+
- }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
//
|
|
317
|
+
@@ -248,10 +258,22 @@ QuicStreamProcessResetFrame(
|
|
318
|
+
_In_ QUIC_VAR_INT ErrorCode
|
|
319
|
+
)
|
|
320
|
+
{
|
|
321
|
+
+ if (Stream->RecvMaxLength != UINT64_MAX && FinalSize != Stream->RecvMaxLength) {
|
|
322
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_FINAL_SIZE_ERROR);
|
|
323
|
+
+ return;
|
|
324
|
+
+ }
|
|
325
|
+
+ if (Stream->Flags.RemoteCloseResetReliable || Stream->Flags.RemoteCloseReset) {
|
|
326
|
+
+ if (ErrorCode != Stream->RecvResetErrorCode) {
|
|
327
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_STREAM_STATE_ERROR);
|
|
328
|
+
+ return;
|
|
329
|
+
+ }
|
|
330
|
+
+ }
|
|
331
|
+
//
|
|
332
|
+
// Make sure the stream is remotely closed if not already.
|
|
333
|
+
//
|
|
334
|
+
Stream->Flags.RemoteCloseReset = TRUE;
|
|
335
|
+
+ Stream->RecvMaxLength = FinalSize;
|
|
336
|
+
+ Stream->RecvResetErrorCode = ErrorCode;
|
|
337
|
+
|
|
338
|
+
if (!Stream->Flags.RemoteCloseAcked) {
|
|
339
|
+
Stream->Flags.RemoteCloseAcked = TRUE;
|
|
340
|
+
@@ -272,7 +294,7 @@ QuicStreamProcessResetFrame(
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
- if (TotalRecvLength < FinalSize) {
|
|
345
|
+
+ if (TotalRecvLength < FinalSize && !Stream->Flags.RemoteCloseResetReliable) {
|
|
346
|
+
//
|
|
347
|
+
// The final offset is indicating that more data was sent than we
|
|
348
|
+
// have actually received. Make sure to update our flow control
|
|
349
|
+
@@ -442,19 +464,8 @@ QuicStreamProcessStreamFrame(
|
|
350
|
+
goto Error;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
- if (Stream->Flags.RemoteCloseResetReliable) {
|
|
354
|
+
- if (Stream->RecvBuffer.BaseOffset >= Stream->RecvMaxLength) {
|
|
355
|
+
- //
|
|
356
|
+
- // We've aborted reliably, but the stream goes past reliable offset, we can just
|
|
357
|
+
- // ignore it.
|
|
358
|
+
- //
|
|
359
|
+
- Status = QUIC_STATUS_SUCCESS;
|
|
360
|
+
- goto Error;
|
|
361
|
+
- }
|
|
362
|
+
- } else if (EndOffset > Stream->RecvMaxLength) {
|
|
363
|
+
- //
|
|
364
|
+
- // Frame goes past the FIN, and the stream is not reset reliably.
|
|
365
|
+
- //
|
|
366
|
+
+ if (EndOffset > Stream->RecvMaxLength) {
|
|
367
|
+
+ QuicConnTransportError(Stream->Connection, QUIC_ERROR_FINAL_SIZE_ERROR);
|
|
368
|
+
Status = QUIC_STATUS_INVALID_PARAMETER;
|
|
369
|
+
goto Error;
|
|
370
|
+
}
|
|
371
|
+
@@ -481,9 +492,9 @@ QuicStreamProcessStreamFrame(
|
|
372
|
+
// On return from QuicRecvBufferWrite, this represents the
|
|
373
|
+
// actual number of bytes written.
|
|
374
|
+
//
|
|
375
|
+
- const uint64_t FlowControlQuota =
|
|
376
|
+
- Stream->Connection->Send.MaxData -
|
|
377
|
+
- Stream->Connection->Send.OrderedStreamBytesReceived;
|
|
378
|
+
+ const uint64_t FlowControlQuota = Stream->Flags.RemoteCloseResetReliable ?
|
|
379
|
+
+ Stream->RecvMaxLength - QuicRecvBufferGetTotalLength(&Stream->RecvBuffer) :
|
|
380
|
+
+ Stream->Connection->Send.MaxData - Stream->Connection->Send.OrderedStreamBytesReceived;
|
|
381
|
+
uint64_t QuotaConsumed = 0;
|
|
382
|
+
uint64_t BufferSizeNeeded = 0;
|
|
383
|
+
|
|
384
|
+
@@ -541,7 +552,9 @@ QuicStreamProcessStreamFrame(
|
|
385
|
+
//
|
|
386
|
+
// Keep track of the total ordered bytes received.
|
|
387
|
+
//
|
|
388
|
+
- Stream->Connection->Send.OrderedStreamBytesReceived += QuotaConsumed;
|
|
389
|
+
+ if (!Stream->Flags.RemoteCloseResetReliable) {
|
|
390
|
+
+ Stream->Connection->Send.OrderedStreamBytesReceived += QuotaConsumed;
|
|
391
|
+
+ }
|
|
392
|
+
CXPLAT_DBG_ASSERT(Stream->Connection->Send.OrderedStreamBytesReceived <= Stream->Connection->Send.MaxData);
|
|
393
|
+
CXPLAT_DBG_ASSERT(Stream->Connection->Send.OrderedStreamBytesReceived >= QuotaConsumed);
|
|
394
|
+
|
|
395
|
+
@@ -735,6 +748,7 @@ QuicStreamRecv(
|
|
396
|
+
|
|
397
|
+
QuicStreamProcessReliableResetFrame(
|
|
398
|
+
Stream,
|
|
399
|
+
+ Frame.FinalSize,
|
|
400
|
+
Frame.ErrorCode,
|
|
401
|
+
Frame.ReliableSize);
|
|
402
|
+
|
|
403
|
+
@@ -981,7 +995,8 @@ QuicStreamRecvFlush(
|
|
404
|
+
//
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
- if (Event.RECEIVE.AbsoluteOffset + Event.RECEIVE.TotalBufferLength == Stream->RecvMaxLength) {
|
|
408
|
+
+ if (!Stream->Flags.RemoteCloseResetReliable &&
|
|
409
|
+
+ Event.RECEIVE.AbsoluteOffset + Event.RECEIVE.TotalBufferLength == Stream->RecvMaxLength) {
|
|
410
|
+
//
|
|
411
|
+
// This data goes all the way to the FIN.
|
|
412
|
+
//
|
|
413
|
+
@@ -1158,6 +1173,11 @@ QuicStreamReceiveComplete(
|
|
414
|
+
return FALSE;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
+ if (Stream->Flags.RemoteCloseResetReliable && Stream->RecvBuffer.BaseOffset >= Stream->ReliableOffsetRecv) {
|
|
418
|
+
+ QuicStreamProcessResetFrame(Stream, Stream->RecvMaxLength, Stream->RecvResetErrorCode);
|
|
419
|
+
+ return FALSE;
|
|
420
|
+
+ }
|
|
421
|
+
+
|
|
422
|
+
if (Stream->Flags.ReceiveDataPending) {
|
|
423
|
+
//
|
|
424
|
+
// There is still more data for the app to process and it still has
|
|
425
|
+
@@ -1204,18 +1224,6 @@ QuicStreamReceiveComplete(
|
|
426
|
+
&Stream->Connection->Send,
|
|
427
|
+
Stream,
|
|
428
|
+
QUIC_STREAM_SEND_FLAG_MAX_DATA | QUIC_STREAM_SEND_FLAG_RECV_ABORT);
|
|
429
|
+
- } else if (Stream->Flags.RemoteCloseResetReliable && Stream->RecvBuffer.BaseOffset >= Stream->RecvMaxLength) {
|
|
430
|
+
- //
|
|
431
|
+
- // ReliableReset was initiated by the peer, and we sent enough data to the app, we can alert the app
|
|
432
|
+
- // we're done and shutdown the RECV direction of this stream.
|
|
433
|
+
- //
|
|
434
|
+
- QuicTraceEvent(
|
|
435
|
+
- StreamRecvState,
|
|
436
|
+
- "[strm][%p] Recv State: %hhu",
|
|
437
|
+
- Stream,
|
|
438
|
+
- QuicStreamRecvGetState(Stream));
|
|
439
|
+
- QuicStreamIndicatePeerSendAbortedEvent(Stream, Stream->RecvShutdownErrorCode);
|
|
440
|
+
- QuicStreamRecvShutdown(Stream, TRUE, Stream->RecvShutdownErrorCode);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return FALSE;
|
|
444
|
+
diff --git c/src/core/stream_send.c i/src/core/stream_send.c
|
|
445
|
+
index 0ac7dd101..ca70133df 100644
|
|
446
|
+
--- c/src/core/stream_send.c
|
|
447
|
+
+++ i/src/core/stream_send.c
|
|
448
|
+
@@ -119,7 +119,8 @@ QuicStreamSendShutdown(
|
|
449
|
+
_In_ QUIC_VAR_INT ErrorCode // Only for !Graceful
|
|
450
|
+
)
|
|
451
|
+
{
|
|
452
|
+
- if (Stream->Flags.LocalCloseAcked) {
|
|
453
|
+
+ if (Stream->Flags.LocalCloseAcked ||
|
|
454
|
+
+ (Stream->Flags.LocalCloseResetReliable && !Silent)) {
|
|
455
|
+
//
|
|
456
|
+
// We have already closed (graceful or abortive) and it has been
|
|
457
|
+
// acknowledged by the peer. Nothing else to be done.
|
|
458
|
+
@@ -275,7 +276,7 @@ QuicStreamSendShutdown(
|
|
459
|
+
QuicSendSetStreamSendFlag(
|
|
460
|
+
&Stream->Connection->Send,
|
|
461
|
+
Stream,
|
|
462
|
+
- QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT,
|
|
463
|
+
+ QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT | QUIC_STREAM_SEND_FLAG_DATA,
|
|
464
|
+
FALSE);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
@@ -361,6 +362,10 @@ QuicStreamSendCanWriteDataFrames(
|
|
468
|
+
return TRUE;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
+ if (Stream->Flags.LocalCloseResetReliable &&
|
|
472
|
+
+ Stream->NextSendOffset >= CXPLAT_MAX(Stream->MaxSentLength, Stream->ReliableOffsetSend)) {
|
|
473
|
+
+ return FALSE;
|
|
474
|
+
+ }
|
|
475
|
+
if (Stream->NextSendOffset == Stream->QueuedSendOffset) {
|
|
476
|
+
//
|
|
477
|
+
// No unsent data. Can send only if a FIN is needed.
|
|
478
|
+
@@ -392,7 +397,11 @@ QuicStreamCanSendNow(
|
|
479
|
+
return FALSE;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
- if (HasStreamControlFrames(Stream->SendFlags) ||
|
|
483
|
+
+ uint32_t SendFlags = Stream->SendFlags;
|
|
484
|
+
+ if (Stream->MaxSentLength < Stream->ReliableOffsetSend) {
|
|
485
|
+
+ SendFlags &= ~QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT;
|
|
486
|
+
+ }
|
|
487
|
+
+ if (HasStreamControlFrames(SendFlags) ||
|
|
488
|
+
(Stream->SendFlags & QUIC_STREAM_SEND_FLAG_OPEN)) {
|
|
489
|
+
//
|
|
490
|
+
// We can always send control frames and/or open new streams.
|
|
491
|
+
@@ -963,6 +972,12 @@ QuicStreamWriteStreamFrames(
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
+ if (Stream->Flags.LocalCloseResetReliable) {
|
|
496
|
+
+ const uint64_t FinalSize = CXPLAT_MAX(Stream->MaxSentLength, Stream->ReliableOffsetSend);
|
|
497
|
+
+ if (Left >= FinalSize) { break; }
|
|
498
|
+
+ Right = CXPLAT_MIN(Right, FinalSize);
|
|
499
|
+
+ }
|
|
500
|
+
+
|
|
501
|
+
//
|
|
502
|
+
// Stream flow control
|
|
503
|
+
//
|
|
504
|
+
@@ -1150,7 +1165,8 @@ QuicStreamSendWrite(
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
- if (Stream->SendFlags & QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT) {
|
|
509
|
+
+ if ((Stream->SendFlags & QUIC_STREAM_SEND_FLAG_RELIABLE_ABORT) &&
|
|
510
|
+
+ Stream->MaxSentLength >= Stream->ReliableOffsetSend) {
|
|
511
|
+
QUIC_RELIABLE_RESET_STREAM_EX Frame = { Stream->ID, Stream->SendShutdownErrorCode, Stream->MaxSentLength, Stream->ReliableOffsetSend };
|
|
512
|
+
|
|
513
|
+
if (QuicReliableResetFrameEncode(
|
|
514
|
+
diff --git c/src/core/unittest/CMakeLists.txt i/src/core/unittest/CMakeLists.txt
|
|
515
|
+
index ae5411ae9..04f79f63c 100644
|
|
516
|
+
--- c/src/core/unittest/CMakeLists.txt
|
|
517
|
+
+++ i/src/core/unittest/CMakeLists.txt
|
|
518
|
+
@@ -9,6 +9,7 @@ set(SOURCES
|
|
519
|
+
PacketNumberTest.cpp
|
|
520
|
+
PartitionTest.cpp
|
|
521
|
+
RangeTest.cpp
|
|
522
|
+
+ ReliableResetTest.cpp
|
|
523
|
+
RecvBufferTest.cpp
|
|
524
|
+
SettingsTest.cpp
|
|
525
|
+
SlidingWindowExtremumTest.cpp
|
|
526
|
+
diff --git c/src/core/unittest/FrameTest.cpp i/src/core/unittest/FrameTest.cpp
|
|
527
|
+
index f3cde7e73..dcd7edb02 100644
|
|
528
|
+
--- c/src/core/unittest/FrameTest.cpp
|
|
529
|
+
+++ i/src/core/unittest/FrameTest.cpp
|
|
530
|
+
@@ -243,6 +243,26 @@ TEST(FrameTest, ReliableResetStreamFrameEncodeDecode)
|
|
531
|
+
ASSERT_EQ(Frame.ReliableSize, DecodedFrame.ReliableSize);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
+// draft-ietf-quic-reliable-stream-reset-09, sections 4 and 8.2.
|
|
535
|
+
+TEST(FrameTest, ResetStreamAtUsesRegisteredWireType)
|
|
536
|
+
+{
|
|
537
|
+
+ QUIC_RELIABLE_RESET_STREAM_EX Frame = {0, 7, 10, 3};
|
|
538
|
+
+ uint8_t Buffer[5];
|
|
539
|
+
+ uint16_t Offset = 0;
|
|
540
|
+
+ ASSERT_TRUE(QuicReliableResetFrameEncode(&Frame, &Offset, sizeof(Buffer), Buffer));
|
|
541
|
+
+ const uint8_t Expected[] = {0x24, 0, 7, 10, 3};
|
|
542
|
+
+ ASSERT_EQ(sizeof(Expected), Offset);
|
|
543
|
+
+ ASSERT_EQ(0, memcmp(Expected, Buffer, sizeof(Expected)));
|
|
544
|
+
+}
|
|
545
|
+
+
|
|
546
|
+
+TEST(FrameTest, ResetStreamAtRejectsReliableSizeBeyondFinalSize)
|
|
547
|
+
+{
|
|
548
|
+
+ const uint8_t Buffer[] = {0x24, 0, 7, 3, 4};
|
|
549
|
+
+ uint16_t Offset = 1;
|
|
550
|
+
+ QUIC_RELIABLE_RESET_STREAM_EX Frame = {};
|
|
551
|
+
+ ASSERT_FALSE(QuicReliableResetFrameDecode(sizeof(Buffer), Buffer, &Offset, &Frame));
|
|
552
|
+
+}
|
|
553
|
+
+
|
|
554
|
+
//
|
|
555
|
+
// Tests if the reordering threshold has been hit. This function initializes a
|
|
556
|
+
// QUIC_ACK_TRACKER and populates it with the provided packet numbers. It then
|
|
557
|
+
diff --git c/src/core/unittest/ReliableResetTest.cpp i/src/core/unittest/ReliableResetTest.cpp
|
|
558
|
+
new file mode 100644
|
|
559
|
+
index 000000000..8c4e15294
|
|
560
|
+
--- /dev/null
|
|
561
|
+
+++ i/src/core/unittest/ReliableResetTest.cpp
|
|
562
|
+
@@ -0,0 +1,33 @@
|
|
563
|
+
+// Copyright (c) Microsoft Corporation.
|
|
564
|
+
+// Licensed under the MIT License.
|
|
565
|
+
+#include "main.h"
|
|
566
|
+
+
|
|
567
|
+
+extern "C" void QuicStreamProcessReliableResetFrame(
|
|
568
|
+
+ QUIC_STREAM*, uint64_t, QUIC_VAR_INT, QUIC_VAR_INT);
|
|
569
|
+
+
|
|
570
|
+
+TEST(ReliableResetTest, FinalSizeIsChargedOnceWhileWaitingForPrefix)
|
|
571
|
+
+{
|
|
572
|
+
+ QUIC_CONNECTION Connection = {};
|
|
573
|
+
+ Connection.State.ReliableResetStreamNegotiated = TRUE;
|
|
574
|
+
+ Connection.Send.MaxData = 100;
|
|
575
|
+
+ QUIC_STREAM Stream = {};
|
|
576
|
+
+ Stream.Connection = &Connection;
|
|
577
|
+
+ Stream.RecvMaxLength = UINT64_MAX;
|
|
578
|
+
+ Stream.MaxAllowedRecvOffset = 100;
|
|
579
|
+
+
|
|
580
|
+
+ QuicStreamProcessReliableResetFrame(&Stream, 50, 42, 10);
|
|
581
|
+
+ EXPECT_EQ(50u, Connection.Send.OrderedStreamBytesReceived);
|
|
582
|
+
+ EXPECT_EQ(50u, Stream.RecvMaxLength);
|
|
583
|
+
+ EXPECT_EQ(10u, Stream.ReliableOffsetRecv);
|
|
584
|
+
+ EXPECT_EQ(100u, Connection.Send.MaxData); // No credit returned before consumption.
|
|
585
|
+
+
|
|
586
|
+
+ QuicStreamProcessReliableResetFrame(&Stream, 50, 42, 5);
|
|
587
|
+
+ EXPECT_EQ(50u, Connection.Send.OrderedStreamBytesReceived);
|
|
588
|
+
+ EXPECT_EQ(50u, Stream.RecvMaxLength);
|
|
589
|
+
+ EXPECT_EQ(5u, Stream.ReliableOffsetRecv);
|
|
590
|
+
+
|
|
591
|
+
+ // A reordered copy must not increase the reliable boundary or charge again.
|
|
592
|
+
+ QuicStreamProcessReliableResetFrame(&Stream, 50, 42, 10);
|
|
593
|
+
+ EXPECT_EQ(50u, Connection.Send.OrderedStreamBytesReceived);
|
|
594
|
+
+ EXPECT_EQ(5u, Stream.ReliableOffsetRecv);
|
|
595
|
+
+}
|
|
596
|
+
diff --git c/src/core/unittest/TicketTest.cpp i/src/core/unittest/TicketTest.cpp
|
|
597
|
+
index 6a55d9e11..493f6c5c8 100644
|
|
598
|
+
--- c/src/core/unittest/TicketTest.cpp
|
|
599
|
+
+++ i/src/core/unittest/TicketTest.cpp
|
|
600
|
+
@@ -57,7 +57,8 @@ TEST(ResumptionTicketTest, ClientEncDec)
|
|
601
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
|
|
602
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI |
|
|
603
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRMS_BIDI |
|
|
604
|
+
- QUIC_TP_FLAG_INITIAL_MAX_STRMS_UNI;
|
|
605
|
+
+ QUIC_TP_FLAG_INITIAL_MAX_STRMS_UNI |
|
|
606
|
+
+ QUIC_TP_FLAG_RELIABLE_RESET_ENABLED;
|
|
607
|
+
ClientTP.ActiveConnectionIdLimit = QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT_MIN;
|
|
608
|
+
|
|
609
|
+
TEST_QUIC_SUCCEEDED(
|
|
610
|
+
@@ -481,7 +482,8 @@ TEST(ResumptionTicketTest, ServerEncDec)
|
|
611
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_BIDI_REMOTE |
|
|
612
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRM_DATA_UNI |
|
|
613
|
+
QUIC_TP_FLAG_INITIAL_MAX_STRMS_BIDI |
|
|
614
|
+
- QUIC_TP_FLAG_INITIAL_MAX_STRMS_UNI;
|
|
615
|
+
+ QUIC_TP_FLAG_INITIAL_MAX_STRMS_UNI |
|
|
616
|
+
+ QUIC_TP_FLAG_RELIABLE_RESET_ENABLED;
|
|
617
|
+
ServerTP.ActiveConnectionIdLimit = QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT_MIN;
|
|
618
|
+
|
|
619
|
+
TEST_QUIC_SUCCEEDED(
|
|
620
|
+
diff --git c/src/core/unittest/TransportParamTest.cpp i/src/core/unittest/TransportParamTest.cpp
|
|
621
|
+
index d5ad1e7f7..555e9a302 100644
|
|
622
|
+
--- c/src/core/unittest/TransportParamTest.cpp
|
|
623
|
+
+++ i/src/core/unittest/TransportParamTest.cpp
|
|
624
|
+
@@ -262,6 +262,25 @@ TEST(TransportParamTest, GreaseQuicBit)
|
|
625
|
+
EncodeDecodeAndCompare(&OriginalTP, true);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
+TEST(TransportParamTest, ResetStreamAtRecognizesRegisteredParameter)
|
|
629
|
+
+{
|
|
630
|
+
+ const uint8_t Buffer[] = {0x1d, 0};
|
|
631
|
+
+ QUIC_TRANSPORT_PARAMETERS Decoded = {};
|
|
632
|
+
+ TransportParametersScope Scope(&Decoded);
|
|
633
|
+
+ ASSERT_TRUE(QuicCryptoTlsDecodeTransportParameters(
|
|
634
|
+
+ &JunkConnection, false, Buffer, sizeof(Buffer), &Decoded));
|
|
635
|
+
+ ASSERT_NE(0u, Decoded.Flags & QUIC_TP_FLAG_RELIABLE_RESET_ENABLED);
|
|
636
|
+
+}
|
|
637
|
+
+
|
|
638
|
+
+TEST(TransportParamTest, ResetStreamAtRejectsNonemptyParameter)
|
|
639
|
+
+{
|
|
640
|
+
+ const uint8_t Buffer[] = {0x1d, 1, 0};
|
|
641
|
+
+ QUIC_TRANSPORT_PARAMETERS Decoded = {};
|
|
642
|
+
+ TransportParametersScope Scope(&Decoded);
|
|
643
|
+
+ ASSERT_FALSE(QuicCryptoTlsDecodeTransportParameters(
|
|
644
|
+
+ &JunkConnection, false, Buffer, sizeof(Buffer), &Decoded));
|
|
645
|
+
+}
|
|
646
|
+
+
|
|
647
|
+
TEST(TransportParamTest, ReliableResetEnabled)
|
|
648
|
+
{
|
|
649
|
+
QUIC_TRANSPORT_PARAMETERS OriginalTP;
|
|
650
|
+
diff --git c/src/test/lib/DataTest.cpp i/src/test/lib/DataTest.cpp
|
|
651
|
+
index e1c2ca2ac..84a87d9db 100644
|
|
652
|
+
--- c/src/test/lib/DataTest.cpp
|
|
653
|
+
+++ i/src/test/lib/DataTest.cpp
|
|
654
|
+
@@ -4415,6 +4415,9 @@ QuicTestStreamReliableReset(
|
|
655
|
+
MsQuicStream Stream(Connection, QUIC_STREAM_OPEN_FLAG_NONE, CleanUpManual, StreamReliableReset::ClientStreamCallback, &Context);
|
|
656
|
+
TEST_QUIC_SUCCEEDED(Stream.GetInitStatus());
|
|
657
|
+
TEST_QUIC_SUCCEEDED(Stream.Start());
|
|
658
|
+
+ // Each loss pattern must deliver its own reliable prefix and error.
|
|
659
|
+
+ Context.ReceivedBufferSize = 0;
|
|
660
|
+
+ Context.ShutdownErrorCode = 0;
|
|
661
|
+
SendContext send1 = {FALSE, 0};
|
|
662
|
+
TEST_QUIC_SUCCEEDED(Stream.Send(&SendBuffer, 1, QUIC_SEND_FLAG_DELAY_SEND, &send1));
|
|
663
|
+
TEST_QUIC_STATUS(
|
|
664
|
+
diff --git c/src/test/lib/HandshakeTest.cpp i/src/test/lib/HandshakeTest.cpp
|
|
665
|
+
index a787edf70..a16a1fb2b 100644
|
|
666
|
+
--- c/src/test/lib/HandshakeTest.cpp
|
|
667
|
+
+++ i/src/test/lib/HandshakeTest.cpp
|
|
668
|
+
@@ -2815,6 +2815,7 @@ QuicTestReliableResetNegotiation(
|
|
669
|
+
MsQuicSettings ServerSettings;
|
|
670
|
+
MsQuicSettings ClientSettings;
|
|
671
|
+
ServerSettings.SetReliableResetEnabled(ServerSupport);
|
|
672
|
+
+ ServerSettings.SetServerResumptionLevel(QUIC_SERVER_RESUME_AND_ZERORTT);
|
|
673
|
+
ClientSettings.SetReliableResetEnabled(ClientSupport);
|
|
674
|
+
|
|
675
|
+
MsQuicConfiguration ServerConfiguration(Registration, "MsQuicTest", ServerSettings, ServerSelfSignedCredConfig);
|
|
676
|
+
@@ -2856,6 +2857,47 @@ QuicTestReliableResetNegotiation(
|
|
677
|
+
} else {
|
|
678
|
+
TEST_FALSE(ServerContext.CallbackReceived);
|
|
679
|
+
}
|
|
680
|
+
+
|
|
681
|
+
+ TEST_QUIC_SUCCEEDED(Listener.LastConnection->SendResumptionTicket(QUIC_SEND_RESUMPTION_FLAG_NONE));
|
|
682
|
+
+ TEST_TRUE(Connection.ResumptionTicketReceivedEvent.WaitTimeout(TestWaitTimeout));
|
|
683
|
+
+
|
|
684
|
+
+ for (bool SettingsBeforeTicket : {false, true}) {
|
|
685
|
+
+ Context ResumedContext;
|
|
686
|
+
+ MsQuicConnection Resumed(Registration, CleanUpManual, Context::s_ConnectionCallback, &ResumedContext);
|
|
687
|
+
+ if (SettingsBeforeTicket) {
|
|
688
|
+
+ TEST_QUIC_SUCCEEDED(Resumed.SetSettings(ClientSettings));
|
|
689
|
+
+ }
|
|
690
|
+
+ TEST_QUIC_SUCCEEDED(Resumed.SetResumptionTicket(Connection.ResumptionTicket, Connection.ResumptionTicketLength));
|
|
691
|
+
+ if (!SettingsBeforeTicket) {
|
|
692
|
+
+ TEST_QUIC_SUCCEEDED(Resumed.SetSettings(ClientSettings));
|
|
693
|
+
+ }
|
|
694
|
+
+ if (ClientSupport) {
|
|
695
|
+
+ // The cached capability must be available before the new handshake.
|
|
696
|
+
+ TEST_TRUE(ResumedContext.CallbackReceived);
|
|
697
|
+
+ TEST_TRUE(ResumedContext.Negotiated == ServerSupport);
|
|
698
|
+
+ }
|
|
699
|
+
+ TEST_QUIC_SUCCEEDED(Resumed.Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort()));
|
|
700
|
+
+ TEST_TRUE(Resumed.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout));
|
|
701
|
+
+ TEST_TRUE(Resumed.HandshakeComplete);
|
|
702
|
+
+ TEST_TRUE(Resumed.HandshakeResumed);
|
|
703
|
+
+ TEST_TRUE(ResumedContext.Negotiated == (ServerSupport && ClientSupport));
|
|
704
|
+
+ }
|
|
705
|
+
+
|
|
706
|
+
+ if (ServerSupport && ClientSupport) {
|
|
707
|
+
+ MsQuicSettings DisabledSettings;
|
|
708
|
+
+ DisabledSettings.SetReliableResetEnabled(false);
|
|
709
|
+
+ TEST_QUIC_SUCCEEDED(ServerConfiguration.SetSettings(DisabledSettings));
|
|
710
|
+
+
|
|
711
|
+
+ Context DowngradedContext;
|
|
712
|
+
+ MsQuicConnection Downgraded(Registration, CleanUpManual, Context::s_ConnectionCallback, &DowngradedContext);
|
|
713
|
+
+ TEST_QUIC_SUCCEEDED(Downgraded.SetSettings(ClientSettings));
|
|
714
|
+
+ TEST_QUIC_SUCCEEDED(Downgraded.SetResumptionTicket(Connection.ResumptionTicket, Connection.ResumptionTicketLength));
|
|
715
|
+
+ TEST_QUIC_SUCCEEDED(Downgraded.Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort()));
|
|
716
|
+
+ TEST_TRUE(Downgraded.HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout));
|
|
717
|
+
+ TEST_TRUE(Downgraded.HandshakeComplete);
|
|
718
|
+
+ TEST_FALSE(Downgraded.HandshakeResumed);
|
|
719
|
+
+ TEST_FALSE(DowngradedContext.Negotiated);
|
|
720
|
+
+ }
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
void
|