~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/http.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-03-09 06:39:13 UTC
  • mfrom: (1596.2.6 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060309063913-6d8ce700706d0802
Merge knit performance stage 1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""
18
18
 
19
19
import os, errno
 
20
from collections import deque
20
21
from cStringIO import StringIO
21
22
import urllib, urllib2
22
23
import urlparse
74
75
            return urllib2.Request.get_method(self)
75
76
 
76
77
 
77
 
def get_url(url, method=None):
 
78
def get_url(url, method=None, ranges=None):
78
79
    import urllib2
79
 
    mutter("get_url %s", url)
 
80
    if ranges:
 
81
        rangestring = ranges
 
82
    else:
 
83
        rangestring = 'all'
 
84
    mutter("get_url %s [%s]", url, rangestring)
80
85
    manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
81
86
    url = extract_auth(url, manager)
82
87
    auth_handler = urllib2.HTTPBasicAuthHandler(manager)
85
90
    request = Request(url)
86
91
    request.method = method
87
92
    request.add_header('User-Agent', 'bzr/%s' % bzrlib.__version__)
 
93
    if ranges:
 
94
        request.add_header('Range', ranges)
88
95
    response = opener.open(request)
89
96
    return response
90
97
 
190
197
                return False
191
198
            raise TransportError(orig_error=e)
192
199
 
193
 
    def get(self, relpath, decode=False):
194
 
        """Get the file at the given relative path.
195
 
 
196
 
        :param relpath: The relative path to the file
197
 
        """
 
200
    def _get(self, relpath, decode=False, ranges=None):
198
201
        path = relpath
199
202
        try:
200
203
            path = self.abspath(relpath)
201
 
            return get_url(path)
 
204
            return get_url(path, ranges=ranges)
202
205
        except urllib2.HTTPError, e:
203
206
            mutter('url error code: %s for has url: %r', e.code, path)
204
207
            if e.code == 404:
214
217
                             % (self.abspath(relpath), str(e)),
215
218
                             orig_error=e)
216
219
 
 
220
    def get(self, relpath, decode=False):
 
221
        """Get the file at the given relative path.
 
222
 
 
223
        :param relpath: The relative path to the file
 
224
        """
 
225
        return self._get(relpath, decode=decode)
 
226
 
 
227
    def readv(self, relpath, offsets):
 
228
        """Get parts of the file at the given relative path.
 
229
 
 
230
        :offsets: A list of (offset, size) tuples.
 
231
        :return: A list or generator of (offset, data) tuples
 
232
        """
 
233
        # this is not quite regular enough to have a single driver routine and
 
234
        # helper method in Transport.
 
235
        def do_combined_read(combined_offsets):
 
236
            # read one coalesced block
 
237
            total_size = 0
 
238
            for offset, size in combined_offsets:
 
239
                total_size += size
 
240
            mutter('readv coalesced %d reads.', len(combined_offsets))
 
241
            offset = combined_offsets[0][0]
 
242
            ranges = 'bytes=%d-%d' % (offset, offset + total_size - 1)
 
243
            response = self._get(relpath, ranges=ranges)
 
244
            if response.code == 206:
 
245
                for off, size in combined_offsets:
 
246
                    yield off, response.read(size)
 
247
            elif response.code == 200:
 
248
                data = response.read(offset + total_size)[offset:offset + total_size]
 
249
                pos = 0
 
250
                for offset, size in combined_offsets:
 
251
                    yield offset, data[pos:pos + size]
 
252
                    pos += size
 
253
                del data
 
254
 
 
255
        if not len(offsets):
 
256
            return
 
257
        pending_offsets = deque(offsets)
 
258
        combined_offsets = []
 
259
        while len(pending_offsets):
 
260
            offset, size = pending_offsets.popleft()
 
261
            if not combined_offsets:
 
262
                combined_offsets = [[offset, size]]
 
263
            else:
 
264
                if (len (combined_offsets) < 50 and
 
265
                    combined_offsets[-1][0] + combined_offsets[-1][1] == offset):
 
266
                    # combatible offset:
 
267
                    combined_offsets.append([offset, size])
 
268
                else:
 
269
                    # incompatible, or over the threshold issue a read and yield
 
270
                    pending_offsets.appendleft((offset, size))
 
271
                    for result in do_combined_read(combined_offsets):
 
272
                        yield result
 
273
                    combined_offsets = []
 
274
        # whatever is left is a single coalesced request
 
275
        if len(combined_offsets):
 
276
            for result in do_combined_read(combined_offsets):
 
277
                yield result
 
278
 
217
279
    def put(self, relpath, f, mode=None):
218
280
        """Copy the file-like or string object into the location.
219
281