mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-06-02 07:27:33 +00:00
Add a hard coded version number to the user agent string, for an eventual identification by the Lambda service ### Motivation: It's [an issue](https://github.com/swift-server/swift-aws-lambda-runtime/issues/108) that was open more than 5 years ago and was never addressed. At the time, the consensus was to pickup a version number for the Package.swift file and the maintainer at the time decided to wait for Swift to implement this. Five years later, and several major version of Swift later, this is still not available. I decided to move on and implement a less optimal solution. This can be replaced in the future if package version ever becomes part of Package.swift. ### Modifications: Add a version enum to isolate the versioning in one place. I decided to keep it simple and not over engineering it with major, minor, patch and pre-release. At the time, it's a simple string. This is all what we need for usage in the user agent string. ### Result: User agent now identifies as `Swift-Lambda/2,0` instead of `Swift-Lambda/unknown`
132 lines
5.1 KiB
Swift
132 lines
5.1 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIOCore
|
|
|
|
struct ControlPlaneRequestEncoder: _EmittingChannelHandler {
|
|
typealias OutboundOut = ByteBuffer
|
|
|
|
private var host: String
|
|
private var byteBuffer: ByteBuffer!
|
|
|
|
init(host: String) {
|
|
self.host = host
|
|
}
|
|
|
|
mutating func writeRequest(
|
|
_ request: ControlPlaneRequest,
|
|
context: ChannelHandlerContext,
|
|
promise: EventLoopPromise<Void>?
|
|
) {
|
|
self.byteBuffer.clear(minimumCapacity: self.byteBuffer.storageCapacity)
|
|
|
|
switch request {
|
|
case .next:
|
|
self.byteBuffer.writeString(.nextInvocationRequestLine)
|
|
self.byteBuffer.writeHostHeader(host: self.host)
|
|
self.byteBuffer.writeString(.userAgentHeader)
|
|
self.byteBuffer.writeString(.CRLF) // end of head
|
|
context.write(self.wrapOutboundOut(self.byteBuffer), promise: promise)
|
|
context.flush()
|
|
|
|
case .invocationResponse(let requestID, let payload):
|
|
let contentLength = payload?.readableBytes ?? 0
|
|
self.byteBuffer.writeInvocationResultRequestLine(requestID)
|
|
self.byteBuffer.writeHostHeader(host: self.host)
|
|
self.byteBuffer.writeString(.userAgentHeader)
|
|
self.byteBuffer.writeContentLengthHeader(length: contentLength)
|
|
self.byteBuffer.writeString(.CRLF) // end of head
|
|
if let payload = payload, contentLength > 0 {
|
|
context.write(self.wrapOutboundOut(self.byteBuffer), promise: nil)
|
|
context.write(self.wrapOutboundOut(payload), promise: promise)
|
|
} else {
|
|
context.write(self.wrapOutboundOut(self.byteBuffer), promise: promise)
|
|
}
|
|
context.flush()
|
|
|
|
case .invocationError(let requestID, let errorMessage):
|
|
let payload = errorMessage.toJSONBytes()
|
|
self.byteBuffer.writeInvocationErrorRequestLine(requestID)
|
|
self.byteBuffer.writeContentLengthHeader(length: payload.count)
|
|
self.byteBuffer.writeHostHeader(host: self.host)
|
|
self.byteBuffer.writeString(.userAgentHeader)
|
|
self.byteBuffer.writeString(.unhandledErrorHeader)
|
|
self.byteBuffer.writeString(.CRLF) // end of head
|
|
self.byteBuffer.writeBytes(payload)
|
|
context.write(self.wrapOutboundOut(self.byteBuffer), promise: promise)
|
|
context.flush()
|
|
|
|
case .initializationError(let errorMessage):
|
|
let payload = errorMessage.toJSONBytes()
|
|
self.byteBuffer.writeString(.runtimeInitErrorRequestLine)
|
|
self.byteBuffer.writeContentLengthHeader(length: payload.count)
|
|
self.byteBuffer.writeHostHeader(host: self.host)
|
|
self.byteBuffer.writeString(.userAgentHeader)
|
|
self.byteBuffer.writeString(.unhandledErrorHeader)
|
|
self.byteBuffer.writeString(.CRLF) // end of head
|
|
self.byteBuffer.writeBytes(payload)
|
|
context.write(self.wrapOutboundOut(self.byteBuffer), promise: promise)
|
|
context.flush()
|
|
}
|
|
}
|
|
|
|
mutating func writerAdded(context: ChannelHandlerContext) {
|
|
self.byteBuffer = context.channel.allocator.buffer(capacity: 256)
|
|
}
|
|
|
|
mutating func writerRemoved(context: ChannelHandlerContext) {
|
|
self.byteBuffer = nil
|
|
}
|
|
}
|
|
|
|
extension String {
|
|
static let CRLF: String = "\r\n"
|
|
|
|
static let userAgent = "Swift-Lambda/\(Version.current)"
|
|
static let userAgentHeader: String = "user-agent: \(userAgent)\r\n"
|
|
static let unhandledErrorHeader: String = "lambda-runtime-function-error-type: Unhandled\r\n"
|
|
|
|
static let nextInvocationRequestLine: String =
|
|
"GET /2018-06-01/runtime/invocation/next HTTP/1.1\r\n"
|
|
|
|
static let runtimeInitErrorRequestLine: String =
|
|
"POST /2018-06-01/runtime/init/error HTTP/1.1\r\n"
|
|
}
|
|
|
|
extension ByteBuffer {
|
|
fileprivate mutating func writeInvocationResultRequestLine(_ requestID: String) {
|
|
self.writeString("POST /2018-06-01/runtime/invocation/")
|
|
self.writeString(requestID)
|
|
self.writeString("/response HTTP/1.1\r\n")
|
|
}
|
|
|
|
fileprivate mutating func writeInvocationErrorRequestLine(_ requestID: String) {
|
|
self.writeString("POST /2018-06-01/runtime/invocation/")
|
|
self.writeString(requestID)
|
|
self.writeString("/error HTTP/1.1\r\n")
|
|
}
|
|
|
|
fileprivate mutating func writeHostHeader(host: String) {
|
|
self.writeString("host: ")
|
|
self.writeString(host)
|
|
self.writeString(.CRLF)
|
|
}
|
|
|
|
fileprivate mutating func writeContentLengthHeader(length: Int) {
|
|
self.writeString("content-length: ")
|
|
self.writeString("\(length)")
|
|
self.writeString(.CRLF)
|
|
}
|
|
}
|