Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip broken pipe errors #1524

Merged
merged 32 commits into from Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7ffb24e
Skipping the broken pipe errors.
robermorales Jan 11, 2019
d4416f0
Included `request_chunked()` in the control.
robermorales Jan 14, 2019
3a285b9
Added tests
robermorales Jan 14, 2019
5e68837
Added tests for the `request_chunked()` case
robermorales Jan 15, 2019
9a484d3
ignoring chunked test for Google App Engine
robermorales Jan 17, 2019
3e18f38
Skip on is_appengine()
sethmlarson Jan 17, 2019
ffeeb0e
merge new master
hodbn Apr 22, 2020
70b1f0c
Merge branch 'master' into broken-pipe-detection
hodbn Apr 22, 2020
5750179
rewrite broken pipe tests
hodbn Apr 23, 2020
567a8f4
account for EPROTOTYPE on osx
hodbn Apr 23, 2020
ec5ba40
Fix test_broken_pipe_ignore ignore on macOS
pquentin Apr 28, 2020
2a12bcf
Change TARPIT_HOST to detect isolated network (#1862)
hodbn Apr 24, 2020
0f3afa9
Add 'other errors' counter to Retry (#1824)
hodbn Apr 27, 2020
3ac99e2
Don't insert 'None' into ConnectionPool if it was empty
hodbn Apr 28, 2020
2f92f16
Fix linting for flake8 v3.8
pquentin May 12, 2020
fe8f85f
Use nox version that works with Python 3.5.2
pquentin May 29, 2020
d3901a8
Add InvalidChunkLength error
hodbn Jun 9, 2020
082b78d
Test buggy incomplete read
hodbn Jun 9, 2020
2ee6eac
Fix coverage, InvalidChunkLength is chunked and has no expected length
hodbn Jun 9, 2020
b0a8d79
Update docs for an old Python version test
hodbn Jun 9, 2020
e83cb29
Remove unnecessary httplib export
hodbn Jun 10, 2020
a024db5
Feature/support env var sslkeylogfile (#1867)
bastiaanb Jun 11, 2020
c9c57b4
Add documentation for using Nox more efficiently (#1887)
PleasantMachine9 Jun 14, 2020
75aca6a
Fix testing of SSLKEYLOGFILE on AppVeyor
hodbn Jun 24, 2020
aa06d7a
Raise a meaningful error for an unknown scheme (#1872)
hodbn Jun 30, 2020
2275717
Add default user agent header (#1750)
Jun 30, 2020
ccd3288
Merge remote-tracking branch 'urllib3/master' into robermorales-broke…
pquentin Jun 30, 2020
4a7040c
Clarify that behavior is identical with Python 2
pquentin Jun 30, 2020
c6ae228
Skip BrokenPipeError test on Windows
sethmlarson Aug 28, 2020
7450d79
Merge branch 'master' into broken-pipe-detection
sethmlarson Sep 4, 2020
d4d97de
Merge branch 'master' of ssh://github.com/urllib3/urllib3 into broken…
sethmlarson Sep 11, 2020
2335967
De-duplicate notWindows decorator
sethmlarson Sep 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/urllib3/connection.py
Expand Up @@ -30,6 +30,15 @@ class ConnectionError(Exception):
pass


try: # Python 3:
# Not a no-op, we're adding this to the namespace so it can be imported.
BrokenPipeError = BrokenPipeError
except NameError: # Python 2:

class BrokenPipeError(Exception):
pass


from .exceptions import (
NewConnectionError,
ConnectTimeoutError,
Expand Down
22 changes: 18 additions & 4 deletions src/urllib3/connectionpool.py
Expand Up @@ -34,6 +34,7 @@
VerifiedHTTPSConnection,
HTTPException,
BaseSSLError,
BrokenPipeError,
)
from .request import RequestMethods
from .response import HTTPResponse
Expand Down Expand Up @@ -386,10 +387,23 @@ def _make_request(

# conn.request() calls httplib.*.request, not the method in
# urllib3.request. It also calls makefile (recv) on the socket.
if chunked:
conn.request_chunked(method, url, **httplib_request_kw)
else:
conn.request(method, url, **httplib_request_kw)
pquentin marked this conversation as resolved.
Show resolved Hide resolved
try:
if chunked:
conn.request_chunked(method, url, **httplib_request_kw)
else:
conn.request(method, url, **httplib_request_kw)

# We are swallowing BrokenPipeError (errno.EPIPE) since the server is
# legitimately able to close the connection after sending a valid response.
# With this behaviour, the received response is still readable.
except BrokenPipeError: # Python 3
pass
except IOError as e:
# https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/
if e.errno == errno.EPROTOTYPE: # macOS
pass
elif e.errno != errno.EPIPE: # Python 2
sethmlarson marked this conversation as resolved.
Show resolved Hide resolved
raise

# Reset the timeout for the recv() on the socket
read_timeout = timeout_obj.read_timeout
Expand Down
35 changes: 35 additions & 0 deletions test/with_dummyserver/test_socketlevel.py
@@ -1,6 +1,7 @@
# TODO: Break this module up into pieces. Maybe group by functionality tested
# rather than the socket level-ness of it.
from urllib3 import HTTPConnectionPool, HTTPSConnectionPool
from urllib3.connection import HTTPConnection
from urllib3.poolmanager import proxy_from_url
from urllib3.exceptions import (
MaxRetryError,
Expand Down Expand Up @@ -1797,3 +1798,37 @@ def socket_handler(listener):
) as pool:
pool.urlopen("GET", "/not_found", preload_content=False)
assert pool.num_connections == 1


class TestBrokenPipe(SocketDummyServerTestCase):
def test_broken_pipe_ignore(self, monkeypatch):
sock_shut = Event()
orig_connect = HTTPConnection.connect
# a buffer that will cause two sendall calls
buf = "a" * 1024 * 1024 * 4

def connect_and_wait(*args, **kw):
ret = orig_connect(*args, **kw)
assert sock_shut.wait(5)
return ret

def socket_handler(listener):
for i in range(2):
sock = listener.accept()[0]
sock.send(
b"HTTP/1.1 404 Not Found\r\n"
b"Connection: close\r\n"
b"Content-Length: 0\r\n"
b"\r\n"
)
sock.shutdown(socket.SHUT_RDWR)
sock_shut.set()
sock.close()

monkeypatch.setattr(HTTPConnection, "connect", connect_and_wait)
self._start_server(socket_handler)
with HTTPConnectionPool(self.host, self.port) as pool:
r = pool.request("POST", "/", body=buf)
assert r.status == 404
r = pool.request("POST", "/admin", chunked=True, body=buf)
assert r.status == 404