Skip to content

Commit 99cfae8

Browse files
committed
More tests
1 parent 2828a22 commit 99cfae8

File tree

3 files changed

+258
-32
lines changed

3 files changed

+258
-32
lines changed

Sources/AWSLambdaRuntimeCore/ControlPlaneResponseDecoder.swift

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ struct ControlPlaneResponseDecoder: NIOSingleStepByteToMessageDecoder {
269269
buffer.moveReaderIndex(forwardBy: 1) // move forward for colon
270270
try self.decodeOptionalWhiteSpaceBeforeFieldValue(from: &buffer)
271271
guard let length = buffer.readIntegerFromHeader() else {
272-
throw LambdaRuntimeError.responseHeadInvalidDeadlineValue
272+
throw LambdaRuntimeError.responseHeadInvalidContentLengthValue
273273
}
274274
return .contentLength(length)
275275
}
@@ -334,13 +334,13 @@ struct ControlPlaneResponseDecoder: NIOSingleStepByteToMessageDecoder {
334334
// Ensure we received a valid http header:
335335
break // fallthrough
336336
}
337-
337+
338338
// We received a header we didn't expect, let's ensure it is valid.
339-
let satisfy = buffer.readableBytesView[0..<colonIndex].allSatisfy { char -> Bool in
339+
let satisfy = buffer.readableBytesView[0 ..< colonIndex].allSatisfy { char -> Bool in
340340
switch char {
341-
case UInt8(ascii: "a")...UInt8(ascii: "z"),
342-
UInt8(ascii: "A")...UInt8(ascii: "Z"),
343-
UInt8(ascii: "0")...UInt8(ascii: "9"),
341+
case UInt8(ascii: "a") ... UInt8(ascii: "z"),
342+
UInt8(ascii: "A") ... UInt8(ascii: "Z"),
343+
UInt8(ascii: "0") ... UInt8(ascii: "9"),
344344
UInt8(ascii: "!"),
345345
UInt8(ascii: "#"),
346346
UInt8(ascii: "$"),
@@ -361,7 +361,7 @@ struct ControlPlaneResponseDecoder: NIOSingleStepByteToMessageDecoder {
361361
return false
362362
}
363363
}
364-
364+
365365
guard satisfy else {
366366
throw LambdaRuntimeError.responseHeadHeaderInvalidCharacter
367367
}
@@ -493,22 +493,6 @@ extension ByteBuffer {
493493

494494
return value
495495
}
496-
497-
// mutating func validateHeaderValue(_ value: String) -> Bool {
498-
// func isNotOptionalWhiteSpace(_ val: UInt8) -> Bool {
499-
// val != UInt8(ascii: " ") && val != UInt8(ascii: "\t")
500-
// }
501-
//
502-
// guard let firstCharacterIndex = self.readableBytesView.firstIndex(where: isNotOptionalWhiteSpace),
503-
// let lastCharacterIndex = self.readableBytesView.lastIndex(where: isNotOptionalWhiteSpace)
504-
// else {
505-
// return false
506-
// }
507-
//
508-
// self.com
509-
// }
510-
511-
mutating func readOptionalWhiteSpace() {}
512496
}
513497

514498
extension Invocation {

Sources/AWSLambdaRuntimeCore/LambdaRuntimeError.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ struct LambdaRuntimeError: Error, Hashable {
5757
static var responseHeadInvalidRequestIDValue = LambdaRuntimeError(.responseHeadInvalidRequestIDValue)
5858
static var responseHeadInvalidTraceIDValue = LambdaRuntimeError(.responseHeadInvalidTraceIDValue)
5959
static var responseHeadInvalidDeadlineValue = LambdaRuntimeError(.responseHeadInvalidDeadlineValue)
60+
6061
static var invocationHeadMissingRequestID = LambdaRuntimeError(.invocationHeadMissingRequestID)
6162
static var invocationHeadMissingDeadlineInMillisSinceEpoch = LambdaRuntimeError(.invocationHeadMissingDeadlineInMillisSinceEpoch)
6263
static var invocationHeadMissingFunctionARN = LambdaRuntimeError(.invocationHeadMissingFunctionARN)

Tests/AWSLambdaRuntimeCoreTests/ControlPlaneResponseDecoderTests.swift

Lines changed: 250 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final class ControlPlaneResponseDecoderTests: XCTestCase {
2424
Content-Type: application/json\r\n\
2525
Lambda-Runtime-Aws-Request-Id: 9028dc49-a01b-4b44-8ffe-4912e9dabbbd\r\n\
2626
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
27-
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:079477498937:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
27+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
2828
Lambda-Runtime-Trace-Id: Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\r\n\
2929
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
3030
Content-Length: 49\r\n\
@@ -35,7 +35,7 @@ final class ControlPlaneResponseDecoderTests: XCTestCase {
3535
let invocation = Invocation(
3636
requestID: "9028dc49-a01b-4b44-8ffe-4912e9dabbbd",
3737
deadlineInMillisSinceEpoch: 1_638_392_696_671,
38-
invokedFunctionARN: "arn:aws:lambda:eu-central-1:079477498937:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x",
38+
invokedFunctionARN: "arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x",
3939
traceID: "Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0",
4040
clientContext: nil,
4141
cognitoIdentity: nil
@@ -63,14 +63,14 @@ final class ControlPlaneResponseDecoderTests: XCTestCase {
6363
decoderFactory: { ControlPlaneResponseDecoder() }
6464
))
6565
}
66-
66+
6767
func testWhitespaceInHeaderIsRejected() {
6868
let nextResponse = ByteBuffer(string: """
6969
HTTP/1.1 200 OK\r\n\
7070
Content-Type: application/json\r\n\
7171
Lambda-Runtime Aws-Request-Id: 9028dc49-a01b-4b44-8ffe-4912e9dabbbd\r\n\
7272
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
73-
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:079477498937:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
73+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
7474
Lambda-Runtime-Trace-Id: Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\r\n\
7575
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
7676
Content-Length: 49\r\n\
@@ -79,17 +79,258 @@ final class ControlPlaneResponseDecoderTests: XCTestCase {
7979
"""
8080
)
8181

82-
let pairs: [(ByteBuffer, [ControlPlaneResponse])] = [
83-
(nextResponse, [])
84-
]
85-
8682
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
87-
inputOutputPairs: pairs,
83+
inputOutputPairs: [(nextResponse, [])],
8884
decoderFactory: { ControlPlaneResponseDecoder() }
8985
)) {
9086
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadHeaderInvalidCharacter)
9187
}
9288
}
89+
90+
func testVeryLongHTTPStatusLine() {
91+
let nextResponse = ByteBuffer(repeating: UInt8(ascii: "H"), count: 1024)
92+
93+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
94+
inputOutputPairs: [(nextResponse, [])],
95+
decoderFactory: { ControlPlaneResponseDecoder() }
96+
)) {
97+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadMoreThan256BytesBeforeCRLF)
98+
}
99+
}
100+
101+
func testVeryLongHTTPHeader() {
102+
let acceptedResponse = ByteBuffer(string: """
103+
HTTP/1.1 202 Accepted\r\n\
104+
Content-Type: application/json\r\n\
105+
Date: Sun, 05 Dec 2021 11:53:40 GMT\r\n\
106+
Content-Length: 16\r\n
107+
"""
108+
) + ByteBuffer(repeating: UInt8(ascii: "H"), count: 1024)
109+
110+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
111+
inputOutputPairs: [(acceptedResponse, [])],
112+
decoderFactory: { ControlPlaneResponseDecoder() }
113+
)) {
114+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadMoreThan256BytesBeforeCRLF)
115+
}
116+
}
117+
118+
func testNextResponseWithoutTraceID() {
119+
let nextResponse = ByteBuffer(string: """
120+
HTTP/1.1 200 OK\r\n\
121+
Content-Type: application/json\r\n\
122+
Lambda-Runtime-Aws-Request-Id: 9028dc49-a01b-4b44-8ffe-4912e9dabbbd\r\n\
123+
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
124+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
125+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
126+
Content-Length: 49\r\n\
127+
\r\n\
128+
{"name":"Fabian","key2":"value2","key3":"value3"}
129+
"""
130+
)
131+
132+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
133+
inputOutputPairs: [(nextResponse, [])],
134+
decoderFactory: { ControlPlaneResponseDecoder() }
135+
)) {
136+
XCTAssertEqual($0 as? LambdaRuntimeError, .invocationHeadMissingTraceID)
137+
}
138+
}
139+
140+
func testNextResponseWithoutRequestID() {
141+
let nextResponse = ByteBuffer(string: """
142+
HTTP/1.1 200 OK\r\n\
143+
Content-Type: application/json\r\n\
144+
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
145+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
146+
Lambda-Runtime-Trace-Id: Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\r\n\
147+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
148+
Content-Length: 49\r\n\
149+
\r\n\
150+
{"name":"Fabian","key2":"value2","key3":"value3"}
151+
"""
152+
)
153+
154+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
155+
inputOutputPairs: [(nextResponse, [])],
156+
decoderFactory: { ControlPlaneResponseDecoder() }
157+
)) {
158+
XCTAssertEqual($0 as? LambdaRuntimeError, .invocationHeadMissingRequestID)
159+
}
160+
}
161+
162+
func testNextResponseWithInvalidStatusCode() {
163+
let nextResponse = ByteBuffer(string: """
164+
HTTP/1.1 20 OK\r\n\
165+
Content-Type: application/json\r\n\
166+
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
167+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
168+
Lambda-Runtime-Trace-Id: Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\r\n\
169+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
170+
Content-Length: 49\r\n\
171+
\r\n\
172+
{"name":"Fabian","key2":"value2","key3":"value3"}
173+
"""
174+
)
175+
176+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
177+
inputOutputPairs: [(nextResponse, [])],
178+
decoderFactory: { ControlPlaneResponseDecoder() }
179+
)) {
180+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidStatusLine)
181+
}
182+
}
183+
184+
func testNextResponseWithVersionHTTP2() {
185+
let nextResponse = ByteBuffer(string: """
186+
HTTP/2.0 200 OK\r\n\
187+
Content-Type: application/json\r\n\
188+
Lambda-Runtime-Deadline-Ms: 1638392696671\r\n\
189+
Lambda-Runtime-Invoked-Function-Arn: arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\r\n\
190+
Lambda-Runtime-Trace-Id: Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\r\n\
191+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
192+
Content-Length: 49\r\n\
193+
\r\n\
194+
{"name":"Fabian","key2":"value2","key3":"value3"}
195+
"""
196+
)
197+
198+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
199+
inputOutputPairs: [(nextResponse, [])],
200+
decoderFactory: { ControlPlaneResponseDecoder() }
201+
)) {
202+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidStatusLine)
203+
}
204+
}
205+
206+
func testNextResponseLeadingAndTrailingWhitespaceHeaders() {
207+
let nextResponse = ByteBuffer(string: """
208+
HTTP/1.1 200 OK\r\n\
209+
Content-Type: \t \t application/json\t \t \r\n\
210+
Lambda-Runtime-Aws-Request-Id: \t \t 9028dc49-a01b-4b44-8ffe-4912e9dabbbd\t \t \r\n\
211+
Lambda-Runtime-Deadline-Ms: \t \t 1638392696671\t \t \r\n\
212+
Lambda-Runtime-Invoked-Function-Arn: \t \t arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x\t \t \r\n\
213+
Lambda-Runtime-Trace-Id: \t \t Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0\t \t \r\n\
214+
Date: \t \t Wed, 01 Dec 2021 21:04:53 GMT\t \t \r\n\
215+
Content-Length: \t \t 49\t \t \r\n\
216+
\r\n\
217+
{"name":"Fabian","key2":"value2","key3":"value3"}
218+
"""
219+
)
220+
let invocation = Invocation(
221+
requestID: "9028dc49-a01b-4b44-8ffe-4912e9dabbbd",
222+
deadlineInMillisSinceEpoch: 1_638_392_696_671,
223+
invokedFunctionARN: "arn:aws:lambda:eu-central-1:000000000000:function:lambda-log-http-HelloWorldLambda-NiDlzMFXtF3x",
224+
traceID: "Root=1-61a7e375-40b3edf95b388fe75d1fa416;Parent=348bb48e251c1254;Sampled=0",
225+
clientContext: nil,
226+
cognitoIdentity: nil
227+
)
228+
let next: ControlPlaneResponse = .next(invocation, ByteBuffer(string: #"{"name":"Fabian","key2":"value2","key3":"value3"}"#))
229+
230+
XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(
231+
inputOutputPairs: [(nextResponse, [next])],
232+
decoderFactory: { ControlPlaneResponseDecoder() }
233+
))
234+
}
235+
236+
func testContentLengthHasTrailingCharacterSurroundedByWhitespace() {
237+
let nextResponse = ByteBuffer(string: """
238+
HTTP/1.1 200 OK\r\n\
239+
Content-Type: \t \t application/json\t \t \r\n\
240+
Content-Length: 49 r \r\n\
241+
Date: \t \t Wed, 01 Dec 2021 21:04:53 GMT\t \t \r\n\
242+
243+
\r\n\
244+
{"name":"Fabian","key2":"value2","key3":"value3"}
245+
"""
246+
)
247+
248+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
249+
inputOutputPairs: [(nextResponse, [])],
250+
decoderFactory: { ControlPlaneResponseDecoder() }
251+
)) {
252+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidContentLengthValue)
253+
}
254+
}
255+
256+
func testInvalidContentLength() {
257+
let nextResponse = ByteBuffer(string: """
258+
HTTP/1.1 200 OK\r\n\
259+
Content-Type: \t \t application/json\t \t \r\n\
260+
Content-Length: 4u9 \r\n\
261+
Date: \t \t Wed, 01 Dec 2021 21:04:53 GMT\t \t \r\n\
262+
263+
\r\n\
264+
{"name":"Fabian","key2":"value2","key3":"value3"}
265+
"""
266+
)
267+
268+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
269+
inputOutputPairs: [(nextResponse, [])],
270+
decoderFactory: { ControlPlaneResponseDecoder() }
271+
)) {
272+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidContentLengthValue)
273+
}
274+
}
275+
276+
func testResponseHeaderWithoutColon() {
277+
let nextResponse = ByteBuffer(string: """
278+
HTTP/1.1 200 OK\r\n\
279+
Content-Type application/json\r\n\
280+
Content-Length: 49\r\n\
281+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
282+
283+
\r\n\
284+
{"name":"Fabian","key2":"value2","key3":"value3"}
285+
"""
286+
)
287+
288+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
289+
inputOutputPairs: [(nextResponse, [])],
290+
decoderFactory: { ControlPlaneResponseDecoder() }
291+
)) {
292+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadHeaderMissingColon)
293+
}
294+
}
295+
296+
func testResponseHeaderWithDoubleCR() {
297+
let nextResponse = ByteBuffer(string: """
298+
HTTP/1.1 200 OK\r\n\
299+
Content-Type: application/json\r\r\n\
300+
Content-Length: 49\r\n\
301+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
302+
303+
\r\n\
304+
{"name":"Fabian","key2":"value2","key3":"value3"}
305+
"""
306+
)
307+
308+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
309+
inputOutputPairs: [(nextResponse, [])],
310+
decoderFactory: { ControlPlaneResponseDecoder() }
311+
)) {
312+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidHeader)
313+
}
314+
}
315+
316+
func testResponseHeaderWithoutValue() {
317+
let nextResponse = ByteBuffer(string: """
318+
HTTP/1.1 200 OK\r\n\
319+
Content-Type: \r\n\
320+
Content-Length: 49\r\n\
321+
Date: Wed, 01 Dec 2021 21:04:53 GMT\r\n\
322+
\r\n\
323+
{"name":"Fabian","key2":"value2","key3":"value3"}
324+
"""
325+
)
326+
327+
XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
328+
inputOutputPairs: [(nextResponse, [])],
329+
decoderFactory: { ControlPlaneResponseDecoder() }
330+
)) {
331+
XCTAssertEqual($0 as? LambdaRuntimeError, .responseHeadInvalidHeader)
332+
}
333+
}
93334
}
94335

95336
extension ByteBuffer {

0 commit comments

Comments
 (0)