@@ -16,7 +16,6 @@ import Logging
16
16
import NIOCore
17
17
import NIOHTTP1
18
18
import NIOPosix
19
- import _NIOBase64
20
19
21
20
final actor NewLambdaRuntimeClient : LambdaRuntimeClientProtocol {
22
21
nonisolated let unownedExecutor : UnownedSerialExecutor
@@ -69,11 +68,22 @@ final actor NewLambdaRuntimeClient: LambdaRuntimeClientProtocol {
69
68
case sentResponse( requestID: String )
70
69
}
71
70
71
+ enum ClosingState {
72
+ case notClosing
73
+ case closing( CheckedContinuation < Void , Never > )
74
+ }
75
+
72
76
private let eventLoop : any EventLoop
73
77
private let logger : Logger
74
78
private let configuration : Configuration
79
+
75
80
private var connectionState : ConnectionState = . disconnected
76
81
private var lambdaState : LambdaState = . idle( previousRequestID: nil )
82
+ private var closingState : ClosingState = . notClosing
83
+
84
+ // connections that are currently being closed. In the `run` method we must await all of them
85
+ // being fully closed before we can return from it.
86
+ private var closingConnections : [ any Channel ] = [ ]
77
87
78
88
static func withRuntimeClient< Result> (
79
89
configuration: Configuration ,
@@ -89,6 +99,8 @@ final actor NewLambdaRuntimeClient: LambdaRuntimeClientProtocol {
89
99
result = . failure( error)
90
100
}
91
101
102
+ await runtime. close ( )
103
+
92
104
//try? await runtime.close()
93
105
return try result. get ( )
94
106
}
@@ -100,6 +112,31 @@ final actor NewLambdaRuntimeClient: LambdaRuntimeClientProtocol {
100
112
self . logger = logger
101
113
}
102
114
115
+ private func close( ) async {
116
+ guard case . notClosing = self . closingState else {
117
+ return
118
+ }
119
+ await withCheckedContinuation { continuation in
120
+ self . closingState = . closing( continuation)
121
+
122
+ switch self . connectionState {
123
+ case . disconnected:
124
+ break
125
+
126
+ case . connecting( let continuations) :
127
+ for continuation in continuations {
128
+ continuation. resume ( throwing: NewLambdaRuntimeError ( code: . closingRuntimeClient) )
129
+ }
130
+ self . connectionState = . connecting( [ ] )
131
+
132
+ case . connected( let channel, let lambdaChannelHandler) :
133
+ channel. clo
134
+ }
135
+ }
136
+
137
+
138
+ }
139
+
103
140
func nextInvocation( ) async throws -> ( Invocation , Writer ) {
104
141
switch self . lambdaState {
105
142
case . idle:
@@ -190,7 +227,20 @@ final actor NewLambdaRuntimeClient: LambdaRuntimeClientProtocol {
190
227
}
191
228
192
229
private func channelClosed( _ channel: any Channel ) {
193
- // TODO: Fill out
230
+ switch self . connectionState {
231
+ case . disconnected:
232
+ break
233
+
234
+ case . connecting( let array) :
235
+ self . connectionState = . disconnected
236
+
237
+ for continuation in array {
238
+ continuation. resume ( throwing: NewLambdaRuntimeError ( code: . lostConnectionToControlPlane) )
239
+ }
240
+
241
+ case . connected:
242
+ self . connectionState = . disconnected
243
+ }
194
244
}
195
245
196
246
private func makeOrGetConnection( ) async throws -> LambdaChannelHandler < NewLambdaRuntimeClient > {
@@ -227,6 +277,7 @@ final actor NewLambdaRuntimeClient: LambdaRuntimeClientProtocol {
227
277
return channel. eventLoop. makeFailedFuture ( error)
228
278
}
229
279
}
280
+ . connectTimeout ( . seconds( 2 ) )
230
281
231
282
do {
232
283
// connect directly via socket address to avoid happy eyeballs (perf)
@@ -279,6 +330,35 @@ extension NewLambdaRuntimeClient: LambdaChannelHandlerDelegate {
279
330
}
280
331
281
332
nonisolated func connectionWillClose( channel: any Channel ) {
333
+ self . assumeIsolated { isolated in
334
+ switch isolated. connectionState {
335
+ case . disconnected:
336
+ // this case should never happen. But whatever
337
+ if channel. isActive {
338
+ isolated. closingConnections. append ( channel)
339
+ }
340
+
341
+ case . connecting( let continuations) :
342
+ // this case should never happen. But whatever
343
+ if channel. isActive {
344
+ isolated. closingConnections. append ( channel)
345
+ }
346
+
347
+ for continuation in continuations {
348
+ continuation. resume ( throwing: NewLambdaRuntimeError ( code: . connectionToControlPlaneLost) )
349
+ }
350
+
351
+ case . connected( let stateChannel, _) :
352
+ guard channel === stateChannel else {
353
+ isolated. closingConnections. append ( channel)
354
+ return
355
+ }
356
+
357
+ isolated. connectionState = . disconnected
358
+
359
+
360
+ }
361
+ }
282
362
283
363
}
284
364
}
@@ -288,7 +368,7 @@ private protocol LambdaChannelHandlerDelegate {
288
368
func connectionErrorHappened( _ error: any Error , channel: any Channel )
289
369
}
290
370
291
- private final class LambdaChannelHandler < Delegate> {
371
+ private final class LambdaChannelHandler < Delegate: LambdaChannelHandlerDelegate > {
292
372
let nextInvocationPath = Consts . invocationURLPrefix + Consts. getNextInvocationURLSuffix
293
373
294
374
enum State {
@@ -652,27 +732,18 @@ extension LambdaChannelHandler: ChannelInboundHandler {
652
732
653
733
func errorCaught( context: ChannelHandlerContext , error: Error ) {
654
734
// pending responses will fail with lastError in channelInactive since we are calling context.close
735
+ self . delegate. connectionErrorHappened ( error, channel: context. channel)
736
+
655
737
self . lastError = error
656
738
context. channel. close ( promise: nil )
657
739
}
658
740
659
741
func channelInactive( context: ChannelHandlerContext ) {
660
742
// fail any pending responses with last error or assume peer disconnected
661
- context. fireChannelInactive ( )
662
743
663
- // switch self.state {
664
- // case .idle:
665
- // break
666
- //
667
- // case .running(let promise, let timeout):
668
- // self.state = .idle
669
- // timeout?.cancel()
670
- // promise.fail(self.lastError ?? HTTPClient.Errors.connectionResetByPeer)
671
- //
672
- // case .waitForConnectionClose(let response, let promise):
673
- // self.state = .idle
674
- // promise.succeed(response)
675
- // }
744
+ // we don't need to forward channelInactive to the delegate, as the delegate observes the
745
+ // closeFuture
746
+ context. fireChannelInactive ( )
676
747
}
677
748
}
678
749
0 commit comments