view mercurial/streamclone.py @ 6945:2cfdabe235fb

hgweb: return content iterator instead of using write() callable This is a new version of 4879468fa28f (which was backed out in 943f066c0d58), with an extra line removed to fix problems with hg serve. hg's internal web server contains checking if the app isn't trying to write more bytes than specified by the Content-Length header. The first try still contained an old line that wrote the response, so the response was sent twice.
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Sat, 30 Aug 2008 17:13:23 +0200
parents 87abfefafe02
children 63b5f4c73c98
line wrap: on
line source

# streamclone.py - streaming clone server support for mercurial
#
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.

import util, lock

class StreamException(Exception):
    def __init__(self, code):
        Exception.__init__(self)
        self.code = code
    def __str__(self):
        return '%i\n' % self.code

# if server supports streaming clone, it advertises "stream"
# capability with value that is version+flags of repo it is serving.
# client only streams if it can read that repo format.

# stream file format is simple.
#
# server writes out line that says how many files, how many total
# bytes.  separator is ascii space, byte counts are strings.
#
# then for each file:
#
#   server writes out line that says file name, how many bytes in
#   file.  separator is ascii nul, byte count is string.
#
#   server writes out raw file data.

def stream_out(repo, untrusted=False):
    '''stream out all metadata files in repository.
    writes to file-like object, must support write() and optional flush().'''

    if not repo.ui.configbool('server', 'uncompressed', untrusted=untrusted):
        raise StreamException(1)

    entries = []
    total_bytes = 0
    try:
        l = None
        try:
            repo.ui.debug('scanning\n')
            # get consistent snapshot of repo, lock during scan
            l = repo.lock()
            for name, ename, size in repo.store.walk():
                entries.append((name, size))
                total_bytes += size
        finally:
            del l
    except (lock.LockHeld, lock.LockUnavailable), inst:
        raise StreamException(2)

    yield '0\n'
    repo.ui.debug('%d files, %d bytes to transfer\n' %
                  (len(entries), total_bytes))
    yield '%d %d\n' % (len(entries), total_bytes)
    for name, size in entries:
        repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
        yield '%s\0%d\n' % (name, size)
        for chunk in util.filechunkiter(repo.sopener(name), limit=size):
            yield chunk