1
# Copyright (C) 2006, 2007 Canonical Ltd
1
# Copyright (C) 2006-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
24
from __future__ import absolute_import
26
28
from cStringIO import StringIO
29
31
from bzrlib import (
37
class ResponseFile(object):
38
"""A wrapper around the http socket containing the result of a GET request.
40
Only read() and seek() (forward) are supported.
42
def __init__(self, path, infile):
45
:param path: File url, for error reports.
47
:param infile: File-like socket set at body start.
56
Dummy implementation for consistency with the 'file' API.
59
def read(self, size=-1):
60
"""Read size bytes from the current position in the file.
62
:param size: The number of bytes to read. Leave unspecified or pass
65
data = self._file.read(size)
66
self._pos += len(data)
70
data = self._file.readline()
71
self._pos += len(data)
77
def seek(self, offset, whence=os.SEEK_SET):
78
if whence == os.SEEK_SET:
79
if offset < self._pos:
81
"Can't seek backwards, pos: %s, offset: %s"
82
% (self._pos, offset))
83
to_discard = offset - self._pos
84
elif whence == os.SEEK_CUR:
87
raise AssertionError("Can't seek backwards")
89
# Just discard the unwanted bytes
36
92
# A RangeFile expects the following grammar (simplified to outline the
37
93
# assumptions we rely upon).
43
# whole_file: [content_length_header] data
45
98
# single_range: content_range_header data
47
100
# multiple_range: boundary_header boundary (content_range_header data boundary)+
49
class RangeFile(object):
102
class RangeFile(ResponseFile):
50
103
"""File-like object that allow access to partial available data.
52
105
All accesses should happen sequentially since the acquisition occurs during
73
126
:param path: File url, for error reports.
74
128
:param infile: File-like socket set at body start.
130
super(RangeFile, self).__init__(path, infile)
78
131
self._boundary = None
79
132
# When using multi parts response, this will be set with the headers
80
133
# associated with the range currently read.
109
162
# To be on the safe side we allow it before any boundary line
110
163
boundary_line = self._file.readline()
165
if boundary_line == '':
166
# A timeout in the proxy server caused the response to end early.
167
# See launchpad bug 198646.
168
raise errors.HttpBoundaryMissing(
112
172
if boundary_line != '--' + self._boundary + '\r\n':
113
173
# rfc822.unquote() incorrectly unquotes strings enclosed in <>
114
174
# IIS 6 and 7 incorrectly wrap boundary strings in <>
291
351
:return: A file-like object that can seek()+read() the
292
352
ranges indicated by the headers.
294
rfile = RangeFile(url, data)
297
size = msg.getheader('content-length', None)
302
rfile.set_range(0, size)
356
rfile = ResponseFile(url, data)
303
357
elif code == 206:
358
rfile = RangeFile(url, data)
304
359
content_type = msg.getheader('content-type', None)
305
360
if content_type is None:
306
361
# When there is no content-type header we treat the response as