##// END OF EJS Templates
httpclient: update to revision 892730fe7f46 of httpplus
Augie Fackler -
r16643:24dbef11 default
parent child Browse files
Show More
@@ -0,0 +1,195 b''
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 """Reader objects to abstract out different body response types.
30
31 This module is package-private. It is not expected that these will
32 have any clients outside of httpplus.
33 """
34
35 import httplib
36 import itertools
37 import logging
38
39 logger = logging.getLogger(__name__)
40
41
42 class ReadNotReady(Exception):
43 """Raised when read() is attempted but not enough data is loaded."""
44
45
46 class HTTPRemoteClosedError(httplib.HTTPException):
47 """The server closed the remote socket in the middle of a response."""
48
49
50 class AbstractReader(object):
51 """Abstract base class for response readers.
52
53 Subclasses must implement _load, and should implement _close if
54 it's not an error for the server to close their socket without
55 some termination condition being detected during _load.
56 """
57 def __init__(self):
58 self._finished = False
59 self._done_chunks = []
60
61 @property
62 def available_data(self):
63 return sum(map(len, self._done_chunks))
64
65 def done(self):
66 return self._finished
67
68 def read(self, amt):
69 if self.available_data < amt and not self._finished:
70 raise ReadNotReady()
71 need = [amt]
72 def pred(s):
73 needed = need[0] > 0
74 need[0] -= len(s)
75 return needed
76 blocks = list(itertools.takewhile(pred, self._done_chunks))
77 self._done_chunks = self._done_chunks[len(blocks):]
78 over_read = sum(map(len, blocks)) - amt
79 if over_read > 0 and blocks:
80 logger.debug('need to reinsert %d data into done chunks', over_read)
81 last = blocks[-1]
82 blocks[-1], reinsert = last[:-over_read], last[-over_read:]
83 self._done_chunks.insert(0, reinsert)
84 result = ''.join(blocks)
85 assert len(result) == amt or (self._finished and len(result) < amt)
86 return result
87
88 def _load(self, data): # pragma: no cover
89 """Subclasses must implement this.
90
91 As data is available to be read out of this object, it should
92 be placed into the _done_chunks list. Subclasses should not
93 rely on data remaining in _done_chunks forever, as it may be
94 reaped if the client is parsing data as it comes in.
95 """
96 raise NotImplementedError
97
98 def _close(self):
99 """Default implementation of close.
100
101 The default implementation assumes that the reader will mark
102 the response as finished on the _finished attribute once the
103 entire response body has been read. In the event that this is
104 not true, the subclass should override the implementation of
105 close (for example, close-is-end responses have to set
106 self._finished in the close handler.)
107 """
108 if not self._finished:
109 raise HTTPRemoteClosedError(
110 'server appears to have closed the socket mid-response')
111
112
113 class AbstractSimpleReader(AbstractReader):
114 """Abstract base class for simple readers that require no response decoding.
115
116 Examples of such responses are Connection: Close (close-is-end)
117 and responses that specify a content length.
118 """
119 def _load(self, data):
120 if data:
121 assert not self._finished, (
122 'tried to add data (%r) to a closed reader!' % data)
123 logger.debug('%s read an addtional %d data', self.name, len(data))
124 self._done_chunks.append(data)
125
126
127 class CloseIsEndReader(AbstractSimpleReader):
128 """Reader for responses that specify Connection: Close for length."""
129 name = 'close-is-end'
130
131 def _close(self):
132 logger.info('Marking close-is-end reader as closed.')
133 self._finished = True
134
135
136 class ContentLengthReader(AbstractSimpleReader):
137 """Reader for responses that specify an exact content length."""
138 name = 'content-length'
139
140 def __init__(self, amount):
141 AbstractReader.__init__(self)
142 self._amount = amount
143 if amount == 0:
144 self._finished = True
145 self._amount_seen = 0
146
147 def _load(self, data):
148 AbstractSimpleReader._load(self, data)
149 self._amount_seen += len(data)
150 if self._amount_seen >= self._amount:
151 self._finished = True
152 logger.debug('content-length read complete')
153
154
155 class ChunkedReader(AbstractReader):
156 """Reader for chunked transfer encoding responses."""
157 def __init__(self, eol):
158 AbstractReader.__init__(self)
159 self._eol = eol
160 self._leftover_skip_amt = 0
161 self._leftover_data = ''
162
163 def _load(self, data):
164 assert not self._finished, 'tried to add data to a closed reader!'
165 logger.debug('chunked read an addtional %d data', len(data))
166 position = 0
167 if self._leftover_data:
168 logger.debug('chunked reader trying to finish block from leftover data')
169 # TODO: avoid this string concatenation if possible
170 data = self._leftover_data + data
171 position = self._leftover_skip_amt
172 self._leftover_data = ''
173 self._leftover_skip_amt = 0
174 datalen = len(data)
175 while position < datalen:
176 split = data.find(self._eol, position)
177 if split == -1:
178 self._leftover_data = data
179 self._leftover_skip_amt = position
180 return
181 amt = int(data[position:split], base=16)
182 block_start = split + len(self._eol)
183 # If the whole data chunk plus the eol trailer hasn't
184 # loaded, we'll wait for the next load.
185 if block_start + amt + len(self._eol) > len(data):
186 self._leftover_data = data
187 self._leftover_skip_amt = position
188 return
189 if amt == 0:
190 self._finished = True
191 logger.debug('closing chunked redaer due to chunk of length 0')
192 return
193 self._done_chunks.append(data[block_start:block_start + amt])
194 position = block_start + amt + len(self._eol)
195 # no-check-code
@@ -0,0 +1,70 b''
1 # Copyright 2010, 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
30 import unittest
31
32 from httpplus import _readers
33
34 def chunkedblock(x, eol='\r\n'):
35 r"""Make a chunked transfer-encoding block.
36
37 >>> chunkedblock('hi')
38 '2\r\nhi\r\n'
39 >>> chunkedblock('hi' * 10)
40 '14\r\nhihihihihihihihihihi\r\n'
41 >>> chunkedblock('hi', eol='\n')
42 '2\nhi\n'
43 """
44 return ''.join((hex(len(x))[2:], eol, x, eol))
45
46 corpus = 'foo\r\nbar\r\nbaz\r\n'
47
48
49 class ChunkedReaderTest(unittest.TestCase):
50 def test_many_block_boundaries(self):
51 for step in xrange(1, len(corpus)):
52 data = ''.join(chunkedblock(corpus[start:start+step]) for
53 start in xrange(0, len(corpus), step))
54 for istep in xrange(1, len(data)):
55 rdr = _readers.ChunkedReader('\r\n')
56 print 'step', step, 'load', istep
57 for start in xrange(0, len(data), istep):
58 rdr._load(data[start:start+istep])
59 rdr._load(chunkedblock(''))
60 self.assertEqual(corpus, rdr.read(len(corpus) + 1))
61
62 def test_small_chunk_blocks_large_wire_blocks(self):
63 data = ''.join(map(chunkedblock, corpus)) + chunkedblock('')
64 rdr = _readers.ChunkedReader('\r\n')
65 for start in xrange(0, len(data), 4):
66 d = data[start:start + 4]
67 if d:
68 rdr._load(d)
69 self.assertEqual(corpus, rdr.read(len(corpus)+100))
70 # no-check-code
@@ -45,6 +45,7 b' import rfc822'
45 45 import select
46 46 import socket
47 47
48 import _readers
48 49 import socketutil
49 50
50 51 logger = logging.getLogger(__name__)
@@ -54,8 +55,6 b' logger = logging.getLogger(__name__)'
54 55 HTTP_VER_1_0 = 'HTTP/1.0'
55 56 HTTP_VER_1_1 = 'HTTP/1.1'
56 57
57 _LEN_CLOSE_IS_END = -1
58
59 58 OUTGOING_BUFFER_SIZE = 1 << 15
60 59 INCOMING_BUFFER_SIZE = 1 << 20
61 60
@@ -83,23 +82,19 b' class HTTPResponse(object):'
83 82 The response will continue to load as available. If you need the
84 83 complete response before continuing, check the .complete() method.
85 84 """
86 def __init__(self, sock, timeout):
85 def __init__(self, sock, timeout, method):
87 86 self.sock = sock
87 self.method = method
88 88 self.raw_response = ''
89 self._body = None
90 89 self._headers_len = 0
91 self._content_len = 0
92 90 self.headers = None
93 91 self.will_close = False
94 92 self.status_line = ''
95 93 self.status = None
94 self.continued = False
96 95 self.http_version = None
97 96 self.reason = None
98 self._chunked = False
99 self._chunked_done = False
100 self._chunked_until_next = 0
101 self._chunked_skip_bytes = 0
102 self._chunked_preloaded_block = None
97 self._reader = None
103 98
104 99 self._read_location = 0
105 100 self._eol = EOL
@@ -117,11 +112,12 b' class HTTPResponse(object):'
117 112 socket is closed, this will nearly always return False, even
118 113 in cases where all the data has actually been loaded.
119 114 """
120 if self._chunked:
121 return self._chunked_done
122 if self._content_len == _LEN_CLOSE_IS_END:
123 return False
124 return self._body is not None and len(self._body) >= self._content_len
115 if self._reader:
116 return self._reader.done()
117
118 def _close(self):
119 if self._reader is not None:
120 self._reader._close()
125 121
126 122 def readline(self):
127 123 """Read a single line from the response body.
@@ -129,30 +125,34 b' class HTTPResponse(object):'
129 125 This may block until either a line ending is found or the
130 126 response is complete.
131 127 """
132 eol = self._body.find('\n', self._read_location)
133 while eol == -1 and not self.complete():
128 # TODO: move this into the reader interface where it can be
129 # smarter (and probably avoid copies)
130 bytes = []
131 while not bytes:
132 try:
133 bytes = [self._reader.read(1)]
134 except _readers.ReadNotReady:
135 self._select()
136 while bytes[-1] != '\n' and not self.complete():
134 137 self._select()
135 eol = self._body.find('\n', self._read_location)
136 if eol != -1:
137 eol += 1
138 else:
139 eol = len(self._body)
140 data = self._body[self._read_location:eol]
141 self._read_location = eol
142 return data
138 bytes.append(self._reader.read(1))
139 if bytes[-1] != '\n':
140 next = self._reader.read(1)
141 while next and next != '\n':
142 bytes.append(next)
143 next = self._reader.read(1)
144 bytes.append(next)
145 return ''.join(bytes)
143 146
144 147 def read(self, length=None):
145 148 # if length is None, unbounded read
146 149 while (not self.complete() # never select on a finished read
147 150 and (not length # unbounded, so we wait for complete()
148 or (self._read_location + length) > len(self._body))):
151 or length > self._reader.available_data)):
149 152 self._select()
150 153 if not length:
151 length = len(self._body) - self._read_location
152 elif len(self._body) < (self._read_location + length):
153 length = len(self._body) - self._read_location
154 r = self._body[self._read_location:self._read_location + length]
155 self._read_location += len(r)
154 length = self._reader.available_data
155 r = self._reader.read(length)
156 156 if self.complete() and self.will_close:
157 157 self.sock.close()
158 158 return r
@@ -160,93 +160,35 b' class HTTPResponse(object):'
160 160 def _select(self):
161 161 r, _, _ = select.select([self.sock], [], [], self._timeout)
162 162 if not r:
163 # socket was not readable. If the response is not complete
164 # and we're not a _LEN_CLOSE_IS_END response, raise a timeout.
165 # If we are a _LEN_CLOSE_IS_END response and we have no data,
166 # raise a timeout.
167 if not (self.complete() or
168 (self._content_len == _LEN_CLOSE_IS_END and self._body)):
163 # socket was not readable. If the response is not
164 # complete, raise a timeout.
165 if not self.complete():
169 166 logger.info('timed out with timeout of %s', self._timeout)
170 167 raise HTTPTimeoutException('timeout reading data')
171 logger.info('cl: %r body: %r', self._content_len, self._body)
172 168 try:
173 169 data = self.sock.recv(INCOMING_BUFFER_SIZE)
174 # If the socket was readable and no data was read, that
175 # means the socket was closed. If this isn't a
176 # _CLOSE_IS_END socket, then something is wrong if we're
177 # here (we shouldn't enter _select() if the response is
178 # complete), so abort.
179 if not data and self._content_len != _LEN_CLOSE_IS_END:
180 raise HTTPRemoteClosedError(
181 'server appears to have closed the socket mid-response')
182 170 except socket.sslerror, e:
183 171 if e.args[0] != socket.SSL_ERROR_WANT_READ:
184 172 raise
185 173 logger.debug('SSL_WANT_READ in _select, should retry later')
186 174 return True
187 175 logger.debug('response read %d data during _select', len(data))
176 # If the socket was readable and no data was read, that means
177 # the socket was closed. Inform the reader (if any) so it can
178 # raise an exception if this is an invalid situation.
188 179 if not data:
189 if self.headers and self._content_len == _LEN_CLOSE_IS_END:
190 self._content_len = len(self._body)
180 if self._reader:
181 self._reader._close()
191 182 return False
192 183 else:
193 184 self._load_response(data)
194 185 return True
195 186
196 def _chunked_parsedata(self, data):
197 if self._chunked_preloaded_block:
198 data = self._chunked_preloaded_block + data
199 self._chunked_preloaded_block = None
200 while data:
201 logger.debug('looping with %d data remaining', len(data))
202 # Slice out anything we should skip
203 if self._chunked_skip_bytes:
204 if len(data) <= self._chunked_skip_bytes:
205 self._chunked_skip_bytes -= len(data)
206 data = ''
207 break
208 else:
209 data = data[self._chunked_skip_bytes:]
210 self._chunked_skip_bytes = 0
211
212 # determine how much is until the next chunk
213 if self._chunked_until_next:
214 amt = self._chunked_until_next
215 logger.debug('reading remaining %d of existing chunk', amt)
216 self._chunked_until_next = 0
217 body = data
218 else:
219 try:
220 amt, body = data.split(self._eol, 1)
221 except ValueError:
222 self._chunked_preloaded_block = data
223 logger.debug('saving %r as a preloaded block for chunked',
224 self._chunked_preloaded_block)
225 return
226 amt = int(amt, base=16)
227 logger.debug('reading chunk of length %d', amt)
228 if amt == 0:
229 self._chunked_done = True
230
231 # read through end of what we have or the chunk
232 self._body += body[:amt]
233 if len(body) >= amt:
234 data = body[amt:]
235 self._chunked_skip_bytes = len(self._eol)
236 else:
237 self._chunked_until_next = amt - len(body)
238 self._chunked_skip_bytes = 0
239 data = ''
240
241 187 def _load_response(self, data):
242 if self._chunked:
243 self._chunked_parsedata(data)
244 return
245 elif self._body is not None:
246 self._body += data
247 return
248
249 # We haven't seen end of headers yet
188 # Being here implies we're not at the end of the headers yet,
189 # since at the end of this method if headers were completely
190 # loaded we replace this method with the load() method of the
191 # reader we created.
250 192 self.raw_response += data
251 193 # This is a bogus server with bad line endings
252 194 if self._eol not in self.raw_response:
@@ -270,6 +212,7 b' class HTTPResponse(object):'
270 212 http_ver, status = hdrs.split(' ', 1)
271 213 if status.startswith('100'):
272 214 self.raw_response = body
215 self.continued = True
273 216 logger.debug('continue seen, setting body to %r', body)
274 217 return
275 218
@@ -289,23 +232,46 b' class HTTPResponse(object):'
289 232 if self._eol != EOL:
290 233 hdrs = hdrs.replace(self._eol, '\r\n')
291 234 headers = rfc822.Message(cStringIO.StringIO(hdrs))
235 content_len = None
292 236 if HDR_CONTENT_LENGTH in headers:
293 self._content_len = int(headers[HDR_CONTENT_LENGTH])
237 content_len = int(headers[HDR_CONTENT_LENGTH])
294 238 if self.http_version == HTTP_VER_1_0:
295 239 self.will_close = True
296 240 elif HDR_CONNECTION_CTRL in headers:
297 241 self.will_close = (
298 242 headers[HDR_CONNECTION_CTRL].lower() == CONNECTION_CLOSE)
299 if self._content_len == 0:
300 self._content_len = _LEN_CLOSE_IS_END
301 243 if (HDR_XFER_ENCODING in headers
302 244 and headers[HDR_XFER_ENCODING].lower() == XFER_ENCODING_CHUNKED):
303 self._body = ''
304 self._chunked_parsedata(body)
305 self._chunked = True
306 if self._body is None:
307 self._body = body
245 self._reader = _readers.ChunkedReader(self._eol)
246 logger.debug('using a chunked reader')
247 else:
248 # HEAD responses are forbidden from returning a body, and
249 # it's implausible for a CONNECT response to use
250 # close-is-end logic for an OK response.
251 if (self.method == 'HEAD' or
252 (self.method == 'CONNECT' and content_len is None)):
253 content_len = 0
254 if content_len is not None:
255 logger.debug('using a content-length reader with length %d',
256 content_len)
257 self._reader = _readers.ContentLengthReader(content_len)
258 else:
259 # Response body had no length specified and is not
260 # chunked, so the end of the body will only be
261 # identifiable by the termination of the socket by the
262 # server. My interpretation of the spec means that we
263 # are correct in hitting this case if
264 # transfer-encoding, content-length, and
265 # connection-control were left unspecified.
266 self._reader = _readers.CloseIsEndReader()
267 logger.debug('using a close-is-end reader')
268 self.will_close = True
269
270 if body:
271 self._reader._load(body)
272 logger.debug('headers complete')
308 273 self.headers = headers
274 self._load_response = self._reader._load
309 275
310 276
311 277 class HTTPConnection(object):
@@ -382,13 +348,14 b' class HTTPConnection(object):'
382 348 {}, HTTP_VER_1_0)
383 349 sock.send(data)
384 350 sock.setblocking(0)
385 r = self.response_class(sock, self.timeout)
351 r = self.response_class(sock, self.timeout, 'CONNECT')
386 352 timeout_exc = HTTPTimeoutException(
387 353 'Timed out waiting for CONNECT response from proxy')
388 354 while not r.complete():
389 355 try:
390 356 if not r._select():
391 raise timeout_exc
357 if not r.complete():
358 raise timeout_exc
392 359 except HTTPTimeoutException:
393 360 # This raise/except pattern looks goofy, but
394 361 # _select can raise the timeout as well as the
@@ -527,7 +494,7 b' class HTTPConnection(object):'
527 494 out = outgoing_headers or body
528 495 blocking_on_continue = False
529 496 if expect_continue and not outgoing_headers and not (
530 response and response.headers):
497 response and (response.headers or response.continued)):
531 498 logger.info(
532 499 'waiting up to %s seconds for'
533 500 ' continue response from server',
@@ -550,11 +517,6 b' class HTTPConnection(object):'
550 517 'server, optimistically sending request body')
551 518 else:
552 519 raise HTTPTimeoutException('timeout sending data')
553 # TODO exceptional conditions with select? (what are those be?)
554 # TODO if the response is loading, must we finish sending at all?
555 #
556 # Certainly not if it's going to close the connection and/or
557 # the response is already done...I think.
558 520 was_first = first
559 521
560 522 # incoming data
@@ -572,11 +534,11 b' class HTTPConnection(object):'
572 534 logger.info('socket appears closed in read')
573 535 self.sock = None
574 536 self._current_response = None
537 if response is not None:
538 response._close()
575 539 # This if/elif ladder is a bit subtle,
576 540 # comments in each branch should help.
577 if response is not None and (
578 response.complete() or
579 response._content_len == _LEN_CLOSE_IS_END):
541 if response is not None and response.complete():
580 542 # Server responded completely and then
581 543 # closed the socket. We should just shut
582 544 # things down and let the caller get their
@@ -605,7 +567,7 b' class HTTPConnection(object):'
605 567 'response was missing or incomplete!')
606 568 logger.debug('read %d bytes in request()', len(data))
607 569 if response is None:
608 response = self.response_class(r[0], self.timeout)
570 response = self.response_class(r[0], self.timeout, method)
609 571 response._load_response(data)
610 572 # Jump to the next select() call so we load more
611 573 # data if the server is still sending us content.
@@ -613,10 +575,6 b' class HTTPConnection(object):'
613 575 except socket.error, e:
614 576 if e[0] != errno.EPIPE and not was_first:
615 577 raise
616 if (response._content_len
617 and response._content_len != _LEN_CLOSE_IS_END):
618 outgoing_headers = sent_data + outgoing_headers
619 reconnect('read')
620 578
621 579 # outgoing data
622 580 if w and out:
@@ -661,7 +619,7 b' class HTTPConnection(object):'
661 619 # close if the server response said to or responded before eating
662 620 # the whole request
663 621 if response is None:
664 response = self.response_class(self.sock, self.timeout)
622 response = self.response_class(self.sock, self.timeout, method)
665 623 complete = response.complete()
666 624 data_left = bool(outgoing_headers or body)
667 625 if data_left:
@@ -679,7 +637,8 b' class HTTPConnection(object):'
679 637 raise httplib.ResponseNotReady()
680 638 r = self._current_response
681 639 while r.headers is None:
682 r._select()
640 if not r._select() and not r.complete():
641 raise _readers.HTTPRemoteClosedError()
683 642 if r.will_close:
684 643 self.sock = None
685 644 self._current_response = None
@@ -705,7 +664,7 b' class HTTPProxyConnectFailedException(ht'
705 664 class HTTPStateError(httplib.HTTPException):
706 665 """Invalid internal state encountered."""
707 666
708
709 class HTTPRemoteClosedError(httplib.HTTPException):
710 """The server closed the remote socket in the middle of a response."""
667 # Forward this exception type from _readers since it needs to be part
668 # of the public API.
669 HTTPRemoteClosedError = _readers.HTTPRemoteClosedError
711 670 # no-check-code
@@ -29,7 +29,7 b''
29 29 import socket
30 30 import unittest
31 31
32 import http
32 import httpplus
33 33
34 34 # relative import to ease embedding the library
35 35 import util
@@ -38,7 +38,7 b' import util'
38 38 class SimpleHttpTest(util.HttpTestBase, unittest.TestCase):
39 39
40 40 def _run_simple_test(self, host, server_data, expected_req, expected_data):
41 con = http.HTTPConnection(host)
41 con = httpplus.HTTPConnection(host)
42 42 con._connect()
43 43 con.sock.data = server_data
44 44 con.request('GET', '/')
@@ -47,9 +47,9 b' class SimpleHttpTest(util.HttpTestBase, '
47 47 self.assertEqual(expected_data, con.getresponse().read())
48 48
49 49 def test_broken_data_obj(self):
50 con = http.HTTPConnection('1.2.3.4:80')
50 con = httpplus.HTTPConnection('1.2.3.4:80')
51 51 con._connect()
52 self.assertRaises(http.BadRequestData,
52 self.assertRaises(httpplus.BadRequestData,
53 53 con.request, 'POST', '/', body=1)
54 54
55 55 def test_no_keepalive_http_1_0(self):
@@ -74,7 +74,7 b' store'
74 74 fncache
75 75 dotencode
76 76 """
77 con = http.HTTPConnection('localhost:9999')
77 con = httpplus.HTTPConnection('localhost:9999')
78 78 con._connect()
79 79 con.sock.data = [expected_response_headers, expected_response_body]
80 80 con.request('GET', '/remote/.hg/requires',
@@ -95,7 +95,7 b' dotencode'
95 95 self.assert_(resp.sock.closed)
96 96
97 97 def test_multiline_header(self):
98 con = http.HTTPConnection('1.2.3.4:80')
98 con = httpplus.HTTPConnection('1.2.3.4:80')
99 99 con._connect()
100 100 con.sock.data = ['HTTP/1.1 200 OK\r\n',
101 101 'Server: BogusServer 1.0\r\n',
@@ -122,7 +122,7 b' dotencode'
122 122 self.assertEqual(con.sock.closed, False)
123 123
124 124 def testSimpleRequest(self):
125 con = http.HTTPConnection('1.2.3.4:80')
125 con = httpplus.HTTPConnection('1.2.3.4:80')
126 126 con._connect()
127 127 con.sock.data = ['HTTP/1.1 200 OK\r\n',
128 128 'Server: BogusServer 1.0\r\n',
@@ -149,12 +149,13 b' dotencode'
149 149 resp.headers.getheaders('server'))
150 150
151 151 def testHeaderlessResponse(self):
152 con = http.HTTPConnection('1.2.3.4', use_ssl=False)
152 con = httpplus.HTTPConnection('1.2.3.4', use_ssl=False)
153 153 con._connect()
154 154 con.sock.data = ['HTTP/1.1 200 OK\r\n',
155 155 '\r\n'
156 156 '1234567890'
157 157 ]
158 con.sock.close_on_empty = True
158 159 con.request('GET', '/')
159 160
160 161 expected_req = ('GET / HTTP/1.1\r\n'
@@ -169,7 +170,30 b' dotencode'
169 170 self.assertEqual(resp.status, 200)
170 171
171 172 def testReadline(self):
172 con = http.HTTPConnection('1.2.3.4')
173 con = httpplus.HTTPConnection('1.2.3.4')
174 con._connect()
175 con.sock.data = ['HTTP/1.1 200 OK\r\n',
176 'Server: BogusServer 1.0\r\n',
177 'Connection: Close\r\n',
178 '\r\n'
179 '1\n2\nabcdefg\n4\n5']
180 con.sock.close_on_empty = True
181
182 expected_req = ('GET / HTTP/1.1\r\n'
183 'Host: 1.2.3.4\r\n'
184 'accept-encoding: identity\r\n\r\n')
185
186 con.request('GET', '/')
187 self.assertEqual(('1.2.3.4', 80), con.sock.sa)
188 self.assertEqual(expected_req, con.sock.sent)
189 r = con.getresponse()
190 for expected in ['1\n', '2\n', 'abcdefg\n', '4\n', '5']:
191 actual = r.readline()
192 self.assertEqual(expected, actual,
193 'Expected %r, got %r' % (expected, actual))
194
195 def testReadlineTrickle(self):
196 con = httpplus.HTTPConnection('1.2.3.4')
173 197 con._connect()
174 198 # make sure it trickles in one byte at a time
175 199 # so that we touch all the cases in readline
@@ -179,6 +203,7 b' dotencode'
179 203 'Connection: Close\r\n',
180 204 '\r\n'
181 205 '1\n2\nabcdefg\n4\n5']))
206 con.sock.close_on_empty = True
182 207
183 208 expected_req = ('GET / HTTP/1.1\r\n'
184 209 'Host: 1.2.3.4\r\n'
@@ -193,6 +218,59 b' dotencode'
193 218 self.assertEqual(expected, actual,
194 219 'Expected %r, got %r' % (expected, actual))
195 220
221 def testVariousReads(self):
222 con = httpplus.HTTPConnection('1.2.3.4')
223 con._connect()
224 # make sure it trickles in one byte at a time
225 # so that we touch all the cases in readline
226 con.sock.data = list(''.join(
227 ['HTTP/1.1 200 OK\r\n',
228 'Server: BogusServer 1.0\r\n',
229 'Connection: Close\r\n',
230 '\r\n'
231 '1\n2',
232 '\na', 'bc',
233 'defg\n4\n5']))
234 con.sock.close_on_empty = True
235
236 expected_req = ('GET / HTTP/1.1\r\n'
237 'Host: 1.2.3.4\r\n'
238 'accept-encoding: identity\r\n\r\n')
239
240 con.request('GET', '/')
241 self.assertEqual(('1.2.3.4', 80), con.sock.sa)
242 self.assertEqual(expected_req, con.sock.sent)
243 r = con.getresponse()
244 for read_amt, expect in [(1, '1'), (1, '\n'),
245 (4, '2\nab'),
246 ('line', 'cdefg\n'),
247 (None, '4\n5')]:
248 if read_amt == 'line':
249 self.assertEqual(expect, r.readline())
250 else:
251 self.assertEqual(expect, r.read(read_amt))
252
253 def testZeroLengthBody(self):
254 con = httpplus.HTTPConnection('1.2.3.4')
255 con._connect()
256 # make sure it trickles in one byte at a time
257 # so that we touch all the cases in readline
258 con.sock.data = list(''.join(
259 ['HTTP/1.1 200 OK\r\n',
260 'Server: BogusServer 1.0\r\n',
261 'Content-length: 0\r\n',
262 '\r\n']))
263
264 expected_req = ('GET / HTTP/1.1\r\n'
265 'Host: 1.2.3.4\r\n'
266 'accept-encoding: identity\r\n\r\n')
267
268 con.request('GET', '/')
269 self.assertEqual(('1.2.3.4', 80), con.sock.sa)
270 self.assertEqual(expected_req, con.sock.sent)
271 r = con.getresponse()
272 self.assertEqual('', r.read())
273
196 274 def testIPv6(self):
197 275 self._run_simple_test('[::1]:8221',
198 276 ['HTTP/1.1 200 OK\r\n',
@@ -226,7 +304,7 b' dotencode'
226 304 '1234567890')
227 305
228 306 def testEarlyContinueResponse(self):
229 con = http.HTTPConnection('1.2.3.4:80')
307 con = httpplus.HTTPConnection('1.2.3.4:80')
230 308 con._connect()
231 309 sock = con.sock
232 310 sock.data = ['HTTP/1.1 403 Forbidden\r\n',
@@ -240,8 +318,23 b' dotencode'
240 318 self.assertEqual("You can't do that.", con.getresponse().read())
241 319 self.assertEqual(sock.closed, True)
242 320
321 def testEarlyContinueResponseNoContentLength(self):
322 con = httpplus.HTTPConnection('1.2.3.4:80')
323 con._connect()
324 sock = con.sock
325 sock.data = ['HTTP/1.1 403 Forbidden\r\n',
326 'Server: BogusServer 1.0\r\n',
327 '\r\n'
328 "You can't do that."]
329 sock.close_on_empty = True
330 expected_req = self.doPost(con, expect_body=False)
331 self.assertEqual(('1.2.3.4', 80), sock.sa)
332 self.assertStringEqual(expected_req, sock.sent)
333 self.assertEqual("You can't do that.", con.getresponse().read())
334 self.assertEqual(sock.closed, True)
335
243 336 def testDeniedAfterContinueTimeoutExpires(self):
244 con = http.HTTPConnection('1.2.3.4:80')
337 con = httpplus.HTTPConnection('1.2.3.4:80')
245 338 con._connect()
246 339 sock = con.sock
247 340 sock.data = ['HTTP/1.1 403 Forbidden\r\n',
@@ -269,7 +362,7 b' dotencode'
269 362 self.assertEqual(sock.closed, True)
270 363
271 364 def testPostData(self):
272 con = http.HTTPConnection('1.2.3.4:80')
365 con = httpplus.HTTPConnection('1.2.3.4:80')
273 366 con._connect()
274 367 sock = con.sock
275 368 sock.read_wait_sentinel = 'POST data'
@@ -286,7 +379,7 b' dotencode'
286 379 self.assertEqual(sock.closed, False)
287 380
288 381 def testServerWithoutContinue(self):
289 con = http.HTTPConnection('1.2.3.4:80')
382 con = httpplus.HTTPConnection('1.2.3.4:80')
290 383 con._connect()
291 384 sock = con.sock
292 385 sock.read_wait_sentinel = 'POST data'
@@ -302,7 +395,7 b' dotencode'
302 395 self.assertEqual(sock.closed, False)
303 396
304 397 def testServerWithSlowContinue(self):
305 con = http.HTTPConnection('1.2.3.4:80')
398 con = httpplus.HTTPConnection('1.2.3.4:80')
306 399 con._connect()
307 400 sock = con.sock
308 401 sock.read_wait_sentinel = 'POST data'
@@ -321,7 +414,7 b' dotencode'
321 414 self.assertEqual(sock.closed, False)
322 415
323 416 def testSlowConnection(self):
324 con = http.HTTPConnection('1.2.3.4:80')
417 con = httpplus.HTTPConnection('1.2.3.4:80')
325 418 con._connect()
326 419 # simulate one byte arriving at a time, to check for various
327 420 # corner cases
@@ -340,12 +433,26 b' dotencode'
340 433 self.assertEqual(expected_req, con.sock.sent)
341 434 self.assertEqual('1234567890', con.getresponse().read())
342 435
436 def testCloseAfterNotAllOfHeaders(self):
437 con = httpplus.HTTPConnection('1.2.3.4:80')
438 con._connect()
439 con.sock.data = ['HTTP/1.1 200 OK\r\n',
440 'Server: NO CARRIER']
441 con.sock.close_on_empty = True
442 con.request('GET', '/')
443 self.assertRaises(httpplus.HTTPRemoteClosedError,
444 con.getresponse)
445
446 expected_req = ('GET / HTTP/1.1\r\n'
447 'Host: 1.2.3.4\r\n'
448 'accept-encoding: identity\r\n\r\n')
449
343 450 def testTimeout(self):
344 con = http.HTTPConnection('1.2.3.4:80')
451 con = httpplus.HTTPConnection('1.2.3.4:80')
345 452 con._connect()
346 453 con.sock.data = []
347 454 con.request('GET', '/')
348 self.assertRaises(http.HTTPTimeoutException,
455 self.assertRaises(httpplus.HTTPTimeoutException,
349 456 con.getresponse)
350 457
351 458 expected_req = ('GET / HTTP/1.1\r\n'
@@ -370,7 +477,7 b' dotencode'
370 477 return s
371 478
372 479 socket.socket = closingsocket
373 con = http.HTTPConnection('1.2.3.4:80')
480 con = httpplus.HTTPConnection('1.2.3.4:80')
374 481 con._connect()
375 482 con.request('GET', '/')
376 483 r1 = con.getresponse()
@@ -381,7 +488,7 b' dotencode'
381 488 self.assertEqual(2, len(sockets))
382 489
383 490 def test_server_closes_before_end_of_body(self):
384 con = http.HTTPConnection('1.2.3.4:80')
491 con = httpplus.HTTPConnection('1.2.3.4:80')
385 492 con._connect()
386 493 s = con.sock
387 494 s.data = ['HTTP/1.1 200 OK\r\n',
@@ -393,9 +500,9 b' dotencode'
393 500 s.close_on_empty = True
394 501 con.request('GET', '/')
395 502 r1 = con.getresponse()
396 self.assertRaises(http.HTTPRemoteClosedError, r1.read)
503 self.assertRaises(httpplus.HTTPRemoteClosedError, r1.read)
397 504
398 505 def test_no_response_raises_response_not_ready(self):
399 con = http.HTTPConnection('foo')
400 self.assertRaises(http.httplib.ResponseNotReady, con.getresponse)
506 con = httpplus.HTTPConnection('foo')
507 self.assertRaises(httpplus.httplib.ResponseNotReady, con.getresponse)
401 508 # no-check-code
@@ -34,7 +34,7 b' against that potential insanit.y'
34 34 """
35 35 import unittest
36 36
37 import http
37 import httpplus
38 38
39 39 # relative import to ease embedding the library
40 40 import util
@@ -43,7 +43,7 b' import util'
43 43 class SimpleHttpTest(util.HttpTestBase, unittest.TestCase):
44 44
45 45 def bogusEOL(self, eol):
46 con = http.HTTPConnection('1.2.3.4:80')
46 con = httpplus.HTTPConnection('1.2.3.4:80')
47 47 con._connect()
48 48 con.sock.data = ['HTTP/1.1 200 OK%s' % eol,
49 49 'Server: BogusServer 1.0%s' % eol,
@@ -29,7 +29,7 b''
29 29 import cStringIO
30 30 import unittest
31 31
32 import http
32 import httpplus
33 33
34 34 # relative import to ease embedding the library
35 35 import util
@@ -50,7 +50,7 b" def chunkedblock(x, eol='\\r\\n'):"
50 50
51 51 class ChunkedTransferTest(util.HttpTestBase, unittest.TestCase):
52 52 def testChunkedUpload(self):
53 con = http.HTTPConnection('1.2.3.4:80')
53 con = httpplus.HTTPConnection('1.2.3.4:80')
54 54 con._connect()
55 55 sock = con.sock
56 56 sock.read_wait_sentinel = '0\r\n\r\n'
@@ -77,7 +77,7 b' class ChunkedTransferTest(util.HttpTestB'
77 77 self.assertEqual(sock.closed, False)
78 78
79 79 def testChunkedDownload(self):
80 con = http.HTTPConnection('1.2.3.4:80')
80 con = httpplus.HTTPConnection('1.2.3.4:80')
81 81 con._connect()
82 82 sock = con.sock
83 83 sock.data = ['HTTP/1.1 200 OK\r\n',
@@ -85,14 +85,31 b' class ChunkedTransferTest(util.HttpTestB'
85 85 'transfer-encoding: chunked',
86 86 '\r\n\r\n',
87 87 chunkedblock('hi '),
88 chunkedblock('there'),
88 ] + list(chunkedblock('there')) + [
89 89 chunkedblock(''),
90 90 ]
91 91 con.request('GET', '/')
92 92 self.assertStringEqual('hi there', con.getresponse().read())
93 93
94 def testChunkedDownloadOddReadBoundaries(self):
95 con = httpplus.HTTPConnection('1.2.3.4:80')
96 con._connect()
97 sock = con.sock
98 sock.data = ['HTTP/1.1 200 OK\r\n',
99 'Server: BogusServer 1.0\r\n',
100 'transfer-encoding: chunked',
101 '\r\n\r\n',
102 chunkedblock('hi '),
103 ] + list(chunkedblock('there')) + [
104 chunkedblock(''),
105 ]
106 con.request('GET', '/')
107 resp = con.getresponse()
108 for amt, expect in [(1, 'h'), (5, 'i the'), (100, 're')]:
109 self.assertEqual(expect, resp.read(amt))
110
94 111 def testChunkedDownloadBadEOL(self):
95 con = http.HTTPConnection('1.2.3.4:80')
112 con = httpplus.HTTPConnection('1.2.3.4:80')
96 113 con._connect()
97 114 sock = con.sock
98 115 sock.data = ['HTTP/1.1 200 OK\n',
@@ -107,7 +124,7 b' class ChunkedTransferTest(util.HttpTestB'
107 124 self.assertStringEqual('hi there', con.getresponse().read())
108 125
109 126 def testChunkedDownloadPartialChunkBadEOL(self):
110 con = http.HTTPConnection('1.2.3.4:80')
127 con = httpplus.HTTPConnection('1.2.3.4:80')
111 128 con._connect()
112 129 sock = con.sock
113 130 sock.data = ['HTTP/1.1 200 OK\n',
@@ -122,7 +139,7 b' class ChunkedTransferTest(util.HttpTestB'
122 139 con.getresponse().read())
123 140
124 141 def testChunkedDownloadPartialChunk(self):
125 con = http.HTTPConnection('1.2.3.4:80')
142 con = httpplus.HTTPConnection('1.2.3.4:80')
126 143 con._connect()
127 144 sock = con.sock
128 145 sock.data = ['HTTP/1.1 200 OK\r\n',
@@ -136,7 +153,7 b' class ChunkedTransferTest(util.HttpTestB'
136 153 con.getresponse().read())
137 154
138 155 def testChunkedDownloadEarlyHangup(self):
139 con = http.HTTPConnection('1.2.3.4:80')
156 con = httpplus.HTTPConnection('1.2.3.4:80')
140 157 con._connect()
141 158 sock = con.sock
142 159 broken = chunkedblock('hi'*20)[:-1]
@@ -149,5 +166,5 b' class ChunkedTransferTest(util.HttpTestB'
149 166 sock.close_on_empty = True
150 167 con.request('GET', '/')
151 168 resp = con.getresponse()
152 self.assertRaises(http.HTTPRemoteClosedError, resp.read)
169 self.assertRaises(httpplus.HTTPRemoteClosedError, resp.read)
153 170 # no-check-code
@@ -29,13 +29,13 b''
29 29 import unittest
30 30 import socket
31 31
32 import http
32 import httpplus
33 33
34 34 # relative import to ease embedding the library
35 35 import util
36 36
37 37
38 def make_preloaded_socket(data):
38 def make_preloaded_socket(data, close=False):
39 39 """Make a socket pre-loaded with data so it can be read during connect.
40 40
41 41 Useful for https proxy tests because we have to read from the
@@ -44,6 +44,7 b' def make_preloaded_socket(data):'
44 44 def s(*args, **kwargs):
45 45 sock = util.MockSocket(*args, **kwargs)
46 46 sock.early_data = data[:]
47 sock.close_on_empty = close
47 48 return sock
48 49 return s
49 50
@@ -51,7 +52,7 b' def make_preloaded_socket(data):'
51 52 class ProxyHttpTest(util.HttpTestBase, unittest.TestCase):
52 53
53 54 def _run_simple_test(self, host, server_data, expected_req, expected_data):
54 con = http.HTTPConnection(host)
55 con = httpplus.HTTPConnection(host)
55 56 con._connect()
56 57 con.sock.data = server_data
57 58 con.request('GET', '/')
@@ -60,7 +61,7 b' class ProxyHttpTest(util.HttpTestBase, u'
60 61 self.assertEqual(expected_data, con.getresponse().read())
61 62
62 63 def testSimpleRequest(self):
63 con = http.HTTPConnection('1.2.3.4:80',
64 con = httpplus.HTTPConnection('1.2.3.4:80',
64 65 proxy_hostport=('magicproxy', 4242))
65 66 con._connect()
66 67 con.sock.data = ['HTTP/1.1 200 OK\r\n',
@@ -88,7 +89,7 b' class ProxyHttpTest(util.HttpTestBase, u'
88 89 resp.headers.getheaders('server'))
89 90
90 91 def testSSLRequest(self):
91 con = http.HTTPConnection('1.2.3.4:443',
92 con = httpplus.HTTPConnection('1.2.3.4:443',
92 93 proxy_hostport=('magicproxy', 4242))
93 94 socket.socket = make_preloaded_socket(
94 95 ['HTTP/1.1 200 OK\r\n',
@@ -124,12 +125,47 b' class ProxyHttpTest(util.HttpTestBase, u'
124 125 self.assertEqual(['BogusServer 1.0'],
125 126 resp.headers.getheaders('server'))
126 127
127 def testSSLProxyFailure(self):
128 con = http.HTTPConnection('1.2.3.4:443',
128 def testSSLRequestNoConnectBody(self):
129 con = httpplus.HTTPConnection('1.2.3.4:443',
129 130 proxy_hostport=('magicproxy', 4242))
130 131 socket.socket = make_preloaded_socket(
131 ['HTTP/1.1 407 Proxy Authentication Required\r\n\r\n'])
132 self.assertRaises(http.HTTPProxyConnectFailedException, con._connect)
133 self.assertRaises(http.HTTPProxyConnectFailedException,
132 ['HTTP/1.1 200 OK\r\n',
133 'Server: BogusServer 1.0\r\n',
134 '\r\n'])
135 con._connect()
136 con.sock.data = ['HTTP/1.1 200 OK\r\n',
137 'Server: BogusServer 1.0\r\n',
138 'Content-Length: 10\r\n',
139 '\r\n'
140 '1234567890'
141 ]
142 connect_sent = con.sock.sent
143 con.sock.sent = ''
144 con.request('GET', '/')
145
146 expected_connect = ('CONNECT 1.2.3.4:443 HTTP/1.0\r\n'
147 'Host: 1.2.3.4\r\n'
148 'accept-encoding: identity\r\n'
149 '\r\n')
150 expected_request = ('GET / HTTP/1.1\r\n'
151 'Host: 1.2.3.4\r\n'
152 'accept-encoding: identity\r\n\r\n')
153
154 self.assertEqual(('127.0.0.42', 4242), con.sock.sa)
155 self.assertStringEqual(expected_connect, connect_sent)
156 self.assertStringEqual(expected_request, con.sock.sent)
157 resp = con.getresponse()
158 self.assertEqual(resp.status, 200)
159 self.assertEqual('1234567890', resp.read())
160 self.assertEqual(['BogusServer 1.0'],
161 resp.headers.getheaders('server'))
162
163 def testSSLProxyFailure(self):
164 con = httpplus.HTTPConnection('1.2.3.4:443',
165 proxy_hostport=('magicproxy', 4242))
166 socket.socket = make_preloaded_socket(
167 ['HTTP/1.1 407 Proxy Authentication Required\r\n\r\n'], close=True)
168 self.assertRaises(httpplus.HTTPProxyConnectFailedException, con._connect)
169 self.assertRaises(httpplus.HTTPProxyConnectFailedException,
134 170 con.request, 'GET', '/')
135 171 # no-check-code
@@ -28,7 +28,7 b''
28 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 29 import unittest
30 30
31 import http
31 import httpplus
32 32
33 33 # relative import to ease embedding the library
34 34 import util
@@ -37,7 +37,7 b' import util'
37 37
38 38 class HttpSslTest(util.HttpTestBase, unittest.TestCase):
39 39 def testSslRereadRequired(self):
40 con = http.HTTPConnection('1.2.3.4:443')
40 con = httpplus.HTTPConnection('1.2.3.4:443')
41 41 con._connect()
42 42 # extend the list instead of assign because of how
43 43 # MockSSLSocket works.
@@ -66,7 +66,7 b' class HttpSslTest(util.HttpTestBase, uni'
66 66 resp.headers.getheaders('server'))
67 67
68 68 def testSslRereadInEarlyResponse(self):
69 con = http.HTTPConnection('1.2.3.4:443')
69 con = httpplus.HTTPConnection('1.2.3.4:443')
70 70 con._connect()
71 71 con.sock.early_data = ['HTTP/1.1 200 OK\r\n',
72 72 'Server: BogusServer 1.0\r\n',
@@ -29,7 +29,7 b''
29 29 import difflib
30 30 import socket
31 31
32 import http
32 import httpplus
33 33
34 34
35 35 class MockSocket(object):
@@ -57,7 +57,7 b' class MockSocket(object):'
57 57 self.remote_closed = self.closed = False
58 58 self.close_on_empty = False
59 59 self.sent = ''
60 self.read_wait_sentinel = http._END_HEADERS
60 self.read_wait_sentinel = httpplus._END_HEADERS
61 61
62 62 def close(self):
63 63 self.closed = True
@@ -86,7 +86,7 b' class MockSocket(object):'
86 86
87 87 @property
88 88 def ready_for_read(self):
89 return ((self.early_data and http._END_HEADERS in self.sent)
89 return ((self.early_data and httpplus._END_HEADERS in self.sent)
90 90 or (self.read_wait_sentinel in self.sent and self.data)
91 91 or self.closed or self.remote_closed)
92 92
@@ -132,7 +132,7 b' class MockSSLSocket(object):'
132 132
133 133
134 134 def mocksslwrap(sock, keyfile=None, certfile=None,
135 server_side=False, cert_reqs=http.socketutil.CERT_NONE,
135 server_side=False, cert_reqs=httpplus.socketutil.CERT_NONE,
136 136 ssl_version=None, ca_certs=None,
137 137 do_handshake_on_connect=True,
138 138 suppress_ragged_eofs=True):
@@ -156,16 +156,16 b' class HttpTestBase(object):'
156 156 self.orig_getaddrinfo = socket.getaddrinfo
157 157 socket.getaddrinfo = mockgetaddrinfo
158 158
159 self.orig_select = http.select.select
160 http.select.select = mockselect
159 self.orig_select = httpplus.select.select
160 httpplus.select.select = mockselect
161 161
162 self.orig_sslwrap = http.socketutil.wrap_socket
163 http.socketutil.wrap_socket = mocksslwrap
162 self.orig_sslwrap = httpplus.socketutil.wrap_socket
163 httpplus.socketutil.wrap_socket = mocksslwrap
164 164
165 165 def tearDown(self):
166 166 socket.socket = self.orig_socket
167 http.select.select = self.orig_select
168 http.socketutil.wrap_socket = self.orig_sslwrap
167 httpplus.select.select = self.orig_select
168 httpplus.socketutil.wrap_socket = self.orig_sslwrap
169 169 socket.getaddrinfo = self.orig_getaddrinfo
170 170
171 171 def assertStringEqual(self, l, r):
General Comments 0
You need to be logged in to leave comments. Login now