@@ -20,45 +20,96 @@ import Foundation
20
20
import LambdaSwiftSprinter
21
21
import NIO
22
22
import NIOHTTP1
23
+ import NIOFoundationCompat
23
24
25
+ /**
26
+ `SprinterNIO` implements the AWS Lambda Custom Runtime with `SwiftNIO`
27
+ */
24
28
public typealias SprinterNIO = Sprinter < LambdaApiNIO >
25
29
30
+
31
+ /**
32
+ SprinterNIOError
33
+ An error related to the `SprinterNIO`
34
+
35
+ ### Errors: ###
36
+ ```
37
+ case invalidResponse(HTTPResponseStatus)
38
+ case invalidBuffer
39
+ ```
40
+ */
26
41
public enum SprinterNIOError : Error {
42
+
43
+ /// Invalid Reponse with `HTTPResponseStatus`
27
44
case invalidResponse( HTTPResponseStatus )
45
+
46
+ /// Invalid Buffer
28
47
case invalidBuffer
29
48
}
30
49
50
+ /** The amount of time the lambda waits for the next event.
51
+
52
+ The `default` timeout for a Lambda is `3600` seconds.
53
+ */
31
54
public var lambdaRuntimeTimeout : TimeAmount = . seconds( 3600 )
55
+
56
+ /// The timeout used to create the instance of the `httpClient`
32
57
public var timeout = HTTPClient . Configuration. Timeout ( connect: lambdaRuntimeTimeout,
33
58
read: lambdaRuntimeTimeout)
34
59
35
- public var httpClient : HTTPClientProtocol = {
36
- let configuration = HTTPClient . Configuration ( timeout: timeout)
37
- return HTTPClient ( eventLoopGroupProvider: . createNew, configuration: configuration)
38
- } ( )
39
-
60
+ /** The HTTPClientProtocol defines a generic httpClient
61
+
62
+ Required for Unit Testing
63
+ */
40
64
public protocol HTTPClientProtocol : class {
65
+ var eventLoopGroup : EventLoopGroup { get }
41
66
func get( url: String , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
42
67
func post( url: String , body: HTTPClient . Body ? , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
43
68
func execute( request: HTTPClient . Request , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
44
69
func syncShutdown( ) throws
45
70
}
46
71
72
+
73
+ /** The httpClient implementing `HTTPClientProtocol`
74
+
75
+ The `default` implementation is an `HTTPClient` defined in `AsyncHTTPClient`
76
+ */
77
+ public var httpClient : HTTPClientProtocol = {
78
+ let configuration = HTTPClient . Configuration ( timeout: timeout)
79
+ return HTTPClient ( eventLoopGroupProvider: . createNew, configuration: configuration)
80
+ } ( )
81
+
47
82
extension HTTPClient : HTTPClientProtocol {
48
83
49
84
}
50
85
86
+ /// The `LambdaApiNIO` class implements the LambdaAPI protocol using NIO.
87
+ ///
51
88
public class LambdaApiNIO : LambdaAPI {
89
+
52
90
let urlBuilder : LambdaRuntimeAPIUrlBuilder
91
+
92
+ private let _nextInvocationRequest : HTTPClient . Request
53
93
94
+ /// Construct a `LambdaApiNIO` class.
95
+ ///
96
+ /// - parameters
97
+ /// - awsLambdaRuntimeAPI: AWS_LAMBDA_RUNTIME_API
54
98
public required init ( awsLambdaRuntimeAPI: String ) throws {
55
99
self . urlBuilder = try LambdaRuntimeAPIUrlBuilder ( awsLambdaRuntimeAPI: awsLambdaRuntimeAPI)
100
+ self . _nextInvocationRequest = try HTTPClient . Request ( url: urlBuilder. nextInvocationURL ( ) , method: . GET)
56
101
}
57
102
103
+ /// Call the next invocation API to get the next event. The response body contains the event data. Response headers contain the `RequestID` and other information.
104
+ ///
105
+ /// - returns:
106
+ /// - `(event: Data, responseHeaders: [AnyHashable: Any])` the event to process and the responseHeaders
107
+ /// - throws:
108
+ /// - `invalidBuffer` if the body is empty or the buffer doesn't contain data.
109
+ /// - `invalidResponse(HTTPResponseStatus)` if the HTTP response is not valid.
58
110
public func getNextInvocation( ) throws -> ( event: Data , responseHeaders: [ AnyHashable : Any ] ) {
59
- let request = try HTTPClient . Request ( url: urlBuilder. nextInvocationURL ( ) , method: . GET)
60
111
let result = try httpClient. execute (
61
- request: request ,
112
+ request: _nextInvocationRequest ,
62
113
deadline: nil
63
114
) . wait ( )
64
115
@@ -69,14 +120,22 @@ public class LambdaApiNIO: LambdaAPI {
69
120
}
70
121
71
122
if let body = result. body,
72
- let buffer = body. getBytes ( at: 0 , length: body. readableBytes) {
73
- let data = buffer. data
123
+ let data = body. getData ( at: 0 ,
124
+ length: body. readableBytes,
125
+ byteTransferStrategy: . noCopy) {
74
126
return ( event: data, responseHeaders: httpHeaders. dictionary)
75
127
} else {
76
128
throw SprinterNIOError . invalidBuffer
77
129
}
78
130
}
79
131
132
+ /// Sends an invocation response to Lambda.
133
+ ///
134
+ /// - parameters:
135
+ /// - requestId: Request ID
136
+ /// - httpBody: data body.
137
+ /// - throws:
138
+ /// - HttpClient errors
80
139
public func postInvocationResponse( for requestId: String , httpBody: Data ) throws {
81
140
var request = try HTTPClient . Request (
82
141
url: urlBuilder. invocationResponseURL ( requestId: requestId) ,
@@ -89,6 +148,13 @@ public class LambdaApiNIO: LambdaAPI {
89
148
) . wait ( )
90
149
}
91
150
151
+ /// Sends an invocation error to Lambda.
152
+ ///
153
+ /// - parameters:
154
+ /// - requestId: Request ID
155
+ /// - error: error
156
+ /// - throws:
157
+ /// - HttpClient errors
92
158
public func postInvocationError( for requestId: String , error: Error ) throws {
93
159
let errorMessage = String ( describing: error)
94
160
let invocationError = InvocationError ( errorMessage: errorMessage,
@@ -105,6 +171,12 @@ public class LambdaApiNIO: LambdaAPI {
105
171
) . wait ( )
106
172
}
107
173
174
+ /// Sends an initialization error to Lambda.
175
+ ///
176
+ /// - parameters:
177
+ /// - error: error
178
+ /// - throws:
179
+ /// - HttpClient errors
108
180
public func postInitializationError( error: Error ) throws {
109
181
let errorMessage = String ( describing: error)
110
182
let invocationError = InvocationError ( errorMessage: errorMessage,
0 commit comments