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:
Facundo Batista 2007-03-23 18:54:07 +00:00
parent f102e24bd3
commit 07c78be0b4
7 changed files with 181 additions and 25 deletions

View File

@ -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}

View File

@ -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}}}}}

View File

@ -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."""

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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