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 |
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 |