1
1
import NIO
2
2
3
- /// `ChannelInboundHandler` that handles the responsibility of coordinating incoming and outgoing messages
4
- /// on a particular connection to Redis.
5
- internal final class RedisMessenger : ChannelInboundHandler {
6
- /// See `ChannelInboundHandler.InboundIn`
7
- public typealias InboundIn = RedisData
8
-
9
- /// See `ChannelInboundHandler.OutboundOut`
10
- public typealias OutboundOut = RedisData
11
-
12
- /// Queue of promises waiting to receive an incoming response value from a outgoing message.
13
- private var waitingResponseQueue : [ EventLoopPromise < InboundIn > ]
14
- /// Queue of unsent outgoing messages, with the oldest objects at the end of the array.
15
- private var outgoingMessageQueue : [ OutboundOut ]
16
-
17
- /// This handler's event loop.
3
+ /// `ChannelInboundHandler` that is responsible for coordinating incoming and outgoing messages on a particular
4
+ /// connection to Redis.
5
+ internal final class RedisMessenger {
18
6
private let eventLoop : EventLoop
19
7
20
- /// Context used for writing outgoing messages with.
21
- private weak var outputContext : ChannelHandlerContext ?
8
+ /// Context to be used for writing outgoing messages with.
9
+ private var channelContext : ChannelHandlerContext ?
10
+
11
+ /// Queue of promises waiting to receive an incoming response value from an outgoing message.
12
+ private var waitingResponseQueue : [ EventLoopPromise < RedisData > ]
13
+ /// Queue of unset outgoing messages, with the oldest messages at the end of the array.
14
+ private var outgoingMessageQueue : [ RedisData ]
22
15
23
16
/// Creates a new handler that works on the specified `EventLoop`.
24
- public init ( on eventLoop: EventLoop ) {
17
+ init ( on eventLoop: EventLoop ) {
25
18
self . waitingResponseQueue = [ ]
26
19
self . outgoingMessageQueue = [ ]
27
20
self . eventLoop = eventLoop
28
21
}
22
+
23
+ /// Adds a complete message encoded as `RedisData` to the queue and returns an `EventLoopFuture` that resolves
24
+ /// the response from Redis.
25
+ func enqueue( _ output: RedisData ) -> EventLoopFuture < RedisData > {
26
+ // ensure that we are on the event loop before modifying our data
27
+ guard eventLoop. inEventLoop else {
28
+ return eventLoop. submit ( { } ) . then { return self . enqueue ( output) }
29
+ }
30
+
31
+ // add the new output to the writing queue at the front
32
+ outgoingMessageQueue. insert ( output, at: 0 )
33
+
34
+ // every outgoing message is expected to receive some form of response, so create a promise that we'll resolve
35
+ // with the response
36
+ let promise = eventLoop. makePromise ( of: RedisData . self)
37
+ waitingResponseQueue. insert ( promise, at: 0 )
38
+
39
+ // if we have a context for writing, flush the outgoing queue
40
+ channelContext? . eventLoop. execute {
41
+ self . _flushOutgoingQueue ( )
42
+ }
43
+
44
+ return promise. futureResult
45
+ }
46
+
47
+ /// Writes all queued outgoing messages to the channel.
48
+ func _flushOutgoingQueue( ) {
49
+ guard let context = channelContext else { return }
50
+
51
+ while let output = outgoingMessageQueue. popLast ( ) {
52
+ context. write ( wrapOutboundOut ( output) , promise: nil )
53
+ context. flush ( )
54
+ }
55
+ }
56
+ }
29
57
58
+ // MARK: ChannelInboundHandler
59
+
60
+ extension RedisMessenger : ChannelInboundHandler {
61
+ /// See `ChannelInboundHandler.InboundIn`
62
+ public typealias InboundIn = RedisData
63
+
64
+ /// See `ChannelInboundHandler.OutboundOut`
65
+ public typealias OutboundOut = RedisData
66
+
67
+ /// Invoked by NIO when the channel for this handler has become active, receiving a context that is ready to
68
+ /// send messages.
69
+ ///
70
+ /// Any queued messages will be flushed at this point.
30
71
/// See `ChannelInboundHandler.channelActive(ctx:)`
31
72
public func channelActive( ctx: ChannelHandlerContext ) {
32
- outputContext = ctx
73
+ channelContext = ctx
33
74
_flushOutgoingQueue ( )
34
75
}
35
76
77
+ /// Invoked by NIO when an error was thrown earlier in the response chain. The waiting promise at the front
78
+ /// of the queue will be failed with the error.
36
79
/// See `ChannelInboundHandler.errorCaught(ctx:error:)`
37
80
public func errorCaught( ctx: ChannelHandlerContext , error: Error ) {
38
81
guard let leadPromise = waitingResponseQueue. last else {
@@ -41,6 +84,8 @@ internal final class RedisMessenger: ChannelInboundHandler {
41
84
leadPromise. fail ( error: error)
42
85
}
43
86
87
+ /// Invoked by NIO when a read has been fired from earlier in the response chain. This forwards the unwrapped
88
+ /// `RedisData` to the response at the front of the queue.
44
89
/// See `ChannelInboundHandler.channelRead(ctx:data:)`
45
90
public func channelRead( ctx: ChannelHandlerContext , data: NIOAny ) {
46
91
let input = unwrapInboundIn ( data)
@@ -54,38 +99,4 @@ internal final class RedisMessenger: ChannelInboundHandler {
54
99
55
100
leadPromise. succeed ( result: input)
56
101
}
57
-
58
- /// Adds a complete message encoded as `RedisData` to the queue and returns an `EventLoopFuture` that resolves
59
- /// the response from Redis.
60
- func enqueue( _ output: OutboundOut ) -> EventLoopFuture < InboundIn > {
61
- // ensure that we are on the event loop before modifying our data
62
- guard eventLoop. inEventLoop else {
63
- return eventLoop. submit { } . then { return self . enqueue ( output) }
64
- }
65
-
66
- // add the new output to the writing queue at the front
67
- outgoingMessageQueue. insert ( output, at: 0 )
68
-
69
- // every outgoing message is expected to receive some form of response, so build
70
- // the context in the readQueue that we resolve with the response
71
- let promise = eventLoop. makePromise ( of: InboundIn . self)
72
- waitingResponseQueue. insert ( promise, at: 0 )
73
-
74
- // if we have a context for writing, flush the outgoing queue
75
- outputContext? . eventLoop. execute {
76
- self . _flushOutgoingQueue ( )
77
- }
78
-
79
- return promise. futureResult
80
- }
81
-
82
- /// Writes all queued outgoing messages to the channel.
83
- func _flushOutgoingQueue( ) {
84
- guard let context = outputContext else { return }
85
-
86
- while let output = outgoingMessageQueue. popLast ( ) {
87
- context. write ( wrapOutboundOut ( output) , promise: nil )
88
- context. flush ( )
89
- }
90
- }
91
102
}
0 commit comments