~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http/response.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
responses.
22
22
"""
23
23
 
24
 
from __future__ import absolute_import
25
24
 
26
 
import os
27
25
import httplib
28
26
from cStringIO import StringIO
29
27
import rfc822
30
28
 
31
29
from bzrlib import (
32
30
    errors,
 
31
    trace,
33
32
    osutils,
34
33
    )
35
34
 
36
35
 
37
 
class ResponseFile(object):
38
 
    """A wrapper around the http socket containing the result of a GET request.
39
 
 
40
 
    Only read() and seek() (forward) are supported.
41
 
 
42
 
    """
43
 
    def __init__(self, path, infile):
44
 
        """Constructor.
45
 
 
46
 
        :param path: File url, for error reports.
47
 
 
48
 
        :param infile: File-like socket set at body start.
49
 
        """
50
 
        self._path = path
51
 
        self._file = infile
52
 
        self._pos = 0
53
 
 
54
 
    def close(self):
55
 
        """Close this file.
56
 
 
57
 
        Dummy implementation for consistency with the 'file' API.
58
 
        """
59
 
 
60
 
    def read(self, size=-1):
61
 
        """Read size bytes from the current position in the file.
62
 
 
63
 
        :param size:  The number of bytes to read.  Leave unspecified or pass
64
 
            -1 to read to EOF.
65
 
        """
66
 
        data =  self._file.read(size)
67
 
        self._pos += len(data)
68
 
        return data
69
 
 
70
 
    def readline(self):
71
 
        data = self._file.readline()
72
 
        self._pos += len(data)
73
 
        return data
74
 
 
75
 
    def __iter__(self):
76
 
        while True:
77
 
            line = self.readline()
78
 
            if not line:
79
 
                return
80
 
            yield line
81
 
 
82
 
    def tell(self):
83
 
        return self._pos
84
 
 
85
 
    def seek(self, offset, whence=os.SEEK_SET):
86
 
        if whence == os.SEEK_SET:
87
 
            if offset < self._pos:
88
 
                raise AssertionError(
89
 
                    "Can't seek backwards, pos: %s, offset: %s"
90
 
                    % (self._pos, offset))
91
 
            to_discard = offset - self._pos
92
 
        elif whence == os.SEEK_CUR:
93
 
            to_discard = offset
94
 
        else:
95
 
            raise AssertionError("Can't seek backwards")
96
 
        if to_discard:
97
 
            # Just discard the unwanted bytes
98
 
            self.read(to_discard)
99
 
 
100
36
# A RangeFile expects the following grammar (simplified to outline the
101
37
# assumptions we rely upon).
102
38
 
103
 
# file: single_range
 
39
# file: whole_file
 
40
#     | single_range
104
41
#     | multiple_range
105
42
 
 
43
# whole_file: [content_length_header] data
 
44
 
106
45
# single_range: content_range_header data
107
46
 
108
47
# multiple_range: boundary_header boundary (content_range_header data boundary)+
109
48
 
110
 
class RangeFile(ResponseFile):
 
49
class RangeFile(object):
111
50
    """File-like object that allow access to partial available data.
112
51
 
113
52
    All accesses should happen sequentially since the acquisition occurs during
132
71
        """Constructor.
133
72
 
134
73
        :param path: File url, for error reports.
135
 
 
136
74
        :param infile: File-like socket set at body start.
137
75
        """
138
 
        super(RangeFile, self).__init__(path, infile)
 
76
        self._path = path
 
77
        self._file = infile
139
78
        self._boundary = None
140
79
        # When using multi parts response, this will be set with the headers
141
80
        # associated with the range currently read.
289
228
                    % (size, self._start, self._size))
290
229
 
291
230
        # read data from file
292
 
        buf = StringIO()
 
231
        buffer = StringIO()
293
232
        limited = size
294
233
        if self._size > 0:
295
234
            # Don't read past the range definition
296
235
            limited = self._start + self._size - self._pos
297
236
            if size >= 0:
298
237
                limited = min(limited, size)
299
 
        osutils.pumpfile(self._file, buf, limited, self._max_read_size)
300
 
        data = buf.getvalue()
 
238
        osutils.pumpfile(self._file, buffer, limited, self._max_read_size)
 
239
        data = buffer.getvalue()
301
240
 
302
241
        # Update _pos respecting the data effectively read
303
242
        self._pos += len(data)
359
298
    :return: A file-like object that can seek()+read() the
360
299
             ranges indicated by the headers.
361
300
    """
 
301
    rfile = RangeFile(url, data)
362
302
    if code == 200:
363
303
        # A whole file
364
 
        rfile = ResponseFile(url, data)
 
304
        size = msg.getheader('content-length', None)
 
305
        if size is None:
 
306
            size = -1
 
307
        else:
 
308
            size = int(size)
 
309
        rfile.set_range(0, size)
365
310
    elif code == 206:
366
 
        rfile = RangeFile(url, data)
367
311
        content_type = msg.getheader('content-type', None)
368
312
        if content_type is None:
369
313
            # When there is no content-type header we treat the response as