58
55
should happen with monotonically increasing offsets.
61
# in _checked_read() below, we may have to discard several MB in the worst
62
# case. To avoid buffering that much, we read and discard by chunks
63
# instead. The underlying file is either a socket or a StringIO, so reading
64
# 8k chunks should be fine.
65
_discarded_buf_size = 8192
67
# maximum size of read requests -- used to avoid MemoryError issues in recv
68
_max_read_size = 512 * 1024
70
58
def __init__(self, path, infile):
109
97
# To be on the safe side we allow it before any boundary line
110
98
boundary_line = self._file.readline()
112
99
if boundary_line != '--' + self._boundary + '\r\n':
113
# rfc822.unquote() incorrectly unquotes strings enclosed in <>
114
# IIS 6 and 7 incorrectly wrap boundary strings in <>
115
# together they make a beautiful bug, which we will be gracious
117
if (self._unquote_boundary(boundary_line) !=
118
'--' + self._boundary + '\r\n'):
119
raise errors.InvalidHttpResponse(
121
"Expected a boundary (%s) line, got '%s'"
122
% (self._boundary, boundary_line))
124
def _unquote_boundary(self, b):
125
return b[:2] + rfc822.unquote(b[2:-2]) + b[-2:]
100
raise errors.InvalidHttpResponse(
102
"Expected a boundary (%s) line, got '%s'" % (self._boundary,
127
105
def read_range_definition(self):
128
106
"""Read a new range definition in a multi parts message.
167
145
self.set_range(start, size)
169
147
def _checked_read(self, size):
170
"""Read the file checking for short reads.
172
The data read is discarded along the way.
148
"""Read the file checking for short reads"""
177
data = self._file.read(min(remaining, self._discarded_buf_size))
178
remaining -= len(data)
180
raise errors.ShortReadvError(self._path, pos, size,
150
data = self._file.read(size)
152
if size > 0 and data_len != size:
153
raise errors.ShortReadvError(self._path, pos, size, data_len)
154
self._pos += data_len
184
157
def _seek_to_next_range(self):
185
158
# We will cross range boundaries
186
159
if self._boundary is None:
187
160
# If we don't have a boundary, we can't find another range
188
raise errors.InvalidRange(self._path, self._pos,
189
"Range (%s, %s) exhausted"
190
% (self._start, self._size))
161
raise errors.InvalidRange(
162
self._path, self._pos,
163
"Range (%s, %s) exhausted"
164
% (self._start, self._size))
191
165
self.read_boundary()
192
166
self.read_range_definition()
198
172
client to clean the socket if we leave bytes unread. This may occur for
199
173
the final boundary line of a multipart response or for any range
200
174
request not entirely consumed by the client (due to offset coalescing)
202
:param size: The number of bytes to read. Leave unspecified or pass
205
176
if (self._size > 0
206
177
and self._pos == self._start + self._size):
220
191
"Can't read %s bytes across range (%s, %s)"
221
192
% (size, self._start, self._size))
223
# read data from file
226
194
if self._size > 0:
227
195
# Don't read past the range definition
228
196
limited = self._start + self._size - self._pos
230
198
limited = min(limited, size)
231
osutils.pumpfile(self._file, buffer, limited, self._max_read_size)
232
data = buffer.getvalue()
199
data = self._file.read(limited)
201
# Size of file unknown, the user may have specified a size or not,
202
# we delegate that to the filesocket object (-1 means read until
204
data = self._file.read(size)
234
205
# Update _pos respecting the data effectively read
235
206
self._pos += len(data)