Added a 'create_connect()' function to socket.py, which creates a
connection with an optional timeout, and modified httplib.py to use this function in HTTPConnection. Applies patch 1676823.
This commit is contained in:
parent
f102e24bd3
commit
07c78be0b4
|
@ -26,18 +26,28 @@ that use HTTP and HTTPS.
|
|||
|
||||
The module provides the following classes:
|
||||
|
||||
\begin{classdesc}{HTTPConnection}{host\optional{, port}}
|
||||
\begin{classdesc}{HTTPConnection}{host\optional{, port\optional{,
|
||||
strict\optional{, timeout}}}}
|
||||
An \class{HTTPConnection} instance represents one transaction with an HTTP
|
||||
server. It should be instantiated passing it a host and optional port number.
|
||||
If no port number is passed, the port is extracted from the host string if it
|
||||
has the form \code{\var{host}:\var{port}}, else the default HTTP port (80) is
|
||||
used. For example, the following calls all create instances that connect to
|
||||
used.
|
||||
When True the optional parameter \var{strict}
|
||||
causes \code{BadStatusLine} to be raised if the status line can't be parsed
|
||||
as a valid HTTP/1.0 or 1.1 status line. If the optional \var{timeout}
|
||||
parameter is given, connection attempts will timeout after that many
|
||||
seconds (if no timeout is passed, or is passed as None, the global default
|
||||
timeout setting is used).
|
||||
|
||||
For example, the following calls all create instances that connect to
|
||||
the server at the same host and port:
|
||||
|
||||
\begin{verbatim}
|
||||
>>> h1 = httplib.HTTPConnection('www.cwi.nl')
|
||||
>>> h2 = httplib.HTTPConnection('www.cwi.nl:80')
|
||||
>>> h3 = httplib.HTTPConnection('www.cwi.nl', 80)
|
||||
>>> h3 = httplib.HTTPConnection('www.cwi.nl', 80, timeout=10)
|
||||
\end{verbatim}
|
||||
\versionadded{2.0}
|
||||
\end{classdesc}
|
||||
|
|
|
@ -170,6 +170,15 @@ supported on this platform.
|
|||
\versionadded{2.3}
|
||||
\end{datadesc}
|
||||
|
||||
\begin{funcdesc}{create_connection}{address\optional{, timeout}}
|
||||
Connects to the \var{address} received (as usual, a pair host/port), with
|
||||
an optional timeout for the connection. Specially useful for higher-level
|
||||
protocols, it is not normally used directly from application-level code.
|
||||
Passing the optional \var{timeout} parameter will set the timeout on the
|
||||
socket instance (if not present, or passed as None, the global default
|
||||
timeout setting is used).
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{getaddrinfo}{host, port\optional{, family\optional{,
|
||||
socktype\optional{, proto\optional{,
|
||||
flags}}}}}
|
||||
|
|
|
@ -625,7 +625,8 @@ class HTTPConnection:
|
|||
debuglevel = 0
|
||||
strict = 0
|
||||
|
||||
def __init__(self, host, port=None, strict=None):
|
||||
def __init__(self, host, port=None, strict=None, timeout=None):
|
||||
self.timeout = timeout
|
||||
self.sock = None
|
||||
self._buffer = []
|
||||
self.__response = None
|
||||
|
@ -658,25 +659,7 @@ class HTTPConnection:
|
|||
|
||||
def connect(self):
|
||||
"""Connect to the host and port specified in __init__."""
|
||||
msg = "getaddrinfo returns an empty list"
|
||||
for res in socket.getaddrinfo(self.host, self.port, 0,
|
||||
socket.SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
try:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
if self.debuglevel > 0:
|
||||
print "connect: (%s, %s)" % (self.host, self.port)
|
||||
self.sock.connect(sa)
|
||||
except socket.error, msg:
|
||||
if self.debuglevel > 0:
|
||||
print 'connect fail:', (self.host, self.port)
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
continue
|
||||
break
|
||||
if not self.sock:
|
||||
raise socket.error, msg
|
||||
self.sock = socket.create_connection((self.host,self.port), self.timeout)
|
||||
|
||||
def close(self):
|
||||
"""Close the connection to the HTTP server."""
|
||||
|
|
|
@ -24,6 +24,7 @@ inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89)
|
|||
ssl() -- secure socket layer support (only available if configured)
|
||||
socket.getdefaulttimeout() -- get the default timeout value
|
||||
socket.setdefaulttimeout() -- set the default timeout value
|
||||
create_connection() -- connects to an address, with an optional timeout
|
||||
|
||||
[*] not available on all platforms!
|
||||
|
||||
|
@ -412,3 +413,31 @@ class _fileobject(object):
|
|||
if not line:
|
||||
raise StopIteration
|
||||
return line
|
||||
|
||||
|
||||
def create_connection(address, timeout=None):
|
||||
"""Connect to address (host, port) with an optional timeout.
|
||||
|
||||
Provides access to socketobject timeout for higher-level
|
||||
protocols. Passing a timeout will set the timeout on the
|
||||
socket instance (if not present, or passed as None, the
|
||||
default global timeout setting will be used).
|
||||
"""
|
||||
|
||||
msg = "getaddrinfo returns an empty list"
|
||||
host, port = address
|
||||
for res in getaddrinfo(host, port, 0, SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
sock = None
|
||||
try:
|
||||
sock = socket(af, socktype, proto)
|
||||
if timeout is not None:
|
||||
sock.settimeout(timeout)
|
||||
sock.connect(sa)
|
||||
return sock
|
||||
|
||||
except error, msg:
|
||||
if sock is not None:
|
||||
sock.close()
|
||||
|
||||
raise error, msg
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import httplib
|
||||
import StringIO
|
||||
import sys
|
||||
import socket
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
|
@ -149,8 +150,47 @@ class OfflineTest(TestCase):
|
|||
def test_responses(self):
|
||||
self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found")
|
||||
|
||||
PORT = 50003
|
||||
HOST = "localhost"
|
||||
|
||||
class TimeoutTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
global PORT
|
||||
PORT = test_support.bind_port(self.serv, HOST, PORT)
|
||||
self.serv.listen(1)
|
||||
|
||||
def tearDown(self):
|
||||
self.serv.close()
|
||||
self.serv = None
|
||||
|
||||
def testTimeoutAttribute(self):
|
||||
'''This will prove that the timeout gets through
|
||||
HTTPConnection and into the socket.
|
||||
'''
|
||||
# default
|
||||
httpConn = httplib.HTTPConnection(HOST, PORT)
|
||||
httpConn.connect()
|
||||
self.assertTrue(httpConn.sock.gettimeout() is None)
|
||||
|
||||
# a value
|
||||
httpConn = httplib.HTTPConnection(HOST, PORT, timeout=10)
|
||||
httpConn.connect()
|
||||
self.assertEqual(httpConn.sock.gettimeout(), 10)
|
||||
|
||||
# None, having other default
|
||||
previous = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(10)
|
||||
httpConn = httplib.HTTPConnection(HOST, PORT, timeout=None)
|
||||
httpConn.connect()
|
||||
socket.setdefaulttimeout(previous)
|
||||
self.assertEqual(httpConn.sock.gettimeout(), 10)
|
||||
|
||||
|
||||
def test_main(verbose=None):
|
||||
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest)
|
||||
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main()
|
||||
|
|
|
@ -75,7 +75,7 @@ class ThreadableTest:
|
|||
|
||||
Note, the server setup function cannot call any blocking
|
||||
functions that rely on the client thread during setup,
|
||||
unless serverExplicityReady() is called just before
|
||||
unless serverExplicitReady() is called just before
|
||||
the blocking call (such as in setting up a client/server
|
||||
connection and performing the accept() in setUp().
|
||||
"""
|
||||
|
@ -810,6 +810,85 @@ class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase):
|
|||
bufsize = 2 # Exercise the buffering code
|
||||
|
||||
|
||||
class NetworkConnectionTest(object):
|
||||
"""Prove network connection."""
|
||||
def clientSetUp(self):
|
||||
self.cli = socket.create_connection((HOST, PORT))
|
||||
self.serv_conn = self.cli
|
||||
|
||||
class BasicTCPTest2(NetworkConnectionTest, BasicTCPTest):
|
||||
"""Tests that NetworkConnection does not break existing TCP functionality.
|
||||
"""
|
||||
|
||||
class NetworkConnectionAttributesTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
global PORT
|
||||
PORT = test_support.bind_port(self.serv, HOST, PORT)
|
||||
self.serv.listen(1)
|
||||
|
||||
def tearDown(self):
|
||||
if self.serv:
|
||||
self.serv.close()
|
||||
self.serv = None
|
||||
|
||||
def testWithoutServer(self):
|
||||
self.tearDown()
|
||||
self.failUnlessRaises(socket.error, lambda: socket.create_connection((HOST, PORT)))
|
||||
|
||||
def testTimeoutAttribute(self):
|
||||
# default
|
||||
sock = socket.create_connection((HOST, PORT))
|
||||
self.assertTrue(sock.gettimeout() is None)
|
||||
|
||||
# a value, named
|
||||
sock = socket.create_connection((HOST, PORT), timeout=10)
|
||||
self.assertEqual(sock.gettimeout(), 10)
|
||||
|
||||
# a value, just the value
|
||||
sock = socket.create_connection((HOST, PORT), 10)
|
||||
self.assertEqual(sock.gettimeout(), 10)
|
||||
|
||||
# None, having other default
|
||||
previous = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(10)
|
||||
sock = socket.create_connection((HOST, PORT), timeout=None)
|
||||
socket.setdefaulttimeout(previous)
|
||||
self.assertEqual(sock.gettimeout(), 10)
|
||||
|
||||
def testFamily(self):
|
||||
sock = socket.create_connection((HOST, PORT), timeout=10)
|
||||
self.assertEqual(sock.family, 2)
|
||||
|
||||
|
||||
def threadedServer(delay):
|
||||
serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
global PORT
|
||||
PORT = test_support.bind_port(serv, HOST, PORT)
|
||||
serv.listen(1)
|
||||
conn, addr = serv.accept()
|
||||
time.sleep(delay)
|
||||
conn.send("done!")
|
||||
conn.close()
|
||||
|
||||
class NetworkConnectionBehaviourTest(unittest.TestCase):
|
||||
def testInsideTimeout(self):
|
||||
threading.Thread(target=threadedServer, args=(3,)).start()
|
||||
time.sleep(.1)
|
||||
sock = socket.create_connection((HOST, PORT))
|
||||
data = sock.recv(5)
|
||||
self.assertEqual(data, "done!")
|
||||
|
||||
def testOutsideTimeout(self):
|
||||
threading.Thread(target=threadedServer, args=(3,)).start()
|
||||
time.sleep(.1)
|
||||
sock = socket.create_connection((HOST, PORT), timeout=1)
|
||||
self.failUnlessRaises(socket.timeout, lambda: sock.recv(5))
|
||||
|
||||
|
||||
class Urllib2FileobjectTest(unittest.TestCase):
|
||||
|
||||
# urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that
|
||||
|
@ -977,7 +1056,7 @@ class BufferIOTest(SocketConnectedTest):
|
|||
|
||||
def test_main():
|
||||
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
|
||||
TestExceptions, BufferIOTest]
|
||||
TestExceptions, BufferIOTest, BasicTCPTest2]
|
||||
if sys.platform != 'mac':
|
||||
tests.extend([ BasicUDPTest, UDPTimeoutTest ])
|
||||
|
||||
|
@ -988,6 +1067,8 @@ def test_main():
|
|||
LineBufferedFileObjectClassTestCase,
|
||||
SmallBufferedFileObjectClassTestCase,
|
||||
Urllib2FileobjectTest,
|
||||
NetworkConnectionAttributesTest,
|
||||
NetworkConnectionBehaviourTest,
|
||||
])
|
||||
if hasattr(socket, "socketpair"):
|
||||
tests.append(BasicSocketPairTest)
|
||||
|
|
|
@ -199,6 +199,10 @@ Core and builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Patch #1676823: Added create_connection() to socket.py, which may be
|
||||
called with a timeout, and use it from httplib (whose HTTPConnection
|
||||
now accepts an optional timeout).
|
||||
|
||||
- Bug #978833: Revert r50844, as it broke _socketobject.dup.
|
||||
|
||||
- Bug #1675967: re patterns pickled with Python 2.4 and earlier can
|
||||
|
|
Loading…
Reference in New Issue