Skip to content

Commit f015dbf

Browse files
authored
Use Swift HTTP types package for status and method (#47)
* Use Swift HTTP types package for status and method * Fix formatting
1 parent 5b4633d commit f015dbf

14 files changed

+57
-188
lines changed

Package.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ let package = Package(
99
],
1010
dependencies: [
1111
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
12+
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.0.0"),
1213
],
1314
targets: [
14-
.target(name: "AWSLambdaEvents", dependencies: []),
15+
.target(name: "AWSLambdaEvents", dependencies: [.product(name: "HTTPTypes", package: "swift-http-types")]),
1516
.testTarget(name: "AWSLambdaEventsTests", dependencies: ["AWSLambdaEvents"]),
1617
]
1718
)

Sources/AWSLambdaEvents/ALB.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
import class Foundation.JSONEncoder
1618

1719
// https://github.com/aws/aws-lambda-go/blob/master/events/alb.go
@@ -22,7 +24,7 @@ public struct ALBTargetGroupRequest: Codable {
2224
public let elb: ELBContext
2325
}
2426

25-
public let httpMethod: HTTPMethod
27+
public let httpMethod: HTTPRequest.Method
2628
public let path: String
2729
public let queryStringParameters: [String: String]
2830

@@ -50,15 +52,15 @@ public struct ALBTargetGroupRequest: Codable {
5052
}
5153

5254
public struct ALBTargetGroupResponse: Codable {
53-
public var statusCode: HTTPResponseStatus
55+
public var statusCode: HTTPResponse.Status
5456
public var statusDescription: String?
5557
public var headers: HTTPHeaders?
5658
public var multiValueHeaders: HTTPMultiValueHeaders?
5759
public var body: String
5860
public var isBase64Encoded: Bool
5961

6062
public init(
61-
statusCode: HTTPResponseStatus,
63+
statusCode: HTTPResponse.Status,
6264
statusDescription: String? = nil,
6365
headers: HTTPHeaders? = nil,
6466
multiValueHeaders: HTTPMultiValueHeaders? = nil,

Sources/AWSLambdaEvents/APIGateway+V2.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
/// `APIGatewayV2Request` contains data coming from the new HTTP API Gateway.
1618
public struct APIGatewayV2Request: Codable {
1719
/// `Context` contains information to identify the AWS account and resources invoking the Lambda function.
1820
public struct Context: Codable {
1921
public struct HTTP: Codable {
20-
public let method: HTTPMethod
22+
public let method: HTTPRequest.Method
2123
public let path: String
2224
public let `protocol`: String
2325
public let sourceIp: String
@@ -125,14 +127,14 @@ public struct APIGatewayV2Request: Codable {
125127
}
126128

127129
public struct APIGatewayV2Response: Codable {
128-
public var statusCode: HTTPResponseStatus
130+
public var statusCode: HTTPResponse.Status
129131
public var headers: HTTPHeaders?
130132
public var body: String?
131133
public var isBase64Encoded: Bool?
132134
public var cookies: [String]?
133135

134136
public init(
135-
statusCode: HTTPResponseStatus,
137+
statusCode: HTTPResponse.Status,
136138
headers: HTTPHeaders? = nil,
137139
body: String? = nil,
138140
isBase64Encoded: Bool? = nil,

Sources/AWSLambdaEvents/APIGateway.swift

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
import class Foundation.JSONEncoder
1618

1719
// https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html
@@ -56,7 +58,7 @@ public struct APIGatewayRequest: Codable {
5658

5759
public let resource: String
5860
public let path: String
59-
public let httpMethod: HTTPMethod
61+
public let httpMethod: HTTPRequest.Method
6062

6163
public let queryStringParameters: [String: String]?
6264
public let multiValueQueryStringParameters: [String: [String]]?
@@ -73,14 +75,14 @@ public struct APIGatewayRequest: Codable {
7375
// MARK: - Response -
7476

7577
public struct APIGatewayResponse: Codable {
76-
public var statusCode: HTTPResponseStatus
78+
public var statusCode: HTTPResponse.Status
7779
public var headers: HTTPHeaders?
7880
public var multiValueHeaders: HTTPMultiValueHeaders?
7981
public var body: String?
8082
public var isBase64Encoded: Bool?
8183

8284
public init(
83-
statusCode: HTTPResponseStatus,
85+
statusCode: HTTPResponse.Status,
8486
headers: HTTPHeaders? = nil,
8587
multiValueHeaders: HTTPMultiValueHeaders? = nil,
8688
body: String? = nil,

Sources/AWSLambdaEvents/APIGatewayLambdaAuthorizers.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
/// `LambdaAuthorizerContext` contains authorizer informations passed to a Lambda function authorizer
1618
public typealias LambdaAuthorizerContext = [String: String]
1719

@@ -29,7 +31,7 @@ public struct APIGatewayLambdaAuthorizerRequest: Codable {
2931
/// `Context` contains information to identify the AWS account and resources invoking the Lambda function.
3032
public struct Context: Codable {
3133
public struct HTTP: Codable {
32-
public let method: HTTPMethod
34+
public let method: HTTPRequest.Method
3335
public let path: String
3436
public let `protocol`: String
3537
public let sourceIp: String

Sources/AWSLambdaEvents/AppSync.swift

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
// https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html
1618
public struct AppSyncEvent: Decodable {
1719
public let arguments: [String: ArgumentValue]

Sources/AWSLambdaEvents/FunctionURL.swift

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// SPDX-License-Identifier: Apache-2.0
1212
//
1313
//===----------------------------------------------------------------------===//
14+
import HTTPTypes
1415

1516
import class Foundation.JSONEncoder
1617

@@ -37,7 +38,7 @@ public struct FunctionURLRequest: Codable {
3738
}
3839

3940
public struct HTTP: Codable {
40-
public let method: HTTPMethod
41+
public let method: HTTPRequest.Method
4142
public let path: String
4243
public let `protocol`: String
4344
public let sourceIp: String
@@ -81,14 +82,14 @@ public struct FunctionURLRequest: Codable {
8182
// MARK: - Response -
8283

8384
public struct FunctionURLResponse: Codable {
84-
public var statusCode: HTTPResponseStatus
85+
public var statusCode: HTTPResponse.Status
8586
public var headers: HTTPHeaders?
8687
public var body: String?
8788
public let cookies: [String]?
8889
public var isBase64Encoded: Bool?
8990

9091
public init(
91-
statusCode: HTTPResponseStatus,
92+
statusCode: HTTPResponse.Status,
9293
headers: HTTPHeaders? = nil,
9394
body: String? = nil,
9495
cookies: [String]? = nil,

Sources/AWSLambdaEvents/LambdaGatewayProxyEvent.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import HTTPTypes
16+
1517
/// LambdaGatewayProxyEvent contains data coming from the new HTTP API Gateway Proxy
1618
public struct LambdaGatewayProxyEvent: Decodable {
1719
public struct RequestContext: Decodable {
@@ -28,7 +30,7 @@ public struct LambdaGatewayProxyEvent: Decodable {
2830
public let stage: String
2931
public let requestID: String
3032

31-
public let httpMethod: HTTPMethod
33+
public let httpMethod: HTTPRequest.Method
3234
public let authorizer: Authorizer?
3335

3436
public let resourcePath: String?

Sources/AWSLambdaEvents/Utils/HTTP.swift

+18-163
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
// MARK: HTTPHeaders
1616

17+
import HTTPTypes
18+
1719
public typealias HTTPHeaders = [String: String]
1820
public typealias HTTPMultiValueHeaders = [String: [String]]
1921

@@ -72,178 +74,31 @@ extension String.UTF8View {
7274
}
7375
}
7476

75-
// MARK: HTTPMethod
76-
77-
public struct HTTPMethod: RawRepresentable, Equatable {
78-
public var rawValue: String
79-
80-
public init?(rawValue: String) {
81-
guard rawValue.isValidHTTPToken else {
82-
return nil
83-
}
84-
self.rawValue = rawValue
85-
}
86-
87-
public static var GET: HTTPMethod { HTTPMethod(rawValue: "GET")! }
88-
public static var POST: HTTPMethod { HTTPMethod(rawValue: "POST")! }
89-
public static var PUT: HTTPMethod { HTTPMethod(rawValue: "PUT")! }
90-
public static var PATCH: HTTPMethod { HTTPMethod(rawValue: "PATCH")! }
91-
public static var DELETE: HTTPMethod { HTTPMethod(rawValue: "DELETE")! }
92-
public static var OPTIONS: HTTPMethod { HTTPMethod(rawValue: "OPTIONS")! }
93-
public static var HEAD: HTTPMethod { HTTPMethod(rawValue: "HEAD")! }
94-
95-
public static func RAW(value: String) -> HTTPMethod? { HTTPMethod(rawValue: value) }
96-
}
97-
98-
extension HTTPMethod: Codable {
99-
public init(from decoder: Decoder) throws {
100-
let container = try decoder.singleValueContainer()
101-
let rawMethod = try container.decode(String.self)
102-
103-
guard let method = HTTPMethod(rawValue: rawMethod) else {
104-
throw DecodingError.dataCorruptedError(
105-
in: container,
106-
debugDescription: #"Method "\#(rawMethod)" does not conform to allowed http method syntax defined in RFC 7230 Section 3.2.6"#
107-
)
108-
}
109-
110-
self = method
111-
}
112-
113-
public func encode(to encoder: Encoder) throws {
77+
extension HTTPResponse.Status: Codable {
78+
public func encode(to encoder: any Encoder) throws {
11479
var container = encoder.singleValueContainer()
115-
try container.encode(self.rawValue)
116-
}
117-
}
118-
119-
// MARK: HTTPResponseStatus
120-
121-
public struct HTTPResponseStatus {
122-
public let code: UInt
123-
public let reasonPhrase: String?
124-
125-
public init(code: UInt, reasonPhrase: String? = nil) {
126-
self.code = code
127-
self.reasonPhrase = reasonPhrase
80+
try container.encode(self.code)
12881
}
12982

130-
public static var `continue`: HTTPResponseStatus { HTTPResponseStatus(code: 100) }
131-
public static var switchingProtocols: HTTPResponseStatus { HTTPResponseStatus(code: 101) }
132-
public static var processing: HTTPResponseStatus { HTTPResponseStatus(code: 102) }
133-
public static var earlyHints: HTTPResponseStatus { HTTPResponseStatus(code: 103) }
134-
135-
public static var ok: HTTPResponseStatus { HTTPResponseStatus(code: 200) }
136-
public static var created: HTTPResponseStatus { HTTPResponseStatus(code: 201) }
137-
public static var accepted: HTTPResponseStatus { HTTPResponseStatus(code: 202) }
138-
public static var nonAuthoritativeInformation: HTTPResponseStatus { HTTPResponseStatus(code: 203) }
139-
public static var noContent: HTTPResponseStatus { HTTPResponseStatus(code: 204) }
140-
public static var resetContent: HTTPResponseStatus { HTTPResponseStatus(code: 205) }
141-
public static var partialContent: HTTPResponseStatus { HTTPResponseStatus(code: 206) }
142-
public static var multiStatus: HTTPResponseStatus { HTTPResponseStatus(code: 207) }
143-
public static var alreadyReported: HTTPResponseStatus { HTTPResponseStatus(code: 208) }
144-
public static var imUsed: HTTPResponseStatus { HTTPResponseStatus(code: 226) }
145-
146-
public static var multipleChoices: HTTPResponseStatus { HTTPResponseStatus(code: 300) }
147-
public static var movedPermanently: HTTPResponseStatus { HTTPResponseStatus(code: 301) }
148-
public static var found: HTTPResponseStatus { HTTPResponseStatus(code: 302) }
149-
public static var seeOther: HTTPResponseStatus { HTTPResponseStatus(code: 303) }
150-
public static var notModified: HTTPResponseStatus { HTTPResponseStatus(code: 304) }
151-
public static var useProxy: HTTPResponseStatus { HTTPResponseStatus(code: 305) }
152-
public static var temporaryRedirect: HTTPResponseStatus { HTTPResponseStatus(code: 307) }
153-
public static var permanentRedirect: HTTPResponseStatus { HTTPResponseStatus(code: 308) }
154-
155-
public static var badRequest: HTTPResponseStatus { HTTPResponseStatus(code: 400) }
156-
public static var unauthorized: HTTPResponseStatus { HTTPResponseStatus(code: 401) }
157-
public static var paymentRequired: HTTPResponseStatus { HTTPResponseStatus(code: 402) }
158-
public static var forbidden: HTTPResponseStatus { HTTPResponseStatus(code: 403) }
159-
public static var notFound: HTTPResponseStatus { HTTPResponseStatus(code: 404) }
160-
public static var methodNotAllowed: HTTPResponseStatus { HTTPResponseStatus(code: 405) }
161-
public static var notAcceptable: HTTPResponseStatus { HTTPResponseStatus(code: 406) }
162-
public static var proxyAuthenticationRequired: HTTPResponseStatus { HTTPResponseStatus(code: 407) }
163-
public static var requestTimeout: HTTPResponseStatus { HTTPResponseStatus(code: 408) }
164-
public static var conflict: HTTPResponseStatus { HTTPResponseStatus(code: 409) }
165-
public static var gone: HTTPResponseStatus { HTTPResponseStatus(code: 410) }
166-
public static var lengthRequired: HTTPResponseStatus { HTTPResponseStatus(code: 411) }
167-
public static var preconditionFailed: HTTPResponseStatus { HTTPResponseStatus(code: 412) }
168-
public static var payloadTooLarge: HTTPResponseStatus { HTTPResponseStatus(code: 413) }
169-
public static var uriTooLong: HTTPResponseStatus { HTTPResponseStatus(code: 414) }
170-
public static var unsupportedMediaType: HTTPResponseStatus { HTTPResponseStatus(code: 415) }
171-
public static var rangeNotSatisfiable: HTTPResponseStatus { HTTPResponseStatus(code: 416) }
172-
public static var expectationFailed: HTTPResponseStatus { HTTPResponseStatus(code: 417) }
173-
public static var imATeapot: HTTPResponseStatus { HTTPResponseStatus(code: 418) }
174-
public static var misdirectedRequest: HTTPResponseStatus { HTTPResponseStatus(code: 421) }
175-
public static var unprocessableEntity: HTTPResponseStatus { HTTPResponseStatus(code: 422) }
176-
public static var locked: HTTPResponseStatus { HTTPResponseStatus(code: 423) }
177-
public static var failedDependency: HTTPResponseStatus { HTTPResponseStatus(code: 424) }
178-
public static var upgradeRequired: HTTPResponseStatus { HTTPResponseStatus(code: 426) }
179-
public static var preconditionRequired: HTTPResponseStatus { HTTPResponseStatus(code: 428) }
180-
public static var tooManyRequests: HTTPResponseStatus { HTTPResponseStatus(code: 429) }
181-
public static var requestHeaderFieldsTooLarge: HTTPResponseStatus { HTTPResponseStatus(code: 431) }
182-
public static var unavailableForLegalReasons: HTTPResponseStatus { HTTPResponseStatus(code: 451) }
183-
184-
public static var internalServerError: HTTPResponseStatus { HTTPResponseStatus(code: 500) }
185-
public static var notImplemented: HTTPResponseStatus { HTTPResponseStatus(code: 501) }
186-
public static var badGateway: HTTPResponseStatus { HTTPResponseStatus(code: 502) }
187-
public static var serviceUnavailable: HTTPResponseStatus { HTTPResponseStatus(code: 503) }
188-
public static var gatewayTimeout: HTTPResponseStatus { HTTPResponseStatus(code: 504) }
189-
public static var httpVersionNotSupported: HTTPResponseStatus { HTTPResponseStatus(code: 505) }
190-
public static var variantAlsoNegotiates: HTTPResponseStatus { HTTPResponseStatus(code: 506) }
191-
public static var insufficientStorage: HTTPResponseStatus { HTTPResponseStatus(code: 507) }
192-
public static var loopDetected: HTTPResponseStatus { HTTPResponseStatus(code: 508) }
193-
public static var notExtended: HTTPResponseStatus { HTTPResponseStatus(code: 510) }
194-
public static var networkAuthenticationRequired: HTTPResponseStatus { HTTPResponseStatus(code: 511) }
195-
}
196-
197-
extension HTTPResponseStatus: Equatable {
198-
public static func == (lhs: Self, rhs: Self) -> Bool {
199-
lhs.code == rhs.code
83+
public init(from decoder: any Decoder) throws {
84+
let code = try decoder.singleValueContainer().decode(Int.self)
85+
self.init(code: code)
20086
}
20187
}
20288

203-
extension HTTPResponseStatus: Codable {
204-
public init(from decoder: Decoder) throws {
205-
let container = try decoder.singleValueContainer()
206-
self.code = try container.decode(UInt.self)
207-
self.reasonPhrase = nil
208-
}
209-
210-
public func encode(to encoder: Encoder) throws {
89+
extension HTTPRequest.Method: Codable {
90+
public func encode(to encoder: any Encoder) throws {
21191
var container = encoder.singleValueContainer()
212-
try container.encode(self.code)
92+
try container.encode(self.rawValue)
21393
}
214-
}
21594

216-
extension String {
217-
internal var isValidHTTPToken: Bool {
218-
self.utf8.allSatisfy { char -> Bool in
219-
switch char {
220-
case UInt8(ascii: "a") ... UInt8(ascii: "z"),
221-
UInt8(ascii: "A") ... UInt8(ascii: "Z"),
222-
UInt8(ascii: "0") ... UInt8(ascii: "9"),
223-
UInt8(ascii: "!"),
224-
UInt8(ascii: "#"),
225-
UInt8(ascii: "$"),
226-
UInt8(ascii: "%"),
227-
UInt8(ascii: "&"),
228-
UInt8(ascii: "'"),
229-
UInt8(ascii: "*"),
230-
UInt8(ascii: "+"),
231-
UInt8(ascii: "-"),
232-
UInt8(ascii: "."),
233-
UInt8(ascii: "^"),
234-
UInt8(ascii: "_"),
235-
UInt8(ascii: "`"),
236-
UInt8(ascii: "|"),
237-
UInt8(ascii: "~"):
238-
return true
239-
default:
240-
return false
241-
}
95+
public init(from decoder: any Decoder) throws {
96+
let container = try decoder.singleValueContainer()
97+
let rawMethod = try container.decode(String.self)
98+
guard let method = HTTPRequest.Method(rawMethod) else {
99+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "\"\(rawMethod)\" is not a valid method")
242100
}
101+
102+
self = method
243103
}
244104
}
245-
246-
#if swift(>=5.6)
247-
extension HTTPMethod: Sendable {}
248-
extension HTTPResponseStatus: Sendable {}
249-
#endif

0 commit comments

Comments
 (0)