summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFacundo Batista <facundobatista@gmail.com>2007-03-23 18:54:07 (GMT)
committerFacundo Batista <facundobatista@gmail.com>2007-03-23 18:54:07 (GMT)
commit07c78be0b4723deb62acebab50888cc59b1e4eb2 (patch)
tree0d1da08d12eafe4ac485889e1aac891e035c0696
parentf102e24bd34442026f4200a298a8b08d1deb3616 (diff)
downloadcpython-07c78be0b4723deb62acebab50888cc59b1e4eb2.zip
cpython-07c78be0b4723deb62acebab50888cc59b1e4eb2.tar.gz
cpython-07c78be0b4723deb62acebab50888cc59b1e4eb2.tar.bz2
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.
-rw-r--r--Doc/lib/libhttplib.tex14
-rw-r--r--Doc/lib/libsocket.tex9
-rw-r--r--Lib/httplib.py23
-rw-r--r--Lib/socket.py29
-rw-r--r--Lib/test/test_httplib.py42
-rw-r--r--Lib/test/test_socket.py85
-rw-r--r--Misc/NEWS4
7 files changed, 181 insertions, 25 deletions
diff --git a/Doc/lib/libhttplib.tex b/Doc/lib/libhttplib.tex
index 557ee3d..67371a4 100644
--- a/Doc/lib/libhttplib.tex
+++ b/Doc/lib/libhttplib.tex
@@ -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}
diff --git a/Doc/lib/libsocket.tex b/Doc/lib/libsocket.tex
index e0ce0a5..c63b52b 100644
--- a/Doc/lib/libsocket.tex
+++ b/Doc/lib/libsocket.tex
@@ -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}}}}}
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 213d80c..008f0a4 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -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."""
diff --git a/Lib/socket.py b/Lib/socket.py
index 0082e76..c03e884 100644
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -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
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 90a4e55..a39a3eb 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -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()
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index c56c3b7..a7ebe04 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -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)
diff --git a/Misc/NEWS b/Misc/NEWS
index 3489240..dd8f51e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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