@@ -20,45 +20,96 @@ import Foundation
2020import LambdaSwiftSprinter
2121import NIO
2222import NIOHTTP1
23+ import NIOFoundationCompat
2324
25+ /**
26+ `SprinterNIO` implements the AWS Lambda Custom Runtime with `SwiftNIO`
27+ */
2428public typealias SprinterNIO = Sprinter < LambdaApiNIO >
2529
30+
31+ /**
32+ SprinterNIOError
33+ An error related to the `SprinterNIO`
34+
35+ ### Errors: ###
36+ ```
37+ case invalidResponse(HTTPResponseStatus)
38+ case invalidBuffer
39+ ```
40+ */
2641public enum SprinterNIOError : Error {
42+
43+ /// Invalid Reponse with `HTTPResponseStatus`
2744 case invalidResponse( HTTPResponseStatus )
45+
46+ /// Invalid Buffer
2847 case invalidBuffer
2948}
3049
50+ /** The amount of time the lambda waits for the next event.
51+
52+ The `default` timeout for a Lambda is `3600` seconds.
53+ */
3154public var lambdaRuntimeTimeout : TimeAmount = . seconds( 3600 )
55+
56+ /// The timeout used to create the instance of the `httpClient`
3257public var timeout = HTTPClient . Configuration. Timeout ( connect: lambdaRuntimeTimeout,
3358 read: lambdaRuntimeTimeout)
3459
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+ */
4064public protocol HTTPClientProtocol : class {
65+ var eventLoopGroup : EventLoopGroup { get }
4166 func get( url: String , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
4267 func post( url: String , body: HTTPClient . Body ? , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
4368 func execute( request: HTTPClient . Request , deadline: NIODeadline ? ) -> EventLoopFuture < HTTPClient . Response >
4469 func syncShutdown( ) throws
4570}
4671
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+
4782extension HTTPClient : HTTPClientProtocol {
4883
4984}
5085
86+ /// The `LambdaApiNIO` class implements the LambdaAPI protocol using NIO.
87+ ///
5188public class LambdaApiNIO : LambdaAPI {
89+
5290 let urlBuilder : LambdaRuntimeAPIUrlBuilder
91+
92+ private let _nextInvocationRequest : HTTPClient . Request
5393
94+ /// Construct a `LambdaApiNIO` class.
95+ ///
96+ /// - parameters
97+ /// - awsLambdaRuntimeAPI: AWS_LAMBDA_RUNTIME_API
5498 public required init ( awsLambdaRuntimeAPI: String ) throws {
5599 self . urlBuilder = try LambdaRuntimeAPIUrlBuilder ( awsLambdaRuntimeAPI: awsLambdaRuntimeAPI)
100+ self . _nextInvocationRequest = try HTTPClient . Request ( url: urlBuilder. nextInvocationURL ( ) , method: . GET)
56101 }
57102
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.
58110 public func getNextInvocation( ) throws -> ( event: Data , responseHeaders: [ AnyHashable : Any ] ) {
59- let request = try HTTPClient . Request ( url: urlBuilder. nextInvocationURL ( ) , method: . GET)
60111 let result = try httpClient. execute (
61- request: request ,
112+ request: _nextInvocationRequest ,
62113 deadline: nil
63114 ) . wait ( )
64115
@@ -69,14 +120,22 @@ public class LambdaApiNIO: LambdaAPI {
69120 }
70121
71122 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) {
74126 return ( event: data, responseHeaders: httpHeaders. dictionary)
75127 } else {
76128 throw SprinterNIOError . invalidBuffer
77129 }
78130 }
79131
132+ /// Sends an invocation response to Lambda.
133+ ///
134+ /// - parameters:
135+ /// - requestId: Request ID
136+ /// - httpBody: data body.
137+ /// - throws:
138+ /// - HttpClient errors
80139 public func postInvocationResponse( for requestId: String , httpBody: Data ) throws {
81140 var request = try HTTPClient . Request (
82141 url: urlBuilder. invocationResponseURL ( requestId: requestId) ,
@@ -89,6 +148,13 @@ public class LambdaApiNIO: LambdaAPI {
89148 ) . wait ( )
90149 }
91150
151+ /// Sends an invocation error to Lambda.
152+ ///
153+ /// - parameters:
154+ /// - requestId: Request ID
155+ /// - error: error
156+ /// - throws:
157+ /// - HttpClient errors
92158 public func postInvocationError( for requestId: String , error: Error ) throws {
93159 let errorMessage = String ( describing: error)
94160 let invocationError = InvocationError ( errorMessage: errorMessage,
@@ -105,6 +171,12 @@ public class LambdaApiNIO: LambdaAPI {
105171 ) . wait ( )
106172 }
107173
174+ /// Sends an initialization error to Lambda.
175+ ///
176+ /// - parameters:
177+ /// - error: error
178+ /// - throws:
179+ /// - HttpClient errors
108180 public func postInitializationError( error: Error ) throws {
109181 let errorMessage = String ( describing: error)
110182 let invocationError = InvocationError ( errorMessage: errorMessage,
0 commit comments