@@ -101,62 +101,91 @@ public WebSocketMessageReader(PipeReader transport, IControlFrameHandler control
101
101
/// </summary>
102
102
/// <param name="cancellationToken">A cancellation token, if any.</param>
103
103
/// <returns>A message read result.</returns>
104
- public async ValueTask < MessageReadResult > ReadAsync ( CancellationToken cancellationToken = default )
104
+ public ValueTask < MessageReadResult > ReadAsync ( CancellationToken cancellationToken = default )
105
105
{
106
106
if ( _awaitingHeader )
107
107
{
108
- var frame = await GetNextMessageFrameAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
109
- ValidateHeader ( frame . Header ) ;
108
+ var readTask = GetNextMessageFrameAsync ( cancellationToken ) ;
109
+ if ( readTask . IsCompletedSuccessfully )
110
+ {
111
+ var frame = readTask . Result ;
112
+ ValidateHeader ( frame . Header ) ;
110
113
111
- _header = frame . Header ;
112
- _payloadReader = frame . Payload ;
114
+ _header = frame . Header ;
115
+ _payloadReader = frame . Payload ;
116
+ }
117
+ else
118
+ {
119
+ return DoReadHeaderRequiredAsync ( readTask , cancellationToken ) ;
120
+ }
113
121
}
114
122
123
+ return ReadPayloadAsync ( cancellationToken ) ;
124
+ }
125
+
126
+ /// <summary>
127
+ /// Completes an async read when reading a header is required.
128
+ /// </summary>
129
+ /// <param name="readTask">The active async read task from the ProtocolReader.</param>
130
+ /// <param name="cancellationToken">A cancellation token.</param>
131
+ /// <returns>A MessageReadResult.</returns>
132
+ private async ValueTask < MessageReadResult > DoReadHeaderRequiredAsync ( ValueTask < WebSocketReadFrame > readTask , CancellationToken cancellationToken )
133
+ {
134
+ var frame = await readTask . ConfigureAwait ( false ) ;
135
+
136
+ ValidateHeader ( frame . Header ) ;
137
+
138
+ _header = frame . Header ;
139
+ _payloadReader = frame . Payload ;
140
+
141
+ return await ReadPayloadAsync ( cancellationToken ) ;
142
+ }
143
+
144
+ /// <summary>
145
+ /// Reads a portion of a message payload.
146
+ /// </summary>
147
+ /// <param name="cancellationToken">A cancellation token.</param>
148
+ /// <returns>A MessageReadResult.</returns>
149
+ private ValueTask < MessageReadResult > ReadPayloadAsync ( CancellationToken cancellationToken )
150
+ {
115
151
//Don't keep reading data into the buffer if we've hit a threshold
116
152
//TODO: Is this even the right value to use in this context?
117
153
if ( _buffer . UnconsumedWrittenCount < _options . PauseWriterThreshold )
118
154
{
119
155
var readTask = _protocolReader . ReadAsync ( _payloadReader , cancellationToken ) ;
120
- ProtocolReadResult < ReadOnlySequence < byte > > payloadSequence ;
121
-
122
156
if ( readTask . IsCompletedSuccessfully )
123
157
{
124
- payloadSequence = readTask . Result ;
158
+ PopulateFromRead ( readTask . Result ) ;
125
159
}
126
160
else
127
161
{
128
- payloadSequence = await readTask ;
129
- }
130
-
131
- if ( payloadSequence . IsCanceled )
132
- {
133
- throw new OperationCanceledException ( "Read canceled while attempting to read WebSocket payload." ) ;
134
- }
135
-
136
- var sequence = payloadSequence . Message ;
137
-
138
- //If there is already data in the buffer, we'll need to add to it
139
- if ( _buffer . UnconsumedWrittenCount > 0 )
140
- {
141
- if ( sequence . IsSingleSegment )
142
- {
143
- _buffer . Write ( sequence . FirstSpan ) ;
144
- }
145
- else
146
- {
147
- foreach ( var segment in sequence )
148
- {
149
- _buffer . Write ( segment . Span ) ;
150
- }
151
- }
152
- }
162
+ return CreateMessageReadResultAsync ( readTask , cancellationToken ) ;
163
+ }
164
+ }
153
165
154
- _currentSequence = payloadSequence . Message ;
155
- _isCompleted = payloadSequence . IsCompleted ;
156
- _isCanceled = payloadSequence . IsCanceled ;
166
+ var endOfMessage = _header . Fin && _payloadReader . BytesRemaining == 0 ;
157
167
158
- _awaitingHeader = _payloadReader . BytesRemaining == 0 ;
168
+ //Serve back buffered data, if it exists, else give the direct sequence without buffering
169
+ if ( _buffer . UnconsumedWrittenCount > 0 )
170
+ {
171
+ return new ValueTask < MessageReadResult > (
172
+ new MessageReadResult ( new ReadOnlySequence < byte > ( _buffer . WrittenMemory ) , endOfMessage , _isCanceled , _isCompleted ) ) ;
159
173
}
174
+ else
175
+ {
176
+ return new ValueTask < MessageReadResult > ( new MessageReadResult ( _currentSequence , endOfMessage , _isCanceled , _isCompleted ) ) ;
177
+ }
178
+ }
179
+
180
+ /// <summary>
181
+ /// Creates a new MessageReadResult asynchronously.
182
+ /// </summary>
183
+ /// <param name="readTask">The active read task from the ProtocolReader.</param>
184
+ /// <param name="cancellationToken">A cancellation token.</param>
185
+ /// <returns>A new MessageReadResult.</returns>
186
+ private async ValueTask < MessageReadResult > CreateMessageReadResultAsync ( ValueTask < ProtocolReadResult < ReadOnlySequence < byte > > > readTask , CancellationToken cancellationToken )
187
+ {
188
+ PopulateFromRead ( await readTask ) ;
160
189
161
190
var endOfMessage = _header . Fin && _payloadReader . BytesRemaining == 0 ;
162
191
@@ -171,6 +200,42 @@ public async ValueTask<MessageReadResult> ReadAsync(CancellationToken cancellati
171
200
}
172
201
}
173
202
203
+ /// <summary>
204
+ /// Populates the message reader from a payload read result.
205
+ /// </summary>
206
+ /// <param name="readResult">The read result to populate the message reader from.</param>
207
+ private void PopulateFromRead ( ProtocolReadResult < ReadOnlySequence < byte > > readResult )
208
+ {
209
+ if ( readResult . IsCanceled )
210
+ {
211
+ throw new OperationCanceledException ( "Read canceled while attempting to read WebSocket payload." ) ;
212
+ }
213
+
214
+ var sequence = readResult . Message ;
215
+
216
+ //If there is already data in the buffer, we'll need to add to it
217
+ if ( _buffer . UnconsumedWrittenCount > 0 )
218
+ {
219
+ if ( sequence . IsSingleSegment )
220
+ {
221
+ _buffer . Write ( sequence . FirstSpan ) ;
222
+ }
223
+ else
224
+ {
225
+ foreach ( var segment in sequence )
226
+ {
227
+ _buffer . Write ( segment . Span ) ;
228
+ }
229
+ }
230
+ }
231
+
232
+ _currentSequence = readResult . Message ;
233
+ _isCompleted = readResult . IsCompleted ;
234
+ _isCanceled = readResult . IsCanceled ;
235
+
236
+ _awaitingHeader = _payloadReader . BytesRemaining == 0 ;
237
+ }
238
+
174
239
/// <summary>
175
240
/// Advances the reader to the provided position.
176
241
/// </summary>
@@ -223,15 +288,40 @@ public void AdvanceTo(SequencePosition consumed, SequencePosition examined)
223
288
/// </summary>
224
289
/// <param name="cancellationToken">A cancellation token, if any.</param>
225
290
/// <returns>True if the message is text, false otherwise.</returns>
226
- public async ValueTask < bool > MoveNextMessageAsync ( CancellationToken cancellationToken = default )
291
+ public ValueTask < bool > MoveNextMessageAsync ( CancellationToken cancellationToken = default )
227
292
{
228
293
if ( _payloadReader is object && _payloadReader . BytesRemaining != 0 )
229
294
{
230
295
throw new InvalidOperationException ( "MoveNextMessageAsync cannot be called while a message is still being read." ) ;
231
296
}
232
297
233
- var frame = await GetNextMessageFrameAsync ( cancellationToken ) ;
298
+ var readTask = GetNextMessageFrameAsync ( cancellationToken ) ;
299
+ if ( readTask . IsCompletedSuccessfully )
300
+ {
301
+ return new ValueTask < bool > ( SetNextMessageAndGetIsText ( readTask . Result ) ) ;
302
+ }
234
303
304
+ return DoSetNextMessageAsync ( readTask ) ;
305
+ }
306
+
307
+ /// <summary>
308
+ /// Sets the next message frame asynchronously.
309
+ /// </summary>
310
+ /// <param name="readTask">The active ProtocolReader read task.</param>
311
+ /// <returns>True if the next message is a text message, false otherwise.</returns>
312
+ private async ValueTask < bool > DoSetNextMessageAsync ( ValueTask < WebSocketReadFrame > readTask )
313
+ {
314
+ return SetNextMessageAndGetIsText ( await readTask ) ;
315
+ }
316
+
317
+ /// <summary>
318
+ /// Sets the message reader up with the next message frame data and determines if the message
319
+ /// is a text or binary message.
320
+ /// </summary>
321
+ /// <param name="frame">The read frame to set the message reader with.</param>
322
+ /// <returns>True if the next message is text, false otherwise.</returns>
323
+ private bool SetNextMessageAndGetIsText ( WebSocketReadFrame frame )
324
+ {
235
325
if ( frame . Header . Opcode != WebSocketOpcode . Binary && frame . Header . Opcode != WebSocketOpcode . Text )
236
326
{
237
327
ThrowBadProtocol ( $ "Expected a start of message frame of Binary or Text but received { frame . Header . Opcode } instead.") ;
0 commit comments