Show More
@@ -0,0 +1,94 | |||||
|
1 | # Copyright 2011, Google Inc. | |||
|
2 | # All rights reserved. | |||
|
3 | # | |||
|
4 | # Redistribution and use in source and binary forms, with or without | |||
|
5 | # modification, are permitted provided that the following conditions are | |||
|
6 | # met: | |||
|
7 | # | |||
|
8 | # * Redistributions of source code must retain the above copyright | |||
|
9 | # notice, this list of conditions and the following disclaimer. | |||
|
10 | # * Redistributions in binary form must reproduce the above | |||
|
11 | # copyright notice, this list of conditions and the following disclaimer | |||
|
12 | # in the documentation and/or other materials provided with the | |||
|
13 | # distribution. | |||
|
14 | # * Neither the name of Google Inc. nor the names of its | |||
|
15 | # contributors may be used to endorse or promote products derived from | |||
|
16 | # this software without specific prior written permission. | |||
|
17 | ||||
|
18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
|
19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
|
20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
|
21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
|
22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
|
23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
|
24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
|
25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
|
26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
|
27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
|
28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
|
29 | import unittest | |||
|
30 | ||||
|
31 | import http | |||
|
32 | ||||
|
33 | # relative import to ease embedding the library | |||
|
34 | import util | |||
|
35 | ||||
|
36 | ||||
|
37 | ||||
|
38 | class HttpSslTest(util.HttpTestBase, unittest.TestCase): | |||
|
39 | def testSslRereadRequired(self): | |||
|
40 | con = http.HTTPConnection('1.2.3.4:443') | |||
|
41 | con._connect() | |||
|
42 | # extend the list instead of assign because of how | |||
|
43 | # MockSSLSocket works. | |||
|
44 | con.sock.data.extend(['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 | con.request('GET', '/') | |||
|
54 | ||||
|
55 | expected_req = ('GET / HTTP/1.1\r\n' | |||
|
56 | 'Host: 1.2.3.4\r\n' | |||
|
57 | 'accept-encoding: identity\r\n\r\n') | |||
|
58 | ||||
|
59 | self.assertEqual(('1.2.3.4', 443), con.sock.sa) | |||
|
60 | self.assertEqual(expected_req, con.sock.sent) | |||
|
61 | resp = con.getresponse() | |||
|
62 | self.assertEqual('1234567890', resp.read()) | |||
|
63 | self.assertEqual(['Value', 'Other Value', 'One More!'], | |||
|
64 | resp.headers.getheaders('multiheader')) | |||
|
65 | self.assertEqual(['BogusServer 1.0'], | |||
|
66 | resp.headers.getheaders('server')) | |||
|
67 | ||||
|
68 | def testSslRereadInEarlyResponse(self): | |||
|
69 | con = http.HTTPConnection('1.2.3.4:443') | |||
|
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 | 'Server: BogusServer 1.0\r\n', | |||
|
75 | 'MultiHeader: Value\r\n' | |||
|
76 | 'MultiHeader: Other Value\r\n' | |||
|
77 | 'MultiHeader: One More!\r\n' | |||
|
78 | 'Content-Length: 10\r\n', | |||
|
79 | '\r\n' | |||
|
80 | '1234567890' | |||
|
81 | ]) | |||
|
82 | ||||
|
83 | expected_req = self.doPost(con, False) | |||
|
84 | self.assertEqual(None, con.sock, | |||
|
85 | 'Connection should have disowned socket') | |||
|
86 | ||||
|
87 | resp = con.getresponse() | |||
|
88 | self.assertEqual(('1.2.3.4', 443), resp.sock.sa) | |||
|
89 | self.assertEqual(expected_req, resp.sock.sent) | |||
|
90 | self.assertEqual('1234567890', resp.read()) | |||
|
91 | self.assertEqual(['Value', 'Other Value', 'One More!'], | |||
|
92 | resp.headers.getheaders('multiheader')) | |||
|
93 | self.assertEqual(['BogusServer 1.0'], | |||
|
94 | resp.headers.getheaders('server')) |
@@ -165,7 +165,13 class HTTPResponse(object): | |||||
165 | logger.info('timed out with timeout of %s', self._timeout) |
|
165 | logger.info('timed out with timeout of %s', self._timeout) | |
166 | raise HTTPTimeoutException('timeout reading data') |
|
166 | raise HTTPTimeoutException('timeout reading data') | |
167 | logger.info('cl: %r body: %r', self._content_len, self._body) |
|
167 | logger.info('cl: %r body: %r', self._content_len, self._body) | |
|
168 | try: | |||
168 | data = self.sock.recv(INCOMING_BUFFER_SIZE) |
|
169 | data = self.sock.recv(INCOMING_BUFFER_SIZE) | |
|
170 | except socket.sslerror, e: | |||
|
171 | if e.args[0] != socket.SSL_ERROR_WANT_READ: | |||
|
172 | raise | |||
|
173 | logger.debug('SSL_WANT_READ in _select, should retry later') | |||
|
174 | return True | |||
169 | logger.debug('response read %d data during _select', len(data)) |
|
175 | logger.debug('response read %d data during _select', len(data)) | |
170 | if not data: |
|
176 | if not data: | |
171 | if not self.headers: |
|
177 | if not self.headers: | |
@@ -545,7 +551,14 class HTTPConnection(object): | |||||
545 | # incoming data |
|
551 | # incoming data | |
546 | if r: |
|
552 | if r: | |
547 | try: |
|
553 | try: | |
|
554 | try: | |||
548 | data = r[0].recv(INCOMING_BUFFER_SIZE) |
|
555 | data = r[0].recv(INCOMING_BUFFER_SIZE) | |
|
556 | except socket.sslerror, e: | |||
|
557 | if e.args[0] != socket.SSL_ERROR_WANT_READ: | |||
|
558 | raise | |||
|
559 | logger.debug( | |||
|
560 | 'SSL_WANT_READ while sending data, retrying...') | |||
|
561 | continue | |||
549 | if not data: |
|
562 | if not data: | |
550 | logger.info('socket appears closed in read') |
|
563 | logger.info('socket appears closed in read') | |
551 | outgoing_headers = body = None |
|
564 | outgoing_headers = body = None |
@@ -39,7 +39,7 class SimpleHttpTest(util.HttpTestBase, | |||||
39 | def _run_simple_test(self, host, server_data, expected_req, expected_data): |
|
39 | def _run_simple_test(self, host, server_data, expected_req, expected_data): | |
40 | con = http.HTTPConnection(host) |
|
40 | con = http.HTTPConnection(host) | |
41 | con._connect() |
|
41 | con._connect() | |
42 |
con.sock.data |
|
42 | con.sock.data.extend(server_data) | |
43 | con.request('GET', '/') |
|
43 | con.request('GET', '/') | |
44 |
|
44 | |||
45 | self.assertStringEqual(expected_req, con.sock.sent) |
|
45 | self.assertStringEqual(expected_req, con.sock.sent) | |
@@ -224,19 +224,6 dotencode | |||||
224 | 'accept-encoding: identity\r\n\r\n'), |
|
224 | 'accept-encoding: identity\r\n\r\n'), | |
225 | '1234567890') |
|
225 | '1234567890') | |
226 |
|
226 | |||
227 | def doPost(self, con, expect_body, body_to_send='This is some POST data'): |
|
|||
228 | con.request('POST', '/', body=body_to_send, |
|
|||
229 | expect_continue=True) |
|
|||
230 | expected_req = ('POST / HTTP/1.1\r\n' |
|
|||
231 | 'Host: 1.2.3.4\r\n' |
|
|||
232 | 'content-length: %d\r\n' |
|
|||
233 | 'Expect: 100-Continue\r\n' |
|
|||
234 | 'accept-encoding: identity\r\n\r\n' % |
|
|||
235 | len(body_to_send)) |
|
|||
236 | if expect_body: |
|
|||
237 | expected_req += body_to_send |
|
|||
238 | return expected_req |
|
|||
239 |
|
||||
240 | def testEarlyContinueResponse(self): |
|
227 | def testEarlyContinueResponse(self): | |
241 | con = http.HTTPConnection('1.2.3.4:80') |
|
228 | con = http.HTTPConnection('1.2.3.4:80') | |
242 | con._connect() |
|
229 | con._connect() |
@@ -97,12 +97,12 class ProxyHttpTest(util.HttpTestBase, u | |||||
97 | '\r\n' |
|
97 | '\r\n' | |
98 | '1234567890']) |
|
98 | '1234567890']) | |
99 | con._connect() |
|
99 | con._connect() | |
100 |
con.sock.data |
|
100 | con.sock.data.extend(['HTTP/1.1 200 OK\r\n', | |
101 | 'Server: BogusServer 1.0\r\n', |
|
101 | 'Server: BogusServer 1.0\r\n', | |
102 | 'Content-Length: 10\r\n', |
|
102 | 'Content-Length: 10\r\n', | |
103 | '\r\n' |
|
103 | '\r\n' | |
104 | '1234567890' |
|
104 | '1234567890' | |
105 | ] |
|
105 | ]) | |
106 | con.request('GET', '/') |
|
106 | con.request('GET', '/') | |
107 |
|
107 | |||
108 | expected_req = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' |
|
108 | expected_req = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n' |
@@ -109,12 +109,29 def mockselect(r, w, x, timeout=0): | |||||
109 | return readable, w[:], [] |
|
109 | return readable, w[:], [] | |
110 |
|
110 | |||
111 |
|
111 | |||
|
112 | class MockSSLSocket(object): | |||
|
113 | def __init__(self, sock): | |||
|
114 | self._sock = sock | |||
|
115 | self._fail_recv = True | |||
|
116 | ||||
|
117 | def __getattr__(self, key): | |||
|
118 | return getattr(self._sock, key) | |||
|
119 | ||||
|
120 | def recv(self, amt=-1): | |||
|
121 | try: | |||
|
122 | if self._fail_recv: | |||
|
123 | raise socket.sslerror(socket.SSL_ERROR_WANT_READ) | |||
|
124 | return self._sock.recv(amt=amt) | |||
|
125 | finally: | |||
|
126 | self._fail_recv = not self._fail_recv | |||
|
127 | ||||
|
128 | ||||
112 | def mocksslwrap(sock, keyfile=None, certfile=None, |
|
129 | def mocksslwrap(sock, keyfile=None, certfile=None, | |
113 | server_side=False, cert_reqs=http.socketutil.CERT_NONE, |
|
130 | server_side=False, cert_reqs=http.socketutil.CERT_NONE, | |
114 | ssl_version=http.socketutil.PROTOCOL_SSLv23, ca_certs=None, |
|
131 | ssl_version=http.socketutil.PROTOCOL_SSLv23, ca_certs=None, | |
115 | do_handshake_on_connect=True, |
|
132 | do_handshake_on_connect=True, | |
116 | suppress_ragged_eofs=True): |
|
133 | suppress_ragged_eofs=True): | |
117 | return sock |
|
134 | return MockSSLSocket(sock) | |
118 |
|
135 | |||
119 |
|
136 | |||
120 | def mockgetaddrinfo(host, port, unused, streamtype): |
|
137 | def mockgetaddrinfo(host, port, unused, streamtype): | |
@@ -157,4 +174,17 class HttpTestBase(object): | |||||
157 | add_nl(l.splitlines()), add_nl(r.splitlines()), |
|
174 | add_nl(l.splitlines()), add_nl(r.splitlines()), | |
158 | fromfile='expected', tofile='got')) |
|
175 | fromfile='expected', tofile='got')) | |
159 | raise |
|
176 | raise | |
|
177 | ||||
|
178 | def doPost(self, con, expect_body, body_to_send='This is some POST data'): | |||
|
179 | con.request('POST', '/', body=body_to_send, | |||
|
180 | expect_continue=True) | |||
|
181 | expected_req = ('POST / HTTP/1.1\r\n' | |||
|
182 | 'Host: 1.2.3.4\r\n' | |||
|
183 | 'content-length: %d\r\n' | |||
|
184 | 'Expect: 100-Continue\r\n' | |||
|
185 | 'accept-encoding: identity\r\n\r\n' % | |||
|
186 | len(body_to_send)) | |||
|
187 | if expect_body: | |||
|
188 | expected_req += body_to_send | |||
|
189 | return expected_req | |||
160 | # no-check-code |
|
190 | # no-check-code |
General Comments 0
You need to be logged in to leave comments.
Login now