mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
e58d89148c
- Adjust notice, security reporting, code of conduct, contribution process to the standard AWS documents - Adjust GitHub issue templates to AWS standard ones. - Adjust the license header in all source files --------- Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
231 lines
9.0 KiB
Swift
231 lines
9.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the SwiftAWSLambdaRuntime open source project
|
|
//
|
|
// Copyright SwiftAWSLambdaRuntime project authors
|
|
// Copyright (c) Amazon.com, Inc. or its affiliates.
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIOCore
|
|
import NIOEmbedded
|
|
import NIOHTTP1
|
|
import Testing
|
|
|
|
@testable import AWSLambdaRuntime
|
|
|
|
#if canImport(FoundationEssentials)
|
|
import FoundationEssentials
|
|
#else
|
|
import Foundation
|
|
#endif
|
|
|
|
struct ControlPlaneRequestEncoderTests {
|
|
let host = "192.168.0.1"
|
|
|
|
@available(LambdaSwift 2.0, *)
|
|
func createChannels() -> (client: EmbeddedChannel, server: EmbeddedChannel) {
|
|
let client = EmbeddedChannel(handler: ControlPlaneRequestEncoderHandler(host: self.host))
|
|
let server = EmbeddedChannel(handlers: [
|
|
ByteToMessageHandler(HTTPRequestDecoder(leftOverBytesStrategy: .dropBytes)),
|
|
NIOHTTPServerRequestAggregator(maxContentLength: 1024 * 1024),
|
|
])
|
|
return (client, server)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testNextRequest() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
let request = try sendRequest(.next, client: client, server: server)
|
|
|
|
#expect(request?.head.isKeepAlive == true)
|
|
#expect(request?.head.method == .GET)
|
|
#expect(request?.head.uri == "/2018-06-01/runtime/invocation/next")
|
|
#expect(request?.head.version == .http1_1)
|
|
#expect(request?.head.headers["host"] == [self.host])
|
|
#expect(request?.head.headers["user-agent"] == [.userAgent])
|
|
|
|
#expect(try server.readInbound(as: NIOHTTPServerRequestFull.self) == nil)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testPostInvocationSuccessWithoutBody() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
let requestID = UUID().uuidString
|
|
let request = try sendRequest(.invocationResponse(requestID, nil), client: client, server: server)
|
|
|
|
#expect(request?.head.isKeepAlive == true)
|
|
#expect(request?.head.method == .POST)
|
|
#expect(request?.head.uri == "/2018-06-01/runtime/invocation/\(requestID)/response")
|
|
#expect(request?.head.version == .http1_1)
|
|
#expect(request?.head.headers["host"] == [self.host])
|
|
#expect(request?.head.headers["user-agent"] == [.userAgent])
|
|
#expect(request?.head.headers["content-length"] == ["0"])
|
|
|
|
#expect(try server.readInbound(as: NIOHTTPServerRequestFull.self) == nil)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testPostInvocationSuccessWithBody() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
let requestID = UUID().uuidString
|
|
let payload = ByteBuffer(string: "hello swift lambda!")
|
|
|
|
let request = try sendRequest(.invocationResponse(requestID, payload), client: client, server: server)
|
|
|
|
#expect(request?.head.isKeepAlive == true)
|
|
#expect(request?.head.method == .POST)
|
|
#expect(request?.head.uri == "/2018-06-01/runtime/invocation/\(requestID)/response")
|
|
#expect(request?.head.version == .http1_1)
|
|
#expect(request?.head.headers["host"] == [self.host])
|
|
#expect(request?.head.headers["user-agent"] == [.userAgent])
|
|
#expect(request?.head.headers["content-length"] == ["\(payload.readableBytes)"])
|
|
#expect(request?.body == payload)
|
|
|
|
#expect(try server.readInbound(as: NIOHTTPServerRequestFull.self) == nil)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testPostInvocationErrorWithBody() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
let requestID = UUID().uuidString
|
|
let error = ErrorResponse(errorType: "SomeError", errorMessage: "An error happened")
|
|
let request = try sendRequest(.invocationError(requestID, error), client: client, server: server)
|
|
|
|
#expect(request?.head.isKeepAlive == true)
|
|
#expect(request?.head.method == .POST)
|
|
#expect(request?.head.uri == "/2018-06-01/runtime/invocation/\(requestID)/error")
|
|
#expect(request?.head.version == .http1_1)
|
|
#expect(request?.head.headers["host"] == [self.host])
|
|
#expect(request?.head.headers["user-agent"] == [.userAgent])
|
|
#expect(request?.head.headers["lambda-runtime-function-error-type"] == ["Unhandled"])
|
|
let expectedBody = #"{"errorType":"SomeError","errorMessage":"An error happened"}"#
|
|
|
|
#expect(request?.head.headers["content-length"] == ["\(expectedBody.utf8.count)"])
|
|
let bodyString = request?.body?.getString(at: 0, length: request?.body?.readableBytes ?? 0)
|
|
#expect(bodyString == expectedBody)
|
|
|
|
#expect(try server.readInbound(as: NIOHTTPServerRequestFull.self) == nil)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testPostStartupError() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
let error = ErrorResponse(errorType: "StartupError", errorMessage: "Urgh! Startup failed. 😨")
|
|
let request = try sendRequest(.initializationError(error), client: client, server: server)
|
|
|
|
#expect(request?.head.isKeepAlive == true)
|
|
#expect(request?.head.method == .POST)
|
|
#expect(request?.head.uri == "/2018-06-01/runtime/init/error")
|
|
#expect(request?.head.version == .http1_1)
|
|
#expect(request?.head.headers["host"] == [self.host])
|
|
#expect(request?.head.headers["user-agent"] == [.userAgent])
|
|
#expect(request?.head.headers["lambda-runtime-function-error-type"] == ["Unhandled"])
|
|
let expectedBody = #"{"errorType":"StartupError","errorMessage":"Urgh! Startup failed. 😨"}"#
|
|
#expect(request?.head.headers["content-length"] == ["\(expectedBody.utf8.count)"])
|
|
let bodyString = request?.body?.getString(at: 0, length: request?.body?.readableBytes ?? 0)
|
|
#expect(bodyString == expectedBody)
|
|
|
|
#expect(try server.readInbound(as: NIOHTTPServerRequestFull.self) == nil)
|
|
}
|
|
|
|
@Test
|
|
@available(LambdaSwift 2.0, *)
|
|
func testMultipleNextAndResponseSuccessRequests() throws {
|
|
let (client, server) = createChannels()
|
|
defer {
|
|
_ = try? client.finish(acceptAlreadyClosed: false)
|
|
_ = try? server.finish(acceptAlreadyClosed: false)
|
|
}
|
|
|
|
for _ in 0..<1000 {
|
|
let nextRequest = try sendRequest(.next, client: client, server: server)
|
|
#expect(nextRequest?.head.method == .GET)
|
|
#expect(nextRequest?.head.uri == "/2018-06-01/runtime/invocation/next")
|
|
|
|
let requestID = UUID().uuidString
|
|
let payload = ByteBuffer(string: "hello swift lambda!")
|
|
let successRequest = try sendRequest(
|
|
.invocationResponse(requestID, payload),
|
|
client: client,
|
|
server: server
|
|
)
|
|
#expect(successRequest?.head.method == .POST)
|
|
#expect(successRequest?.head.uri == "/2018-06-01/runtime/invocation/\(requestID)/response")
|
|
}
|
|
}
|
|
|
|
@available(LambdaSwift 2.0, *)
|
|
func sendRequest(
|
|
_ request: ControlPlaneRequest,
|
|
client: EmbeddedChannel,
|
|
server: EmbeddedChannel
|
|
) throws -> NIOHTTPServerRequestFull? {
|
|
try client.writeOutbound(request)
|
|
while let part = try client.readOutbound(as: ByteBuffer.self) {
|
|
try server.writeInbound(part)
|
|
}
|
|
return try server.readInbound(as: NIOHTTPServerRequestFull.self)
|
|
}
|
|
}
|
|
|
|
@available(LambdaSwift 2.0, *)
|
|
private final class ControlPlaneRequestEncoderHandler: ChannelOutboundHandler {
|
|
typealias OutboundIn = ControlPlaneRequest
|
|
typealias OutboundOut = ByteBuffer
|
|
|
|
private var encoder: ControlPlaneRequestEncoder
|
|
|
|
init(host: String) {
|
|
self.encoder = ControlPlaneRequestEncoder(host: host)
|
|
}
|
|
|
|
func handlerAdded(context: ChannelHandlerContext) {
|
|
self.encoder.writerAdded(context: context)
|
|
}
|
|
|
|
func handlerRemoved(context: ChannelHandlerContext) {
|
|
self.encoder.writerRemoved(context: context)
|
|
}
|
|
|
|
func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
|
|
self.encoder.writeRequest(self.unwrapOutboundIn(data), context: context, promise: promise)
|
|
}
|
|
}
|