@@ -45,8 +45,8 @@ internal static class Framing
45
45
* +------------+---------+----------------+---------+------------------+
46
46
* | 1 byte | 2 bytes | 4 bytes | x bytes | 1 byte |
47
47
* +------------+---------+----------------+---------+------------------+ */
48
- private const int BaseFrameSize = 1 + 2 + 4 + 1 ;
49
- private const int StartPayload = 7 ;
48
+ internal const int BaseFrameSize = 1 + 2 + 4 + 1 ;
49
+ internal const int StartPayload = 7 ;
50
50
51
51
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
52
52
private static int WriteBaseFrame ( Span < byte > span , FrameType type , ushort channel , int payloadLength )
@@ -152,90 +152,104 @@ private InboundFrame(FrameType type, int channel, ReadOnlyMemory<byte> payload,
152
152
_rentedArray = rentedArray ;
153
153
}
154
154
155
- private static void ProcessProtocolHeader ( ReadOnlySpan < byte > protocolError )
156
- {
157
- byte b1 = protocolError [ 0 ] ;
158
- byte b2 = protocolError [ 1 ] ;
159
- byte b3 = protocolError [ 2 ] ;
160
- if ( b1 != 'M' || b2 != 'Q' || b3 != 'P' )
161
- {
162
- throw new MalformedFrameException ( "Invalid AMQP protocol header from server" ) ;
163
- }
164
-
165
- int transportHigh = protocolError [ 3 ] ;
166
- int transportLow = protocolError [ 4 ] ;
167
- int serverMajor = protocolError [ 5 ] ;
168
- int serverMinor = protocolError [ 6 ] ;
169
- throw new PacketNotRecognizedException ( transportHigh , transportLow , serverMajor , serverMinor ) ;
170
- }
171
-
172
155
internal static bool TryReadFrame ( ref ReadOnlySequence < byte > buffer , out InboundFrame frame )
173
156
{
174
- // We'll always need to read at least 8 bytes (type (1) + channel (2) + payloadSize (4) + end marker (1)) or ( 8 bytes of protocol error, see ProcessProtocolHeader).
175
- if ( buffer . Length < 8 )
157
+ // We'll always need to read at least 8 bytes (the Framing.BaseFrameSize) or 8 bytes of protocol error ( see ProcessProtocolHeader).
158
+ if ( buffer . Length < Framing . BaseFrameSize )
176
159
{
177
160
frame = default ;
178
161
return false ;
179
162
}
180
163
164
+ // Let's first check if we have a protocol header.
181
165
if ( buffer . First . Span [ 0 ] == 'A' )
182
166
{
183
- // Probably an AMQP protocol header, otherwise meaningless
184
- if ( buffer . First . Length >= 8 )
185
- {
186
- ProcessProtocolHeader ( buffer . First . Span . Slice ( 1 , 7 ) ) ;
187
- }
188
- else
189
- {
190
- Span < byte > protocolError = stackalloc byte [ 7 ] ;
191
- buffer . Slice ( 1 , 7 ) . CopyTo ( protocolError ) ;
192
- ProcessProtocolHeader ( protocolError ) ;
193
- }
167
+ ProcessProtocolHeader ( buffer ) ;
194
168
}
195
169
196
170
int type = buffer . First . Span [ 0 ] ;
197
- int channel ;
198
- int payloadSize ;
171
+ ParseFrameHeader ( buffer , out int channel , out int payloadSize ) ;
199
172
200
- if ( buffer . First . Length >= 7 )
201
- {
202
- channel = NetworkOrderDeserializer . ReadUInt16 ( buffer . First . Span . Slice ( 1 , 2 ) ) ;
203
- payloadSize = NetworkOrderDeserializer . ReadInt32 ( buffer . First . Span . Slice ( 3 , 4 ) ) ; // FIXME - throw exn on unreasonable value
204
- }
205
- else
206
- {
207
- Span < byte > headerBytes = stackalloc byte [ 6 ] ;
208
- buffer . Slice ( 1 , 6 ) . CopyTo ( headerBytes ) ;
209
- channel = NetworkOrderDeserializer . ReadUInt16 ( headerBytes . Slice ( 0 , 2 ) ) ;
210
- payloadSize = NetworkOrderDeserializer . ReadInt32 ( headerBytes . Slice ( 2 , 4 ) ) ; // FIXME - throw exn on unreasonable value
211
- }
173
+ // We'll need to read the payloadSize + FrameEndMarker (1 byte)
174
+ int readSize = payloadSize + 1 ;
212
175
213
- const int EndMarkerLength = 1 ;
214
- int readSize = payloadSize + EndMarkerLength ;
215
-
216
- // Do we have enough bytes to read an entire frame (type + channel + payloadSize + payload + end marker)
217
- if ( buffer . Length < ( 7 + readSize ) )
176
+ // Do we have enough bytes to read the rest of the frame
177
+ if ( buffer . Length < ( Framing . StartPayload + readSize ) )
218
178
{
219
179
frame = default ;
220
180
return false ;
221
181
}
222
182
223
- // Is returned by InboundFrame.ReturnPayload in Connection.MainLoopIteration
183
+ // The rented array is returned by InboundFrame.ReturnPayload in Connection.MainLoopIteration
224
184
byte [ ] payloadBytes = ArrayPool < byte > . Shared . Rent ( readSize ) ;
225
185
Memory < byte > payloadMemory = payloadBytes . AsMemory ( 0 , readSize ) ;
226
186
ReadOnlySequence < byte > payloadSlice = buffer . Slice ( 7 , readSize ) ;
227
187
payloadSlice . CopyTo ( payloadMemory . Span ) ;
188
+
189
+ // Let's validate that the frame contains a valid Frame End Marker
228
190
if ( payloadBytes [ payloadSize ] != Constants . FrameEnd )
229
191
{
230
192
ArrayPool < byte > . Shared . Return ( payloadBytes ) ;
231
193
throw new MalformedFrameException ( $ "Bad frame end marker: { payloadBytes [ payloadSize ] } ") ;
232
194
}
233
195
234
- buffer = buffer . Slice ( payloadSlice . End ) ;
196
+ // We have a frame!
235
197
frame = new InboundFrame ( ( FrameType ) type , channel , payloadMemory . Slice ( 0 , payloadSize ) , payloadBytes ) ;
198
+
199
+ // Update the buffer
200
+ buffer = buffer . Slice ( payloadSlice . End ) ;
236
201
return true ;
237
202
}
238
203
204
+ private static void ProcessProtocolHeader ( ReadOnlySequence < byte > buffer )
205
+ {
206
+ if ( buffer . First . Length >= 8 )
207
+ {
208
+ EvaluateProtocolError ( buffer . First . Span . Slice ( 1 , 7 ) ) ;
209
+ }
210
+ else
211
+ {
212
+ Span < byte > tempBuffer = stackalloc byte [ 7 ] ;
213
+ buffer . Slice ( 1 , 7 ) . CopyTo ( tempBuffer ) ;
214
+ EvaluateProtocolError ( tempBuffer ) ;
215
+ }
216
+
217
+ void EvaluateProtocolError ( ReadOnlySpan < byte > protocolError )
218
+ {
219
+ byte b1 = protocolError [ 0 ] ;
220
+ byte b2 = protocolError [ 1 ] ;
221
+ byte b3 = protocolError [ 2 ] ;
222
+ if ( b1 != 'M' || b2 != 'Q' || b3 != 'P' )
223
+ {
224
+ throw new MalformedFrameException ( "Invalid AMQP protocol header from server" ) ;
225
+ }
226
+
227
+ int transportHigh = protocolError [ 3 ] ;
228
+ int transportLow = protocolError [ 4 ] ;
229
+ int serverMajor = protocolError [ 5 ] ;
230
+ int serverMinor = protocolError [ 6 ] ;
231
+ throw new PacketNotRecognizedException ( transportHigh , transportLow , serverMajor , serverMinor ) ;
232
+ }
233
+ }
234
+
235
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
236
+
237
+ private static void ParseFrameHeader ( ReadOnlySequence < byte > buffer , out int channel , out int payloadSize )
238
+ {
239
+ if ( buffer . First . Length >= 7 )
240
+ {
241
+ channel = NetworkOrderDeserializer . ReadUInt16 ( buffer . First . Span . Slice ( 1 , 2 ) ) ;
242
+ payloadSize = NetworkOrderDeserializer . ReadInt32 ( buffer . First . Span . Slice ( 3 , 4 ) ) ; // FIXME - throw exn on unreasonable value
243
+ }
244
+ else
245
+ {
246
+ Span < byte > headerBytes = stackalloc byte [ 6 ] ;
247
+ buffer . Slice ( 1 , 6 ) . CopyTo ( headerBytes ) ;
248
+ channel = NetworkOrderDeserializer . ReadUInt16 ( headerBytes . Slice ( 0 , 2 ) ) ;
249
+ payloadSize = NetworkOrderDeserializer . ReadInt32 ( headerBytes . Slice ( 2 , 4 ) ) ; // FIXME - throw exn on unreasonable value
250
+ }
251
+ }
252
+
239
253
public byte [ ] TakeoverPayload ( )
240
254
{
241
255
return _rentedArray ;
0 commit comments