|
@@ -18,6 +18,14 @@ import SwiftProtobuf
|
|
|
import NIO
|
|
import NIO
|
|
|
import NIOHTTP1
|
|
import NIOHTTP1
|
|
|
|
|
|
|
|
|
|
+/// For calls which support client streaming we need to delay the creation of the event observer
|
|
|
|
|
+/// until the handler has been added to the pipeline.
|
|
|
|
|
+enum ClientStreamingHandlerObserverState<Factory, Observer> {
|
|
|
|
|
+ case pendingCreation(Factory)
|
|
|
|
|
+ case created(EventLoopFuture<Observer>)
|
|
|
|
|
+ case notRequired
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/// Handles client-streaming calls. Forwards incoming messages and end-of-stream events to the observer block.
|
|
/// Handles client-streaming calls. Forwards incoming messages and end-of-stream events to the observer block.
|
|
|
///
|
|
///
|
|
|
/// - The observer block is implemented by the framework user and fulfills `context.responsePromise` when done.
|
|
/// - The observer block is implemented by the framework user and fulfills `context.responsePromise` when done.
|
|
@@ -25,29 +33,42 @@ import NIOHTTP1
|
|
|
/// they can fail the observer block future.
|
|
/// they can fail the observer block future.
|
|
|
/// - To close the call and send the response, complete `context.responsePromise`.
|
|
/// - To close the call and send the response, complete `context.responsePromise`.
|
|
|
public class ClientStreamingCallHandler<RequestMessage: Message, ResponseMessage: Message>: BaseCallHandler<RequestMessage, ResponseMessage> {
|
|
public class ClientStreamingCallHandler<RequestMessage: Message, ResponseMessage: Message>: BaseCallHandler<RequestMessage, ResponseMessage> {
|
|
|
|
|
+ public typealias Context = UnaryResponseCallContext<ResponseMessage>
|
|
|
public typealias EventObserver = (StreamEvent<RequestMessage>) -> Void
|
|
public typealias EventObserver = (StreamEvent<RequestMessage>) -> Void
|
|
|
- private var eventObserver: EventLoopFuture<EventObserver>?
|
|
|
|
|
|
|
+ public typealias EventObserverFactory = (Context) -> EventLoopFuture<EventObserver>
|
|
|
|
|
|
|
|
|
|
+ private var observerState: ClientStreamingHandlerObserverState<EventObserverFactory, EventObserver>
|
|
|
private var callContext: UnaryResponseCallContext<ResponseMessage>?
|
|
private var callContext: UnaryResponseCallContext<ResponseMessage>?
|
|
|
|
|
|
|
|
// We ask for a future of type `EventObserver` to allow the framework user to e.g. asynchronously authenticate a call.
|
|
// We ask for a future of type `EventObserver` to allow the framework user to e.g. asynchronously authenticate a call.
|
|
|
// If authentication fails, they can simply fail the observer future, which causes the call to be terminated.
|
|
// If authentication fails, they can simply fail the observer future, which causes the call to be terminated.
|
|
|
- public init(channel: Channel, request: HTTPRequestHead, errorDelegate: ServerErrorDelegate?, eventObserverFactory: (UnaryResponseCallContext<ResponseMessage>) -> EventLoopFuture<EventObserver>) {
|
|
|
|
|
- super.init(errorDelegate: errorDelegate)
|
|
|
|
|
|
|
+ public init(channel: Channel, request: HTTPRequestHead, errorDelegate: ServerErrorDelegate?, eventObserverFactory: @escaping EventObserverFactory) {
|
|
|
|
|
+ // Delay the creation of the event observer until `handlerAdded(context:)`, otherwise it is
|
|
|
|
|
+ // possible for the service to write into the pipeline (by fulfilling the response promise
|
|
|
|
|
+ // of the call context outside of the observer) before it has been configured.
|
|
|
|
|
+ self.observerState = .pendingCreation(eventObserverFactory)
|
|
|
|
|
+
|
|
|
let callContext = UnaryResponseCallContextImpl<ResponseMessage>(channel: channel, request: request, errorDelegate: errorDelegate)
|
|
let callContext = UnaryResponseCallContextImpl<ResponseMessage>(channel: channel, request: request, errorDelegate: errorDelegate)
|
|
|
self.callContext = callContext
|
|
self.callContext = callContext
|
|
|
- let eventObserver = eventObserverFactory(callContext)
|
|
|
|
|
- self.eventObserver = eventObserver
|
|
|
|
|
|
|
+
|
|
|
|
|
+ super.init(errorDelegate: errorDelegate)
|
|
|
|
|
+
|
|
|
callContext.responsePromise.futureResult.whenComplete { _ in
|
|
callContext.responsePromise.futureResult.whenComplete { _ in
|
|
|
// When done, reset references to avoid retain cycles.
|
|
// When done, reset references to avoid retain cycles.
|
|
|
- self.eventObserver = nil
|
|
|
|
|
self.callContext = nil
|
|
self.callContext = nil
|
|
|
|
|
+ self.observerState = .notRequired
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public override func handlerAdded(context: ChannelHandlerContext) {
|
|
public override func handlerAdded(context: ChannelHandlerContext) {
|
|
|
- guard let eventObserver = self.eventObserver,
|
|
|
|
|
- let callContext = self.callContext else { return }
|
|
|
|
|
|
|
+ guard let callContext = self.callContext,
|
|
|
|
|
+ case let .pendingCreation(factory) = self.observerState else {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let eventObserver = factory(callContext)
|
|
|
|
|
+ self.observerState = .created(eventObserver)
|
|
|
|
|
+
|
|
|
// Terminate the call if the future providing an observer fails.
|
|
// Terminate the call if the future providing an observer fails.
|
|
|
// This is being done _after_ we have been added as a handler to ensure that the `GRPCServerCodec` required to
|
|
// This is being done _after_ we have been added as a handler to ensure that the `GRPCServerCodec` required to
|
|
|
// translate our outgoing `GRPCServerResponsePart<ResponseMessage>` message is already present on the channel.
|
|
// translate our outgoing `GRPCServerResponsePart<ResponseMessage>` message is already present on the channel.
|
|
@@ -56,13 +77,15 @@ public class ClientStreamingCallHandler<RequestMessage: Message, ResponseMessage
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public override func processMessage(_ message: RequestMessage) {
|
|
public override func processMessage(_ message: RequestMessage) {
|
|
|
- self.eventObserver?.whenSuccess { observer in
|
|
|
|
|
|
|
+ guard case .created(let eventObserver) = self.observerState else { return }
|
|
|
|
|
+ eventObserver.whenSuccess { observer in
|
|
|
observer(.message(message))
|
|
observer(.message(message))
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public override func endOfStreamReceived() throws {
|
|
public override func endOfStreamReceived() throws {
|
|
|
- self.eventObserver?.whenSuccess { observer in
|
|
|
|
|
|
|
+ guard case .created(let eventObserver) = self.observerState else { return }
|
|
|
|
|
+ eventObserver.whenSuccess { observer in
|
|
|
observer(.end)
|
|
observer(.end)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|