Skip to content

Commit 0805daa

Browse files
committed
Adding tests
1 parent e7d216a commit 0805daa

File tree

4 files changed

+292
-2
lines changed

4 files changed

+292
-2
lines changed

‎Sources/AsyncHTTPClient/ConnectionPool/HTTP1.1/HTTP1ConnectionStateMachine.swift‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,12 @@ struct HTTP1ConnectionStateMachine{
200200
preconditionFailure("This event must only happen, if the connection is leased. During startup this is impossible")
201201

202202
case.idle:
203-
self.state =.closing
204-
return.close
203+
if closeConnection {
204+
self.state =.closing
205+
return.close
206+
}else{
207+
return.wait
208+
}
205209

206210
case.inRequest(var requestStateMachine, close:let close):
207211
returnself.avoidingStateMachineCoW{ state ->Actionin
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2018-2019 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
//
15+
// HTTP1ConnectionStateMachineTests+XCTest.swift
16+
//
17+
import XCTest
18+
19+
///
20+
/// NOTE: This file was generated by generate_linux_tests.rb
21+
///
22+
/// Do NOT edit this file directly as it will be regenerated automatically when needed.
23+
///
24+
25+
extensionHTTP1ConnectionStateMachineTests{
26+
staticvarallTests:[(String,(HTTP1ConnectionStateMachineTests)->()throws->Void)]{
27+
return[
28+
("testPOSTRequestWithWriteAndReadBackpressure", testPOSTRequestWithWriteAndReadBackpressure),
29+
("testResponseReadingWithBackpressure", testResponseReadingWithBackpressure),
30+
("testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest", testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest),
31+
("testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest", testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest),
32+
("testNIOTriggersChannelActiveTwice", testNIOTriggersChannelActiveTwice),
33+
("testIdleConnectionBecomesInactive", testIdleConnectionBecomesInactive),
34+
("testConnectionGoesAwayWhileInRequest", testConnectionGoesAwayWhileInRequest),
35+
("testRequestWasCancelledWhileUploadingData", testRequestWasCancelledWhileUploadingData),
36+
("testCancelRequestIsIgnoredWhenConnectionIsIdle", testCancelRequestIsIgnoredWhenConnectionIsIdle),
37+
("testReadsAreForwardedIfConnectionIsClosing", testReadsAreForwardedIfConnectionIsClosing),
38+
("testChannelReadsAreIgnoredIfConnectionIsClosing", testChannelReadsAreIgnoredIfConnectionIsClosing),
39+
("testRequestIsCancelledWhileWaitingForWritable", testRequestIsCancelledWhileWaitingForWritable),
40+
]
41+
}
42+
}
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
@testableimport AsyncHTTPClient
16+
import NIO
17+
import NIOHTTP1
18+
import XCTest
19+
20+
classHTTP1ConnectionStateMachineTests:XCTestCase{
21+
func testPOSTRequestWithWriteAndReadBackpressure(){
22+
varstate=HTTP1ConnectionStateMachine()
23+
XCTAssertEqual(state.channelActive(isWritable:false),.fireChannelActive)
24+
25+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.POST, uri:"/", headers:HTTPHeaders([("content-length","4")]))
26+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.fixedSize(4))
27+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.wait)
28+
XCTAssertEqual(state.writabilityChanged(writable:true),.sendRequestHead(requestHead, startBody:true))
29+
30+
letpart0=IOData.byteBuffer(ByteBuffer(bytes:[0]))
31+
letpart1=IOData.byteBuffer(ByteBuffer(bytes:[1]))
32+
letpart2=IOData.byteBuffer(ByteBuffer(bytes:[2]))
33+
letpart3=IOData.byteBuffer(ByteBuffer(bytes:[3]))
34+
XCTAssertEqual(state.requestStreamPartReceived(part0),.sendBodyPart(part0))
35+
XCTAssertEqual(state.requestStreamPartReceived(part1),.sendBodyPart(part1))
36+
37+
// oh the channel reports... we should slow down producing...
38+
XCTAssertEqual(state.writabilityChanged(writable:false),.pauseRequestBodyStream)
39+
40+
// but we issued a .produceMoreRequestBodyData before... Thus, we must accept more produced
41+
// data
42+
XCTAssertEqual(state.requestStreamPartReceived(part2),.sendBodyPart(part2))
43+
// however when we have put the data on the channel, we should not issue further
44+
// .produceMoreRequestBodyData events
45+
46+
// once we receive a writable event again, we can allow the producer to produce more data
47+
XCTAssertEqual(state.writabilityChanged(writable:true),.resumeRequestBodyStream)
48+
XCTAssertEqual(state.requestStreamPartReceived(part3),.sendBodyPart(part3))
49+
XCTAssertEqual(state.requestStreamFinished(),.sendRequestEnd)
50+
51+
letresponseHead=HTTPResponseHead(version:.http1_1, status:.ok)
52+
XCTAssertEqual(state.channelRead(.head(responseHead)),.forwardResponseHead(responseHead, pauseRequestBodyStream:false))
53+
letresponseBody=ByteBuffer(bytes:[1,2,3,4])
54+
XCTAssertEqual(state.channelRead(.body(responseBody)),.wait)
55+
XCTAssertEqual(state.channelRead(.end(nil)),.succeedRequest(.informConnectionIsIdle,.init([responseBody])))
56+
XCTAssertEqual(state.channelReadComplete(),.wait)
57+
}
58+
59+
func testResponseReadingWithBackpressure(){
60+
varstate=HTTP1ConnectionStateMachine()
61+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
62+
63+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.GET, uri:"/")
64+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.none)
65+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.sendRequestHead(requestHead, startBody:false))
66+
67+
letresponseHead=HTTPResponseHead(version:.http1_1, status:.ok, headers:HTTPHeaders([("content-length","12")]))
68+
XCTAssertEqual(state.channelRead(.head(responseHead)),.forwardResponseHead(responseHead, pauseRequestBodyStream:false))
69+
letpart0=ByteBuffer(bytes:0...3)
70+
letpart1=ByteBuffer(bytes:4...7)
71+
letpart2=ByteBuffer(bytes:8...11)
72+
XCTAssertEqual(state.channelRead(.body(part0)),.wait)
73+
XCTAssertEqual(state.channelRead(.body(part1)),.wait)
74+
XCTAssertEqual(state.channelReadComplete(),.forwardResponseBodyParts(.init([part0, part1])))
75+
XCTAssertEqual(state.read(),.wait)
76+
XCTAssertEqual(state.read(),.wait,"Expected to be able to consume a second read event")
77+
XCTAssertEqual(state.demandMoreResponseBodyParts(),.read)
78+
XCTAssertEqual(state.channelRead(.body(part2)),.wait)
79+
XCTAssertEqual(state.channelReadComplete(),.forwardResponseBodyParts(.init([part2])))
80+
XCTAssertEqual(state.demandMoreResponseBodyParts(),.wait)
81+
XCTAssertEqual(state.read(),.read)
82+
XCTAssertEqual(state.channelRead(.end(nil)),.succeedRequest(.informConnectionIsIdle,.init()))
83+
XCTAssertEqual(state.channelReadComplete(),.wait)
84+
XCTAssertEqual(state.read(),.read)
85+
}
86+
87+
func testAConnectionCloseHeaderInTheRequestLeadsToConnectionCloseAfterRequest(){
88+
varstate=HTTP1ConnectionStateMachine()
89+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
90+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.GET, uri:"/", headers:["connection":"close"])
91+
letmetadata=RequestFramingMetadata(connectionClose:true, body:.none)
92+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.sendRequestHead(requestHead, startBody:false))
93+
94+
letresponseHead=HTTPResponseHead(version:.http1_1, status:.ok)
95+
XCTAssertEqual(state.channelRead(.head(responseHead)),.forwardResponseHead(responseHead, pauseRequestBodyStream:false))
96+
letresponseBody=ByteBuffer(bytes:[1,2,3,4])
97+
XCTAssertEqual(state.channelRead(.body(responseBody)),.wait)
98+
XCTAssertEqual(state.channelRead(.end(nil)),.succeedRequest(.close,.init([responseBody])))
99+
}
100+
101+
func testAConnectionCloseHeaderInTheResponseLeadsToConnectionCloseAfterRequest(){
102+
varstate=HTTP1ConnectionStateMachine()
103+
XCTAssertEqual(state.channelActive(isWritable:false),.fireChannelActive)
104+
XCTAssertEqual(state.writabilityChanged(writable:true),.wait)
105+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.GET, uri:"/")
106+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.none)
107+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.sendRequestHead(requestHead, startBody:false))
108+
109+
letresponseHead=HTTPResponseHead(version:.http1_1, status:.ok, headers:["connection":"close"])
110+
XCTAssertEqual(state.channelRead(.head(responseHead)),.forwardResponseHead(responseHead, pauseRequestBodyStream:false))
111+
letresponseBody=ByteBuffer(bytes:[1,2,3,4])
112+
XCTAssertEqual(state.channelRead(.body(responseBody)),.wait)
113+
XCTAssertEqual(state.channelRead(.end(nil)),.succeedRequest(.close,.init([responseBody])))
114+
}
115+
116+
func testNIOTriggersChannelActiveTwice(){
117+
varstate=HTTP1ConnectionStateMachine()
118+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
119+
XCTAssertEqual(state.channelActive(isWritable:true),.wait)
120+
}
121+
122+
func testIdleConnectionBecomesInactive(){
123+
varstate=HTTP1ConnectionStateMachine()
124+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
125+
XCTAssertEqual(state.channelInactive(),.fireChannelInactive)
126+
XCTAssertEqual(state.channelInactive(),.wait)
127+
}
128+
129+
func testConnectionGoesAwayWhileInRequest(){
130+
varstate=HTTP1ConnectionStateMachine()
131+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
132+
133+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.GET, uri:"/")
134+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.none)
135+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.sendRequestHead(requestHead, startBody:false))
136+
137+
XCTAssertEqual(state.channelInactive(),.failRequest(HTTPClientError.remoteConnectionClosed,.none))
138+
}
139+
140+
func testRequestWasCancelledWhileUploadingData(){
141+
varstate=HTTP1ConnectionStateMachine()
142+
XCTAssertEqual(state.channelActive(isWritable:false),.fireChannelActive)
143+
144+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.POST, uri:"/", headers:HTTPHeaders([("content-length","4")]))
145+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.fixedSize(4))
146+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.wait)
147+
XCTAssertEqual(state.writabilityChanged(writable:true),.sendRequestHead(requestHead, startBody:true))
148+
149+
letpart0=IOData.byteBuffer(ByteBuffer(bytes:[0]))
150+
letpart1=IOData.byteBuffer(ByteBuffer(bytes:[1]))
151+
XCTAssertEqual(state.requestStreamPartReceived(part0),.sendBodyPart(part0))
152+
XCTAssertEqual(state.requestStreamPartReceived(part1),.sendBodyPart(part1))
153+
XCTAssertEqual(state.requestCancelled(closeConnection:false),.failRequest(HTTPClientError.cancelled,.close))
154+
}
155+
156+
func testCancelRequestIsIgnoredWhenConnectionIsIdle(){
157+
varstate=HTTP1ConnectionStateMachine()
158+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
159+
XCTAssertEqual(state.requestCancelled(closeConnection:false),.wait,"Should be ignored.")
160+
XCTAssertEqual(state.requestCancelled(closeConnection:true),.close,"Should lead to connection closure.")
161+
XCTAssertEqual(state.requestCancelled(closeConnection:true),.wait,"Should be ignored. Connection is already closing")
162+
XCTAssertEqual(state.channelInactive(),.fireChannelInactive)
163+
XCTAssertEqual(state.requestCancelled(closeConnection:true),.wait,"Should be ignored. Connection is already closed")
164+
}
165+
166+
func testReadsAreForwardedIfConnectionIsClosing(){
167+
varstate=HTTP1ConnectionStateMachine()
168+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
169+
XCTAssertEqual(state.requestCancelled(closeConnection:true),.close)
170+
XCTAssertEqual(state.read(),.read)
171+
XCTAssertEqual(state.channelInactive(),.fireChannelInactive)
172+
XCTAssertEqual(state.read(),.read)
173+
}
174+
175+
func testChannelReadsAreIgnoredIfConnectionIsClosing(){
176+
varstate=HTTP1ConnectionStateMachine()
177+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
178+
XCTAssertEqual(state.requestCancelled(closeConnection:true),.close)
179+
XCTAssertEqual(state.channelRead(.end(nil)),.wait)
180+
XCTAssertEqual(state.channelReadComplete(),.wait)
181+
XCTAssertEqual(state.channelInactive(),.fireChannelInactive)
182+
XCTAssertEqual(state.channelRead(.end(nil)),.wait)
183+
}
184+
185+
func testRequestIsCancelledWhileWaitingForWritable(){
186+
varstate=HTTP1ConnectionStateMachine()
187+
XCTAssertEqual(state.channelActive(isWritable:false),.fireChannelActive)
188+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.POST, uri:"/", headers:HTTPHeaders([("content-length","4")]))
189+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.fixedSize(4))
190+
XCTAssertEqual(state.runNewRequest(head: requestHead, metadata: metadata),.wait)
191+
XCTAssertEqual(state.requestCancelled(closeConnection:false),.failRequest(HTTPClientError.cancelled,.informConnectionIsIdle))
192+
}
193+
}
194+
195+
extensionHTTP1ConnectionStateMachine.Action:Equatable{
196+
publicstaticfunc==(lhs:Self, rhs:Self)->Bool{
197+
switch(lhs, rhs){
198+
case(.fireChannelActive,.fireChannelActive):
199+
returntrue
200+
201+
case(.fireChannelInactive,.fireChannelInactive):
202+
returntrue
203+
204+
case(.sendRequestHead(let lhsHead,let lhsStartBody),.sendRequestHead(let rhsHead,let rhsStartBody)):
205+
return lhsHead == rhsHead && lhsStartBody == rhsStartBody
206+
207+
case(.sendBodyPart(let lhsData),.sendBodyPart(let rhsData)):
208+
return lhsData == rhsData
209+
210+
case(.sendRequestEnd,.sendRequestEnd):
211+
returntrue
212+
213+
case(.pauseRequestBodyStream,.pauseRequestBodyStream):
214+
returntrue
215+
case(.resumeRequestBodyStream,.resumeRequestBodyStream):
216+
returntrue
217+
218+
case(.forwardResponseHead(let lhsHead,let lhsPauseRequestBodyStream),.forwardResponseHead(let rhsHead,let rhsPauseRequestBodyStream)):
219+
return lhsHead == rhsHead && lhsPauseRequestBodyStream == rhsPauseRequestBodyStream
220+
221+
case(.forwardResponseBodyParts(let lhsData),.forwardResponseBodyParts(let rhsData)):
222+
return lhsData == rhsData
223+
224+
case(.succeedRequest(let lhsFinalAction,let lhsFinalBuffer),.succeedRequest(let rhsFinalAction,let rhsFinalBuffer)):
225+
return lhsFinalAction == rhsFinalAction && lhsFinalBuffer == rhsFinalBuffer
226+
227+
case(.failRequest(_,let lhsFinalAction),.failRequest(_,let rhsFinalAction)):
228+
return lhsFinalAction == rhsFinalAction
229+
230+
case(.read,.read):
231+
returntrue
232+
233+
case(.close,.close):
234+
returntrue
235+
236+
case(.wait,.wait):
237+
returntrue
238+
239+
default:
240+
returnfalse
241+
}
242+
}
243+
}

‎Tests/LinuxMain.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import XCTest
2828
XCTMain([
2929
testCase(ConnectionPoolTests.allTests),
3030
testCase(ConnectionTests.allTests),
31+
testCase(HTTP1ConnectionStateMachineTests.allTests),
3132
testCase(HTTP1ProxyConnectHandlerTests.allTests),
3233
testCase(HTTPClientCookieTests.allTests),
3334
testCase(HTTPClientInternalTests.allTests),

0 commit comments

Comments
(0)