comparison mercurial/httpclient/_readers.py @ 19182:fae47ecaa952

httpclient: upgrade to fe8c09e4db64 of httpplus
author Augie Fackler <raf@durin42.com>
date Sat, 11 May 2013 20:25:15 -0500
parents 36733ab7fa05
children 1ad9da968a2e
comparison
equal deleted inserted replaced
19181:8c2fdf7d5645 19182:fae47ecaa952
31 This module is package-private. It is not expected that these will 31 This module is package-private. It is not expected that these will
32 have any clients outside of httpplus. 32 have any clients outside of httpplus.
33 """ 33 """
34 34
35 import httplib 35 import httplib
36 import itertools
37 import logging 36 import logging
38 37
39 logger = logging.getLogger(__name__) 38 logger = logging.getLogger(__name__)
40 39
41 40
57 def __init__(self): 56 def __init__(self):
58 self._finished = False 57 self._finished = False
59 self._done_chunks = [] 58 self._done_chunks = []
60 self.available_data = 0 59 self.available_data = 0
61 60
62 def addchunk(self, data): 61 def _addchunk(self, data):
63 self._done_chunks.append(data) 62 self._done_chunks.append(data)
64 self.available_data += len(data) 63 self.available_data += len(data)
65 64
66 def pushchunk(self, data): 65 def _pushchunk(self, data):
67 self._done_chunks.insert(0, data) 66 self._done_chunks.insert(0, data)
68 self.available_data += len(data) 67 self.available_data += len(data)
69 68
70 def popchunk(self): 69 def _popchunk(self):
71 b = self._done_chunks.pop(0) 70 b = self._done_chunks.pop(0)
72 self.available_data -= len(b) 71 self.available_data -= len(b)
73 72
74 return b 73 return b
75 74
76 def done(self): 75 def done(self):
76 """Returns true if the response body is entirely read."""
77 return self._finished 77 return self._finished
78 78
79 def read(self, amt): 79 def read(self, amt):
80 """Read amt bytes from the response body."""
80 if self.available_data < amt and not self._finished: 81 if self.available_data < amt and not self._finished:
81 raise ReadNotReady() 82 raise ReadNotReady()
82 blocks = [] 83 blocks = []
83 need = amt 84 need = amt
84 while self._done_chunks: 85 while self._done_chunks:
85 b = self.popchunk() 86 b = self._popchunk()
86 if len(b) > need: 87 if len(b) > need:
87 nb = b[:need] 88 nb = b[:need]
88 self.pushchunk(b[need:]) 89 self._pushchunk(b[need:])
89 b = nb 90 b = nb
90 blocks.append(b) 91 blocks.append(b)
91 need -= len(b) 92 need -= len(b)
92 if need == 0: 93 if need == 0:
93 break 94 break
105 """ 106 """
106 if blocks is None: 107 if blocks is None:
107 blocks = [] 108 blocks = []
108 109
109 while self._done_chunks: 110 while self._done_chunks:
110 b = self.popchunk() 111 b = self._popchunk()
111 i = b.find(delimstr) + len(delimstr) 112 i = b.find(delimstr) + len(delimstr)
112 if i: 113 if i:
113 if i < len(b): 114 if i < len(b):
114 self.pushchunk(b[i:]) 115 self._pushchunk(b[i:])
115 blocks.append(b[:i]) 116 blocks.append(b[:i])
116 break 117 break
117 else: 118 else:
118 blocks.append(b) 119 blocks.append(b)
119 120
152 """ 153 """
153 def _load(self, data): 154 def _load(self, data):
154 if data: 155 if data:
155 assert not self._finished, ( 156 assert not self._finished, (
156 'tried to add data (%r) to a closed reader!' % data) 157 'tried to add data (%r) to a closed reader!' % data)
157 logger.debug('%s read an additional %d data', self.name, len(data)) 158 logger.debug('%s read an additional %d data',
158 self.addchunk(data) 159 self.name, len(data)) # pylint: disable=E1101
160 self._addchunk(data)
159 161
160 162
161 class CloseIsEndReader(AbstractSimpleReader): 163 class CloseIsEndReader(AbstractSimpleReader):
162 """Reader for responses that specify Connection: Close for length.""" 164 """Reader for responses that specify Connection: Close for length."""
163 name = 'close-is-end' 165 name = 'close-is-end'
170 class ContentLengthReader(AbstractSimpleReader): 172 class ContentLengthReader(AbstractSimpleReader):
171 """Reader for responses that specify an exact content length.""" 173 """Reader for responses that specify an exact content length."""
172 name = 'content-length' 174 name = 'content-length'
173 175
174 def __init__(self, amount): 176 def __init__(self, amount):
175 AbstractReader.__init__(self) 177 AbstractSimpleReader.__init__(self)
176 self._amount = amount 178 self._amount = amount
177 if amount == 0: 179 if amount == 0:
178 self._finished = True 180 self._finished = True
179 self._amount_seen = 0 181 self._amount_seen = 0
180 182
197 def _load(self, data): 199 def _load(self, data):
198 assert not self._finished, 'tried to add data to a closed reader!' 200 assert not self._finished, 'tried to add data to a closed reader!'
199 logger.debug('chunked read an additional %d data', len(data)) 201 logger.debug('chunked read an additional %d data', len(data))
200 position = 0 202 position = 0
201 if self._leftover_data: 203 if self._leftover_data:
202 logger.debug('chunked reader trying to finish block from leftover data') 204 logger.debug(
205 'chunked reader trying to finish block from leftover data')
203 # TODO: avoid this string concatenation if possible 206 # TODO: avoid this string concatenation if possible
204 data = self._leftover_data + data 207 data = self._leftover_data + data
205 position = self._leftover_skip_amt 208 position = self._leftover_skip_amt
206 self._leftover_data = '' 209 self._leftover_data = ''
207 self._leftover_skip_amt = 0 210 self._leftover_skip_amt = 0
222 return 225 return
223 if amt == 0: 226 if amt == 0:
224 self._finished = True 227 self._finished = True
225 logger.debug('closing chunked reader due to chunk of length 0') 228 logger.debug('closing chunked reader due to chunk of length 0')
226 return 229 return
227 self.addchunk(data[block_start:block_start + amt]) 230 self._addchunk(data[block_start:block_start + amt])
228 position = block_start + amt + len(self._eol) 231 position = block_start + amt + len(self._eol)
229 # no-check-code 232 # no-check-code