mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	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
					
				
					 7 changed files with 181 additions and 25 deletions
				
			
		|  | @ -26,18 +26,28 @@ that use HTTP and HTTPS. | ||||||
| 
 | 
 | ||||||
| The module provides the following classes: | 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 | An \class{HTTPConnection} instance represents one transaction with an HTTP | ||||||
| server.  It should be instantiated passing it a host and optional port number. | 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 | 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 | 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: | the server at the same host and port: | ||||||
| 
 | 
 | ||||||
| \begin{verbatim} | \begin{verbatim} | ||||||
| >>> h1 = httplib.HTTPConnection('www.cwi.nl') | >>> h1 = httplib.HTTPConnection('www.cwi.nl') | ||||||
| >>> h2 = httplib.HTTPConnection('www.cwi.nl:80') | >>> h2 = httplib.HTTPConnection('www.cwi.nl:80') | ||||||
| >>> h3 = httplib.HTTPConnection('www.cwi.nl', 80) | >>> h3 = httplib.HTTPConnection('www.cwi.nl', 80) | ||||||
|  | >>> h3 = httplib.HTTPConnection('www.cwi.nl', 80, timeout=10) | ||||||
| \end{verbatim} | \end{verbatim} | ||||||
| \versionadded{2.0} | \versionadded{2.0} | ||||||
| \end{classdesc} | \end{classdesc} | ||||||
|  |  | ||||||
|  | @ -170,6 +170,15 @@ supported on this platform. | ||||||
| \versionadded{2.3} | \versionadded{2.3} | ||||||
| \end{datadesc} | \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{, | \begin{funcdesc}{getaddrinfo}{host, port\optional{, family\optional{, | ||||||
|                               socktype\optional{, proto\optional{, |                               socktype\optional{, proto\optional{, | ||||||
|                               flags}}}}} |                               flags}}}}} | ||||||
|  |  | ||||||
|  | @ -625,7 +625,8 @@ class HTTPConnection: | ||||||
|     debuglevel = 0 |     debuglevel = 0 | ||||||
|     strict = 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.sock = None | ||||||
|         self._buffer = [] |         self._buffer = [] | ||||||
|         self.__response = None |         self.__response = None | ||||||
|  | @ -658,25 +659,7 @@ def set_debuglevel(self, level): | ||||||
| 
 | 
 | ||||||
|     def connect(self): |     def connect(self): | ||||||
|         """Connect to the host and port specified in __init__.""" |         """Connect to the host and port specified in __init__.""" | ||||||
|         msg = "getaddrinfo returns an empty list" |         self.sock = socket.create_connection((self.host,self.port), self.timeout) | ||||||
|         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 |  | ||||||
| 
 | 
 | ||||||
|     def close(self): |     def close(self): | ||||||
|         """Close the connection to the HTTP server.""" |         """Close the connection to the HTTP server.""" | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ | ||||||
| ssl() -- secure socket layer support (only available if configured) | ssl() -- secure socket layer support (only available if configured) | ||||||
| socket.getdefaulttimeout() -- get the default timeout value | socket.getdefaulttimeout() -- get the default timeout value | ||||||
| socket.setdefaulttimeout() -- set 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! |  [*] not available on all platforms! | ||||||
| 
 | 
 | ||||||
|  | @ -412,3 +413,31 @@ def next(self): | ||||||
|         if not line: |         if not line: | ||||||
|             raise StopIteration |             raise StopIteration | ||||||
|         return line |         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 httplib | ||||||
| import StringIO | import StringIO | ||||||
| import sys | import sys | ||||||
|  | import socket | ||||||
| 
 | 
 | ||||||
| from unittest import TestCase | from unittest import TestCase | ||||||
| 
 | 
 | ||||||
|  | @ -149,8 +150,47 @@ class OfflineTest(TestCase): | ||||||
|     def test_responses(self): |     def test_responses(self): | ||||||
|         self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found") |         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): | def test_main(verbose=None): | ||||||
|     test_support.run_unittest(HeaderTests, OfflineTest, BasicTest) |     test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest) | ||||||
| 
 | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     test_main() |     test_main() | ||||||
|  |  | ||||||
|  | @ -75,7 +75,7 @@ def _testFoo(self): | ||||||
| 
 | 
 | ||||||
|     Note, the server setup function cannot call any blocking |     Note, the server setup function cannot call any blocking | ||||||
|     functions that rely on the client thread during setup, |     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 |     the blocking call (such as in setting up a client/server | ||||||
|     connection and performing the accept() in setUp(). |     connection and performing the accept() in setUp(). | ||||||
|     """ |     """ | ||||||
|  | @ -810,6 +810,85 @@ class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase): | ||||||
|     bufsize = 2 # Exercise the buffering code |     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): | class Urllib2FileobjectTest(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     # urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that |     # urllib2.HTTPHandler has "borrowed" socket._fileobject, and requires that | ||||||
|  | @ -977,7 +1056,7 @@ def _testRecvFromInto(self): | ||||||
| 
 | 
 | ||||||
| def test_main(): | def test_main(): | ||||||
|     tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, |     tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, | ||||||
|              TestExceptions, BufferIOTest] |              TestExceptions, BufferIOTest, BasicTCPTest2] | ||||||
|     if sys.platform != 'mac': |     if sys.platform != 'mac': | ||||||
|         tests.extend([ BasicUDPTest, UDPTimeoutTest ]) |         tests.extend([ BasicUDPTest, UDPTimeoutTest ]) | ||||||
| 
 | 
 | ||||||
|  | @ -988,6 +1067,8 @@ def test_main(): | ||||||
|         LineBufferedFileObjectClassTestCase, |         LineBufferedFileObjectClassTestCase, | ||||||
|         SmallBufferedFileObjectClassTestCase, |         SmallBufferedFileObjectClassTestCase, | ||||||
|         Urllib2FileobjectTest, |         Urllib2FileobjectTest, | ||||||
|  |         NetworkConnectionAttributesTest, | ||||||
|  |         NetworkConnectionBehaviourTest, | ||||||
|     ]) |     ]) | ||||||
|     if hasattr(socket, "socketpair"): |     if hasattr(socket, "socketpair"): | ||||||
|         tests.append(BasicSocketPairTest) |         tests.append(BasicSocketPairTest) | ||||||
|  |  | ||||||
|  | @ -199,6 +199,10 @@ Core and builtins | ||||||
| Library | 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 #978833: Revert r50844, as it broke _socketobject.dup. | ||||||
| 
 | 
 | ||||||
| - Bug #1675967: re patterns pickled with Python 2.4 and earlier can | - Bug #1675967: re patterns pickled with Python 2.4 and earlier can | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Facundo Batista
						Facundo Batista