gh-141938/fix-http-client-state-reset #141941
Open
+36
−4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Closes gh-141938
Context:
As reported in gh-141938, when using
http.client, if anOSError(such asTimeoutErrororConnectionRefusedError) occurs during_send_request—specifically after the state has transitioned to_CS_REQ_STARTED—theHTTPConnectionobject remains in a "dirty" state.Because the state is not reset to
_CS_IDLE, the connection object becomes unusable. Any subsequent attempts to use.request(), even after calling.connect(), result in aCannotSendRequesterror.The Fix:
request(): Wrapped_send_requestin atry...except OSErrorblock.OSErroroccurs, we checkerrno.errno.EPIPE(Broken Pipe),self.close()is called explicitly. This resets the internal state machine (self.__state) to_CS_IDLEand closes the socket.errno.EPIPEis excluded to preserve existing behavior where a server closes the connection early (e.g., sending a 413 response), allowing the client to still read the response (prevents regression intest_epipe).__init__(): Changedself._create_connectionto use alambdainstead of capturingsocket.create_connectiondirectly.socket.create_connectionis looked up at call time, allowing unit tests to properly mock it for individual requests without the mock being permanently captured by the instance.Verification:
HTTPConnectionStateTeststoLib/test/test_httplib.py.TimeoutErrorresets state toIdle(previously stuck atRequest-sent) and closes the socket.conn.request()call proceeds without raisingCannotSendRequest.test_epipepasses, ensuring no regression for broken pipe scenarios.