Files
Fabian Fett 5d235c0a3b Add ControlPlaneRequestEncoder (#239)
Add a new `ControlPlaneRequestEncoder` that encodes all control plane requests into an existing, reused buffer.
2021-12-11 13:35:57 +01:00

180 lines
8.0 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2021 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// 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
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaRuntimeCore
import NIOCore
import NIOEmbedded
import NIOHTTP1
import XCTest
final class ControlPlaneRequestEncoderTests: XCTestCase {
let host = "192.168.0.1"
var client: EmbeddedChannel!
var server: EmbeddedChannel!
override func setUp() {
self.client = EmbeddedChannel(handler: ControlPlaneRequestEncoderHandler(host: self.host))
self.server = EmbeddedChannel(handlers: [
ByteToMessageHandler(HTTPRequestDecoder(leftOverBytesStrategy: .dropBytes)),
NIOHTTPServerRequestAggregator(maxContentLength: 1024 * 1024),
])
}
override func tearDown() {
XCTAssertNoThrow(try self.client.finish(acceptAlreadyClosed: false))
XCTAssertNoThrow(try self.server.finish(acceptAlreadyClosed: false))
self.client = nil
self.server = nil
}
func testNextRequest() {
var request: NIOHTTPServerRequestFull?
XCTAssertNoThrow(request = try self.sendRequest(.next))
XCTAssertEqual(request?.head.isKeepAlive, true)
XCTAssertEqual(request?.head.method, .GET)
XCTAssertEqual(request?.head.uri, "/2018-06-01/runtime/invocation/next")
XCTAssertEqual(request?.head.version, .http1_1)
XCTAssertEqual(request?.head.headers["host"], [self.host])
XCTAssertEqual(request?.head.headers["user-agent"], ["Swift-Lambda/Unknown"])
XCTAssertNil(try self.server.readInbound(as: NIOHTTPServerRequestFull.self))
}
func testPostInvocationSuccessWithoutBody() {
let requestID = UUID().uuidString
var request: NIOHTTPServerRequestFull?
XCTAssertNoThrow(request = try self.sendRequest(.invocationResponse(requestID, nil)))
XCTAssertEqual(request?.head.isKeepAlive, true)
XCTAssertEqual(request?.head.method, .POST)
XCTAssertEqual(request?.head.uri, "/2018-06-01/runtime/invocation/\(requestID)/response")
XCTAssertEqual(request?.head.version, .http1_1)
XCTAssertEqual(request?.head.headers["host"], [self.host])
XCTAssertEqual(request?.head.headers["user-agent"], ["Swift-Lambda/Unknown"])
XCTAssertEqual(request?.head.headers["content-length"], ["0"])
XCTAssertNil(try self.server.readInbound(as: NIOHTTPServerRequestFull.self))
}
func testPostInvocationSuccessWithBody() {
let requestID = UUID().uuidString
let payload = ByteBuffer(string: "hello swift lambda!")
var request: NIOHTTPServerRequestFull?
XCTAssertNoThrow(request = try self.sendRequest(.invocationResponse(requestID, payload)))
XCTAssertEqual(request?.head.isKeepAlive, true)
XCTAssertEqual(request?.head.method, .POST)
XCTAssertEqual(request?.head.uri, "/2018-06-01/runtime/invocation/\(requestID)/response")
XCTAssertEqual(request?.head.version, .http1_1)
XCTAssertEqual(request?.head.headers["host"], [self.host])
XCTAssertEqual(request?.head.headers["user-agent"], ["Swift-Lambda/Unknown"])
XCTAssertEqual(request?.head.headers["content-length"], ["\(payload.readableBytes)"])
XCTAssertEqual(request?.body, payload)
XCTAssertNil(try self.server.readInbound(as: NIOHTTPServerRequestFull.self))
}
func testPostInvocationErrorWithBody() {
let requestID = UUID().uuidString
let error = ErrorResponse(errorType: "SomeError", errorMessage: "An error happened")
var request: NIOHTTPServerRequestFull?
XCTAssertNoThrow(request = try self.sendRequest(.invocationError(requestID, error)))
XCTAssertEqual(request?.head.isKeepAlive, true)
XCTAssertEqual(request?.head.method, .POST)
XCTAssertEqual(request?.head.uri, "/2018-06-01/runtime/invocation/\(requestID)/error")
XCTAssertEqual(request?.head.version, .http1_1)
XCTAssertEqual(request?.head.headers["host"], [self.host])
XCTAssertEqual(request?.head.headers["user-agent"], ["Swift-Lambda/Unknown"])
XCTAssertEqual(request?.head.headers["lambda-runtime-function-error-type"], ["Unhandled"])
let expectedBody = #"{"errorType":"SomeError","errorMessage":"An error happened"}"#
XCTAssertEqual(request?.head.headers["content-length"], ["\(expectedBody.utf8.count)"])
XCTAssertEqual(try request?.body?.getString(at: 0, length: XCTUnwrap(request?.body?.readableBytes)),
expectedBody)
XCTAssertNil(try self.server.readInbound(as: NIOHTTPServerRequestFull.self))
}
func testPostStartupError() {
let error = ErrorResponse(errorType: "StartupError", errorMessage: "Urgh! Startup failed. 😨")
var request: NIOHTTPServerRequestFull?
XCTAssertNoThrow(request = try self.sendRequest(.initializationError(error)))
XCTAssertEqual(request?.head.isKeepAlive, true)
XCTAssertEqual(request?.head.method, .POST)
XCTAssertEqual(request?.head.uri, "/2018-06-01/runtime/init/error")
XCTAssertEqual(request?.head.version, .http1_1)
XCTAssertEqual(request?.head.headers["host"], [self.host])
XCTAssertEqual(request?.head.headers["user-agent"], ["Swift-Lambda/Unknown"])
XCTAssertEqual(request?.head.headers["lambda-runtime-function-error-type"], ["Unhandled"])
let expectedBody = #"{"errorType":"StartupError","errorMessage":"Urgh! Startup failed. 😨"}"#
XCTAssertEqual(request?.head.headers["content-length"], ["\(expectedBody.utf8.count)"])
XCTAssertEqual(try request?.body?.getString(at: 0, length: XCTUnwrap(request?.body?.readableBytes)),
expectedBody)
XCTAssertNil(try self.server.readInbound(as: NIOHTTPServerRequestFull.self))
}
func testMultipleNextAndResponseSuccessRequests() {
for _ in 0 ..< 1000 {
var nextRequest: NIOHTTPServerRequestFull?
XCTAssertNoThrow(nextRequest = try self.sendRequest(.next))
XCTAssertEqual(nextRequest?.head.method, .GET)
XCTAssertEqual(nextRequest?.head.uri, "/2018-06-01/runtime/invocation/next")
let requestID = UUID().uuidString
let payload = ByteBuffer(string: "hello swift lambda!")
var successRequest: NIOHTTPServerRequestFull?
XCTAssertNoThrow(successRequest = try self.sendRequest(.invocationResponse(requestID, payload)))
XCTAssertEqual(successRequest?.head.method, .POST)
XCTAssertEqual(successRequest?.head.uri, "/2018-06-01/runtime/invocation/\(requestID)/response")
}
}
func sendRequest(_ request: ControlPlaneRequest) throws -> NIOHTTPServerRequestFull? {
try self.client.writeOutbound(request)
while let part = try self.client.readOutbound(as: ByteBuffer.self) {
XCTAssertNoThrow(try self.server.writeInbound(part))
}
return try self.server.readInbound(as: NIOHTTPServerRequestFull.self)
}
}
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)
}
}