Show More
@@ -112,6 +112,10 b' class HTTPResponse(object):' | |||
|
112 | 112 | |
|
113 | 113 | def complete(self): |
|
114 | 114 | """Returns true if this response is completely loaded. |
|
115 | ||
|
116 | Note that if this is a connection where complete means the | |
|
117 | socket is closed, this will nearly always return False, even | |
|
118 | in cases where all the data has actually been loaded. | |
|
115 | 119 | """ |
|
116 | 120 | if self._chunked: |
|
117 | 121 | return self._chunked_done |
@@ -174,10 +178,7 b' class HTTPResponse(object):' | |||
|
174 | 178 | return True |
|
175 | 179 | logger.debug('response read %d data during _select', len(data)) |
|
176 | 180 | if not data: |
|
177 | if not self.headers: | |
|
178 | self._load_response(self._end_headers) | |
|
179 | self._content_len = 0 | |
|
180 | elif self._content_len == _LEN_CLOSE_IS_END: | |
|
181 | if self.headers and self._content_len == _LEN_CLOSE_IS_END: | |
|
181 | 182 | self._content_len = len(self._body) |
|
182 | 183 | return False |
|
183 | 184 | else: |
@@ -561,17 +562,46 b' class HTTPConnection(object):' | |||
|
561 | 562 | continue |
|
562 | 563 | if not data: |
|
563 | 564 | logger.info('socket appears closed in read') |
|
564 |
|
|
|
565 |
|
|
|
565 | self.sock = None | |
|
566 | self._current_response = None | |
|
567 | # This if/elif ladder is a bit subtle, | |
|
568 | # comments in each branch should help. | |
|
569 | if response is not None and ( | |
|
570 | response.complete() or | |
|
571 | response._content_len == _LEN_CLOSE_IS_END): | |
|
572 | # Server responded completely and then | |
|
573 | # closed the socket. We should just shut | |
|
574 | # things down and let the caller get their | |
|
575 | # response. | |
|
576 | logger.info('Got an early response, ' | |
|
577 | 'aborting remaining request.') | |
|
578 | break | |
|
579 | elif was_first and response is None: | |
|
580 | # Most likely a keepalive that got killed | |
|
581 | # on the server's end. Commonly happens | |
|
582 | # after getting a really large response | |
|
583 | # from the server. | |
|
584 | logger.info( | |
|
585 | 'Connection appeared closed in read on first' | |
|
586 | ' request loop iteration, will retry.') | |
|
587 | reconnect('read') | |
|
588 | continue | |
|
589 | else: | |
|
590 | # We didn't just send the first data hunk, | |
|
591 | # and either have a partial response or no | |
|
592 | # response at all. There's really nothing | |
|
593 | # meaningful we can do here. | |
|
594 | raise HTTPStateError( | |
|
595 | 'Connection appears closed after ' | |
|
596 | 'some request data was written, but the ' | |
|
597 | 'response was missing or incomplete!') | |
|
598 | logger.debug('read %d bytes in request()', len(data)) | |
|
566 | 599 | if response is None: |
|
567 | 600 | response = self.response_class(r[0], self.timeout) |
|
568 | 601 | response._load_response(data) |
|
569 | if (response._content_len == _LEN_CLOSE_IS_END | |
|
570 | and len(data) == 0): | |
|
571 | response._content_len = len(response._body) | |
|
572 | if response.complete(): | |
|
573 | w = [] | |
|
574 | response.will_close = True | |
|
602 | # Jump to the next select() call so we load more | |
|
603 | # data if the server is still sending us content. | |
|
604 | continue | |
|
575 | 605 | except socket.error, e: |
|
576 | 606 | if e[0] != errno.EPIPE and not was_first: |
|
577 | 607 | raise |
@@ -662,4 +692,7 b' class BadRequestData(httplib.HTTPExcepti' | |||
|
662 | 692 | |
|
663 | 693 | class HTTPProxyConnectFailedException(httplib.HTTPException): |
|
664 | 694 | """Connecting to the HTTP proxy failed.""" |
|
695 | ||
|
696 | class HTTPStateError(httplib.HTTPException): | |
|
697 | """Invalid internal state encountered.""" | |
|
665 | 698 | # no-check-code |
@@ -26,6 +26,7 b'' | |||
|
26 | 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 | 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 | 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 | import socket | |
|
29 | 30 | import unittest |
|
30 | 31 | |
|
31 | 32 | import http |
@@ -39,7 +40,7 b' class SimpleHttpTest(util.HttpTestBase, ' | |||
|
39 | 40 | def _run_simple_test(self, host, server_data, expected_req, expected_data): |
|
40 | 41 | con = http.HTTPConnection(host) |
|
41 | 42 | con._connect() |
|
42 |
con.sock.data |
|
|
43 | con.sock.data = server_data | |
|
43 | 44 | con.request('GET', '/') |
|
44 | 45 | |
|
45 | 46 | self.assertStringEqual(expected_req, con.sock.sent) |
@@ -353,4 +354,33 b' dotencode' | |||
|
353 | 354 | |
|
354 | 355 | self.assertEqual(('1.2.3.4', 80), con.sock.sa) |
|
355 | 356 | self.assertEqual(expected_req, con.sock.sent) |
|
357 | ||
|
358 | def test_conn_keep_alive_but_server_close_anyway(self): | |
|
359 | sockets = [] | |
|
360 | def closingsocket(*args, **kwargs): | |
|
361 | s = util.MockSocket(*args, **kwargs) | |
|
362 | sockets.append(s) | |
|
363 | s.data = ['HTTP/1.1 200 OK\r\n', | |
|
364 | 'Server: BogusServer 1.0\r\n', | |
|
365 | 'Connection: Keep-Alive\r\n', | |
|
366 | 'Content-Length: 16', | |
|
367 | '\r\n\r\n', | |
|
368 | 'You can do that.'] | |
|
369 | s.close_on_empty = True | |
|
370 | return s | |
|
371 | ||
|
372 | socket.socket = closingsocket | |
|
373 | con = http.HTTPConnection('1.2.3.4:80') | |
|
374 | con._connect() | |
|
375 | con.request('GET', '/') | |
|
376 | r1 = con.getresponse() | |
|
377 | r1.read() | |
|
378 | self.assertFalse(con.sock.closed) | |
|
379 | self.assert_(con.sock.remote_closed) | |
|
380 | con.request('GET', '/') | |
|
381 | self.assertEqual(2, len(sockets)) | |
|
382 | ||
|
383 | def test_no_response_raises_response_not_ready(self): | |
|
384 | con = http.HTTPConnection('foo') | |
|
385 | self.assertRaises(http.httplib.ResponseNotReady, con.getresponse) | |
|
356 | 386 | # no-check-code |
@@ -53,7 +53,7 b' class ChunkedTransferTest(util.HttpTestB' | |||
|
53 | 53 | con = http.HTTPConnection('1.2.3.4:80') |
|
54 | 54 | con._connect() |
|
55 | 55 | sock = con.sock |
|
56 |
sock.read_wait_sentinel = ' |
|
|
56 | sock.read_wait_sentinel = '0\r\n\r\n' | |
|
57 | 57 | sock.data = ['HTTP/1.1 200 OK\r\n', |
|
58 | 58 | 'Server: BogusServer 1.0\r\n', |
|
59 | 59 | 'Content-Length: 6', |
@@ -43,7 +43,7 b' def make_preloaded_socket(data):' | |||
|
43 | 43 | """ |
|
44 | 44 | def s(*args, **kwargs): |
|
45 | 45 | sock = util.MockSocket(*args, **kwargs) |
|
46 | sock.data = data[:] | |
|
46 | sock.early_data = data[:] | |
|
47 | 47 | return sock |
|
48 | 48 | return s |
|
49 | 49 | |
@@ -97,24 +97,27 b' class ProxyHttpTest(util.HttpTestBase, u' | |||
|
97 | 97 | '\r\n' |
|
98 | 98 | '1234567890']) |
|
99 | 99 | con._connect() |
|
100 |
con.sock.data |
|
|
101 |
|
|
|
102 |
|
|
|
103 |
|
|
|
104 |
|
|
|
105 |
|
|
|
100 | con.sock.data = ['HTTP/1.1 200 OK\r\n', | |
|
101 | 'Server: BogusServer 1.0\r\n', | |
|
102 | 'Content-Length: 10\r\n', | |
|
103 | '\r\n' | |
|
104 | '1234567890' | |
|
105 | ] | |
|
106 | connect_sent = con.sock.sent | |
|
107 | con.sock.sent = '' | |
|
106 | 108 | con.request('GET', '/') |
|
107 | 109 | |
|
108 |
expected_ |
|
|
109 | 'Host: 1.2.3.4\r\n' | |
|
110 | 'accept-encoding: identity\r\n' | |
|
111 | '\r\n' | |
|
112 |
|
|
|
113 | 'Host: 1.2.3.4\r\n' | |
|
114 | 'accept-encoding: identity\r\n\r\n') | |
|
110 | expected_connect = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' | |
|
111 | 'Host: 1.2.3.4\r\n' | |
|
112 | 'accept-encoding: identity\r\n' | |
|
113 | '\r\n') | |
|
114 | expected_request = ('GET / HTTP/1.1\r\n' | |
|
115 | 'Host: 1.2.3.4\r\n' | |
|
116 | 'accept-encoding: identity\r\n\r\n') | |
|
115 | 117 | |
|
116 | 118 | self.assertEqual(('127.0.0.42', 4242), con.sock.sa) |
|
117 |
self.assertStringEqual(expected_ |
|
|
119 | self.assertStringEqual(expected_connect, connect_sent) | |
|
120 | self.assertStringEqual(expected_request, con.sock.sent) | |
|
118 | 121 | resp = con.getresponse() |
|
119 | 122 | self.assertEqual(resp.status, 200) |
|
120 | 123 | self.assertEqual('1234567890', resp.read()) |
@@ -41,15 +41,15 b' class HttpSslTest(util.HttpTestBase, uni' | |||
|
41 | 41 | con._connect() |
|
42 | 42 | # extend the list instead of assign because of how |
|
43 | 43 | # MockSSLSocket works. |
|
44 |
con.sock.data |
|
|
45 |
|
|
|
46 |
|
|
|
47 |
|
|
|
48 |
|
|
|
49 |
|
|
|
50 |
|
|
|
51 |
|
|
|
52 |
|
|
|
44 | con.sock.data = ['HTTP/1.1 200 OK\r\n', | |
|
45 | 'Server: BogusServer 1.0\r\n', | |
|
46 | 'MultiHeader: Value\r\n' | |
|
47 | 'MultiHeader: Other Value\r\n' | |
|
48 | 'MultiHeader: One More!\r\n' | |
|
49 | 'Content-Length: 10\r\n', | |
|
50 | '\r\n' | |
|
51 | '1234567890' | |
|
52 | ] | |
|
53 | 53 | con.request('GET', '/') |
|
54 | 54 | |
|
55 | 55 | expected_req = ('GET / HTTP/1.1\r\n' |
@@ -68,17 +68,15 b' class HttpSslTest(util.HttpTestBase, uni' | |||
|
68 | 68 | def testSslRereadInEarlyResponse(self): |
|
69 | 69 | con = http.HTTPConnection('1.2.3.4:443') |
|
70 | 70 | con._connect() |
|
71 | # extend the list instead of assign because of how | |
|
72 | # MockSSLSocket works. | |
|
73 | con.sock.early_data.extend(['HTTP/1.1 200 OK\r\n', | |
|
74 |
|
|
|
75 |
|
|
|
76 |
|
|
|
77 |
|
|
|
78 |
|
|
|
79 |
|
|
|
80 | '1234567890' | |
|
81 | ]) | |
|
71 | con.sock.early_data = ['HTTP/1.1 200 OK\r\n', | |
|
72 | 'Server: BogusServer 1.0\r\n', | |
|
73 | 'MultiHeader: Value\r\n' | |
|
74 | 'MultiHeader: Other Value\r\n' | |
|
75 | 'MultiHeader: One More!\r\n' | |
|
76 | 'Content-Length: 10\r\n', | |
|
77 | '\r\n' | |
|
78 | '1234567890' | |
|
79 | ] | |
|
82 | 80 | |
|
83 | 81 | expected_req = self.doPost(con, False) |
|
84 | 82 | self.assertEqual(None, con.sock, |
@@ -92,3 +90,4 b' class HttpSslTest(util.HttpTestBase, uni' | |||
|
92 | 90 | resp.headers.getheaders('multiheader')) |
|
93 | 91 | self.assertEqual(['BogusServer 1.0'], |
|
94 | 92 | resp.headers.getheaders('server')) |
|
93 | # no-check-code |
@@ -88,7 +88,7 b' class MockSocket(object):' | |||
|
88 | 88 | def ready_for_read(self): |
|
89 | 89 | return ((self.early_data and http._END_HEADERS in self.sent) |
|
90 | 90 | or (self.read_wait_sentinel in self.sent and self.data) |
|
91 | or self.closed) | |
|
91 | or self.closed or self.remote_closed) | |
|
92 | 92 | |
|
93 | 93 | def send(self, data): |
|
94 | 94 | # this is a horrible mock, but nothing needs us to raise the |
@@ -117,6 +117,11 b' class MockSSLSocket(object):' | |||
|
117 | 117 | def __getattr__(self, key): |
|
118 | 118 | return getattr(self._sock, key) |
|
119 | 119 | |
|
120 | def __setattr__(self, key, value): | |
|
121 | if key not in ('_sock', '_fail_recv'): | |
|
122 | return setattr(self._sock, key, value) | |
|
123 | return object.__setattr__(self, key, value) | |
|
124 | ||
|
120 | 125 | def recv(self, amt=-1): |
|
121 | 126 | try: |
|
122 | 127 | if self._fail_recv: |
General Comments 0
You need to be logged in to leave comments.
Login now