Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcbf5330

Browse files
authored
Fix race between connection close and scheduling new request (#546) (#548)
1 parenteb7b1fb commitcbf5330

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,35 @@ struct HTTP1ConnectionStateMachine {
158158
head:HTTPRequestHead,
159159
metadata:RequestFramingMetadata
160160
)->Action{
161-
guard case.idle=self.stateelse{
161+
switchself.state{
162+
case.initialized,.closing,.inRequest:
163+
// These states are unreachable as the connection pool state machine has put the
164+
// connection into these states. In other words the connection pool state machine must
165+
// be aware about these states before the connection itself. For this reason the
166+
// connection pool state machine must not send a new request to the connection, if the
167+
// connection is `.initialized`, `.closing` or `.inRequest`
162168
preconditionFailure("Invalid state:\(self.state)")
163-
}
164169

165-
varrequestStateMachine=HTTPRequestStateMachine(isChannelWritable:self.isChannelWritable)
166-
letaction= requestStateMachine.startRequest(head: head, metadata: metadata)
170+
case.closed:
171+
// The remote may have closed the connection and the connection pool state machine
172+
// was not updated yet because of a race condition. New request vs. marking connection
173+
// as closed.
174+
//
175+
// TODO: AHC should support a fast rescheduling mechanism here.
176+
return.failRequest(HTTPClientError.remoteConnectionClosed,.none)
177+
178+
case.idle:
179+
varrequestStateMachine=HTTPRequestStateMachine(isChannelWritable:self.isChannelWritable)
180+
letaction= requestStateMachine.startRequest(head: head, metadata: metadata)
167181

168-
// by default we assume a persistent connection. however in `requestVerified`, we read the
169-
// "connection" header.
170-
self.state=.inRequest(requestStateMachine, close: metadata.connectionClose)
171-
returnself.state.modify(with: action)
182+
// by default we assume a persistent connection. however in `requestVerified`, we read the
183+
// "connection" header.
184+
self.state=.inRequest(requestStateMachine, close: metadata.connectionClose)
185+
returnself.state.modify(with: action)
186+
187+
case.modifying:
188+
preconditionFailure("Invalid state:\(self.state)")
189+
}
172190
}
173191

174192
mutatingfunc requestStreamPartReceived(_ part:IOData)->Action{

‎Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests+XCTest.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ extension HTTP1ConnectionStateMachineTests {
4242
("testConnectionIsClosedIfErrorHappensWhileInRequest", testConnectionIsClosedIfErrorHappensWhileInRequest),
4343
("testConnectionIsClosedAfterSwitchingProtocols", testConnectionIsClosedAfterSwitchingProtocols),
4444
("testWeDontCrashAfterEarlyHintsAndConnectionClose", testWeDontCrashAfterEarlyHintsAndConnectionClose),
45+
("testWeDontCrashInRaceBetweenSchedulingNewRequestAndConnectionClose", testWeDontCrashInRaceBetweenSchedulingNewRequestAndConnectionClose),
4546
]
4647
}
4748
}

‎Tests/AsyncHTTPClientTests/HTTP1ConnectionStateMachineTests.swift‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,20 @@ class HTTP1ConnectionStateMachineTests: XCTestCase {
267267
XCTAssertEqual(state.channelRead(.head(responseHead)),.wait)
268268
XCTAssertEqual(state.channelInactive(),.failRequest(HTTPClientError.remoteConnectionClosed,.none))
269269
}
270+
271+
func testWeDontCrashInRaceBetweenSchedulingNewRequestAndConnectionClose(){
272+
varstate=HTTP1ConnectionStateMachine()
273+
XCTAssertEqual(state.channelActive(isWritable:true),.fireChannelActive)
274+
XCTAssertEqual(state.channelInactive(),.fireChannelInactive)
275+
276+
letrequestHead=HTTPRequestHead(version:.http1_1, method:.GET, uri:"/")
277+
letmetadata=RequestFramingMetadata(connectionClose:false, body:.fixedSize(0))
278+
letnewRequestAction= state.runNewRequest(head: requestHead, metadata: metadata)
279+
guard case.failRequest(let error,.none)= newRequestActionelse{
280+
returnXCTFail("Unexpected test case")
281+
}
282+
XCTAssertEqual(erroras?HTTPClientError,.remoteConnectionClosed)
283+
}
270284
}
271285

272286
extensionHTTP1ConnectionStateMachine.Action:Equatable{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp