Patch #1627441: close sockets properly in urllib2.
This commit is contained in:
parent
b84c13792d
commit
dd7b0525e9
|
@ -204,9 +204,10 @@ class _fileobject(object):
|
||||||
|
|
||||||
__slots__ = ["mode", "bufsize", "softspace",
|
__slots__ = ["mode", "bufsize", "softspace",
|
||||||
# "closed" is a property, see below
|
# "closed" is a property, see below
|
||||||
"_sock", "_rbufsize", "_wbufsize", "_rbuf", "_wbuf"]
|
"_sock", "_rbufsize", "_wbufsize", "_rbuf", "_wbuf",
|
||||||
|
"_close"]
|
||||||
|
|
||||||
def __init__(self, sock, mode='rb', bufsize=-1):
|
def __init__(self, sock, mode='rb', bufsize=-1, close=False):
|
||||||
self._sock = sock
|
self._sock = sock
|
||||||
self.mode = mode # Not actually used in this version
|
self.mode = mode # Not actually used in this version
|
||||||
if bufsize < 0:
|
if bufsize < 0:
|
||||||
|
@ -222,6 +223,7 @@ class _fileobject(object):
|
||||||
self._wbufsize = bufsize
|
self._wbufsize = bufsize
|
||||||
self._rbuf = "" # A string
|
self._rbuf = "" # A string
|
||||||
self._wbuf = [] # A list of strings
|
self._wbuf = [] # A list of strings
|
||||||
|
self._close = close
|
||||||
|
|
||||||
def _getclosed(self):
|
def _getclosed(self):
|
||||||
return self._sock is None
|
return self._sock is None
|
||||||
|
@ -232,6 +234,8 @@ class _fileobject(object):
|
||||||
if self._sock:
|
if self._sock:
|
||||||
self.flush()
|
self.flush()
|
||||||
finally:
|
finally:
|
||||||
|
if self._close:
|
||||||
|
self._sock.close()
|
||||||
self._sock = None
|
self._sock = None
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
|
|
@ -809,6 +809,31 @@ class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase):
|
||||||
|
|
||||||
bufsize = 2 # Exercise the buffering code
|
bufsize = 2 # Exercise the buffering code
|
||||||
|
|
||||||
|
|
||||||
|
class Urllib2FileobjectTest(unittest.TestCase):
|
||||||
|
|
||||||
|
# urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that
|
||||||
|
# it close the socket if the close c'tor argument is true
|
||||||
|
|
||||||
|
def testClose(self):
|
||||||
|
class MockSocket:
|
||||||
|
closed = False
|
||||||
|
def flush(self): pass
|
||||||
|
def close(self): self.closed = True
|
||||||
|
|
||||||
|
# must not close unless we request it: the original use of _fileobject
|
||||||
|
# by module socket requires that the underlying socket not be closed until
|
||||||
|
# the _socketobject that created the _fileobject is closed
|
||||||
|
s = MockSocket()
|
||||||
|
f = socket._fileobject(s)
|
||||||
|
f.close()
|
||||||
|
self.assert_(not s.closed)
|
||||||
|
|
||||||
|
s = MockSocket()
|
||||||
|
f = socket._fileobject(s, close=True)
|
||||||
|
f.close()
|
||||||
|
self.assert_(s.closed)
|
||||||
|
|
||||||
class TCPTimeoutTest(SocketTCPTest):
|
class TCPTimeoutTest(SocketTCPTest):
|
||||||
|
|
||||||
def testTCPTimeout(self):
|
def testTCPTimeout(self):
|
||||||
|
@ -961,7 +986,8 @@ def test_main():
|
||||||
FileObjectClassTestCase,
|
FileObjectClassTestCase,
|
||||||
UnbufferedFileObjectClassTestCase,
|
UnbufferedFileObjectClassTestCase,
|
||||||
LineBufferedFileObjectClassTestCase,
|
LineBufferedFileObjectClassTestCase,
|
||||||
SmallBufferedFileObjectClassTestCase
|
SmallBufferedFileObjectClassTestCase,
|
||||||
|
Urllib2FileobjectTest,
|
||||||
])
|
])
|
||||||
if hasattr(socket, "socketpair"):
|
if hasattr(socket, "socketpair"):
|
||||||
tests.append(BasicSocketPairTest)
|
tests.append(BasicSocketPairTest)
|
||||||
|
|
|
@ -64,6 +64,27 @@ class AuthTests(unittest.TestCase):
|
||||||
# urllib2.urlopen, "http://evil:thing@example.com")
|
# urllib2.urlopen, "http://evil:thing@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
class CloseSocketTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_close(self):
|
||||||
|
import socket, httplib, gc
|
||||||
|
|
||||||
|
# calling .close() on urllib2's response objects should close the
|
||||||
|
# underlying socket
|
||||||
|
|
||||||
|
# delve deep into response to fetch socket._socketobject
|
||||||
|
response = urllib2.urlopen("http://www.python.org/")
|
||||||
|
abused_fileobject = response.fp
|
||||||
|
self.assert_(abused_fileobject.__class__ is socket._fileobject)
|
||||||
|
httpresponse = abused_fileobject._sock
|
||||||
|
self.assert_(httpresponse.__class__ is httplib.HTTPResponse)
|
||||||
|
fileobject = httpresponse.fp
|
||||||
|
self.assert_(fileobject.__class__ is socket._fileobject)
|
||||||
|
|
||||||
|
self.assert_(not fileobject.closed)
|
||||||
|
response.close()
|
||||||
|
self.assert_(fileobject.closed)
|
||||||
|
|
||||||
class urlopenNetworkTests(unittest.TestCase):
|
class urlopenNetworkTests(unittest.TestCase):
|
||||||
"""Tests urllib2.urlopen using the network.
|
"""Tests urllib2.urlopen using the network.
|
||||||
|
|
||||||
|
@ -263,8 +284,12 @@ class OtherNetworkTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
test_support.requires("network")
|
test_support.requires("network")
|
||||||
test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests,
|
test_support.run_unittest(URLTimeoutTest,
|
||||||
AuthTests, OtherNetworkTests)
|
urlopenNetworkTests,
|
||||||
|
AuthTests,
|
||||||
|
OtherNetworkTests,
|
||||||
|
CloseSocketTest,
|
||||||
|
)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -1087,7 +1087,7 @@ class AbstractHTTPHandler(BaseHandler):
|
||||||
# out of socket._fileobject() and into a base class.
|
# out of socket._fileobject() and into a base class.
|
||||||
|
|
||||||
r.recv = r.read
|
r.recv = r.read
|
||||||
fp = socket._fileobject(r)
|
fp = socket._fileobject(r, close=True)
|
||||||
|
|
||||||
resp = addinfourl(fp, r.msg, req.get_full_url())
|
resp = addinfourl(fp, r.msg, req.get_full_url())
|
||||||
resp.code = r.status
|
resp.code = r.status
|
||||||
|
|
|
@ -111,6 +111,8 @@ Core and builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Patch #1627441: close sockets properly in urllib2.
|
||||||
|
|
||||||
- Bug #494589: make ntpath.expandvars behave according to its docstring.
|
- Bug #494589: make ntpath.expandvars behave according to its docstring.
|
||||||
|
|
||||||
- Changed platform module API python_version_tuple() to actually
|
- Changed platform module API python_version_tuple() to actually
|
||||||
|
|
Loading…
Reference in New Issue