@@ -42,7 +42,7 @@ internal final class AMQPConnectionMultiplexHandler: ChannelDuplexHandler {
42
42
// NOTE: this can be extended to keep some state of the open request so a response can be verified against its request
43
43
var pendingRequests : Deque < EventLoopPromise < AMQPResponse > >
44
44
weak var eventHandler : AMQPChannelHandler ?
45
- var nextMessage : ( frame : Frame . Method . Basic , properties : Properties ? , bodySize : UInt64 ? , prevBody : ByteBuffer ? ) ?
45
+ var nextMessage : PartialDelivery ?
46
46
47
47
init ( initialResponsePromise: EventLoopPromise < AMQPResponse > ) {
48
48
pendingRequests = . init( [ initialResponsePromise] )
@@ -207,7 +207,7 @@ internal final class AMQPConnectionMultiplexHandler: ChannelDuplexHandler {
207
207
channel. fulfilNextPendingRequest ( with: . channel( . message( . get( ) ) ) )
208
208
case . deliver, . getOk, . return:
209
209
// TODO: wrap this away more nicely, assert message must be nil
210
- channel. nextMessage = ( frame : basic, nil , nil , nil )
210
+ channel. nextMessage = PartialDelivery ( method : basic)
211
211
case . recoverOk:
212
212
channel. fulfilNextPendingRequest ( with: . channel( . basic( . recovered) ) )
213
213
case let . consumeOk( consumerTag) :
@@ -266,72 +266,56 @@ internal final class AMQPConnectionMultiplexHandler: ChannelDuplexHandler {
266
266
}
267
267
}
268
268
case let . header( header) :
269
- channel. nextMessage? . properties = header. properties
270
- channel. nextMessage? . bodySize = header. bodySize
269
+ channel. nextMessage? . setHeader ( header)
271
270
case let . body( body) :
272
- guard let msg = channel. nextMessage, let properties = msg. properties, let bodySize = msg. bodySize else {
273
- // TODO: take down channel
274
- return
275
- }
276
-
277
- let prevSize = msg. prevBody? . readableBytes ?? 0
278
- if ( prevSize + body. readableBytes < bodySize) {
279
- if var prevBody = msg. prevBody {
280
- prevBody. writeImmutableBuffer ( body)
281
- channel. nextMessage? . prevBody = prevBody
282
- } else {
283
- channel. nextMessage? . prevBody = body
284
- }
285
-
286
- return
287
- }
288
-
289
- let completeBody : ByteBuffer
290
-
291
- if var prevBody = msg. prevBody {
292
- prevBody. writeImmutableBuffer ( body)
293
- completeBody = prevBody
294
- } else {
295
- completeBody = body
296
- }
297
-
298
- switch msg. frame {
299
- case let . getOk( getOk) :
300
- channel. fulfilNextPendingRequest ( with: . channel( . message( . get( . init(
301
- message: . init(
302
- exchange: getOk. exchange,
303
- routingKey: getOk. routingKey,
304
- deliveryTag: getOk. deliveryTag,
305
- properties: properties,
306
- redelivered: getOk. redelivered,
307
- body: completeBody
308
- ) ,
309
- messageCount: getOk. messageCount
310
- ) ) ) ) )
311
- case let . deliver( deliver) :
312
- channel. eventHandler? . receiveDelivery (
313
- . init(
314
- exchange: deliver. exchange,
315
- routingKey: deliver. routingKey,
316
- deliveryTag: deliver. deliveryTag,
271
+ // TODO: take down channel
272
+ guard channel. nextMessage != nil else { return }
273
+
274
+ // written like this to avoid unnecessary copies
275
+ channel. nextMessage!. addBody ( body)
276
+
277
+ if channel. nextMessage!. isComplete {
278
+ let ( method, properties, completeBody) = channel. nextMessage!. asCompletedMessage ( )
279
+ channel. nextMessage = nil
280
+
281
+ switch method {
282
+ case let . getOk( getOk) :
283
+ channel. fulfilNextPendingRequest ( with: . channel( . message( . get( . init(
284
+ message: . init(
285
+ exchange: getOk. exchange,
286
+ routingKey: getOk. routingKey,
287
+ deliveryTag: getOk. deliveryTag,
288
+ properties: properties,
289
+ redelivered: getOk. redelivered,
290
+ body: completeBody
291
+ ) ,
292
+ messageCount: getOk. messageCount
293
+ ) ) ) ) )
294
+ case let . deliver( deliver) :
295
+ channel. eventHandler? . receiveDelivery (
296
+ . init(
297
+ exchange: deliver. exchange,
298
+ routingKey: deliver. routingKey,
299
+ deliveryTag: deliver. deliveryTag,
300
+ properties: properties,
301
+ redelivered: deliver. redelivered,
302
+ body: completeBody
303
+ ) ,
304
+ for: deliver. consumerTag
305
+ )
306
+ case let . return( `return`) :
307
+ channel. eventHandler? . receiveReturn ( . init(
308
+ replyCode: `return`. replyCode,
309
+ replyText: `return`. replyText,
310
+ exchange: `return`. exchange,
311
+ routingKey: `return`. routingKey,
317
312
properties: properties,
318
- redelivered: deliver. redelivered,
319
313
body: completeBody
320
- ) ,
321
- for: deliver. consumerTag
322
- )
323
- case let . return( `return`) :
324
- channel. eventHandler? . receiveReturn ( . init(
325
- replyCode: `return`. replyCode,
326
- replyText: `return`. replyText,
327
- exchange: `return`. exchange,
328
- routingKey: `return`. routingKey,
329
- properties: properties,
330
- body: completeBody
331
- ) )
332
- default :
333
- // TODO: take down channel
334
- preconditionUnexpectedFrame ( frame)
314
+ ) )
315
+ default :
316
+ // TODO: take down channel
317
+ preconditionUnexpectedFrame ( frame)
318
+ }
335
319
}
336
320
case . heartbeat:
337
321
let heartbeat = Frame ( channelID: frame. channelID, payload: . heartbeat)
@@ -400,3 +384,41 @@ extension AMQPConnectionMultiplexHandler.ChannelState {
400
384
eventHandler? . reportAsClosed ( error: error)
401
385
}
402
386
}
387
+
388
+ private struct PartialDelivery {
389
+ let method : Frame . Method . Basic
390
+ private var header : Frame . Header ?
391
+ private var payload : ByteBuffer ?
392
+
393
+ init ( method: Frame . Method . Basic ) {
394
+ self . method = method
395
+ }
396
+
397
+ var isComplete : Bool { header != nil && header!. bodySize <= ( payload? . readableBytes ?? 0 ) }
398
+
399
+ // NOTE: should be made throwing with validation for a more restrictive protocol implementation
400
+ mutating func setHeader( _ header: consuming Frame . Header ) {
401
+ // validate that self.header == nil
402
+ self . header = header
403
+ }
404
+
405
+ // NOTE: should be made throwing with validation for a more restrictive protocol implementation
406
+ mutating func addBody( _ buffer: consuming ByteBuffer ) {
407
+ guard let header else { return } // probably should take channel down
408
+
409
+ if payload == nil {
410
+ buffer. reserveCapacity ( Int ( header. bodySize) )
411
+ payload = consume buffer
412
+ } else {
413
+ payload!. writeBuffer ( & buffer)
414
+ }
415
+ }
416
+
417
+ func asCompletedMessage( ) -> ( Frame . Method . Basic , Properties , ByteBuffer ) {
418
+ // NOTE: this could be made a consuming func once partial is possible I think
419
+ assert ( isComplete)
420
+
421
+ // header and payloads are guaranteed to be non-nil after isComplete
422
+ return ( method, header!. properties, payload!)
423
+ }
424
+ }
0 commit comments