def unbundle(repo, req): proto = req.env.get('wsgi.url_scheme') or 'http' their_heads = req.form['heads'][0].split(' ') def check_heads(): heads = map(hex, repo.heads()) return their_heads == [hex('force')] or their_heads == heads # fail early if possible if not check_heads(): req.drain() raise ErrorResponse(HTTP_OK, 'unsynced changes') # do not lock repo until all changegroup data is # streamed. save to temporary file. fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') fp = os.fdopen(fd, 'wb+') try: length = int(req.env['CONTENT_LENGTH']) for s in util.filechunkiter(req, limit=length): fp.write(s) try: lock = repo.lock() try: if not check_heads(): raise ErrorResponse(HTTP_OK, 'unsynced changes') fp.seek(0) header = fp.read(6) if header.startswith('HG') and not header.startswith('HG10'): raise ValueError('unknown bundle version') elif header not in changegroupmod.bundletypes: raise ValueError('unknown bundle compression type') gen = changegroupmod.unbundle(header, fp) # send addchangegroup output to client oldio = sys.stdout, sys.stderr sys.stderr = sys.stdout = cStringIO.StringIO() try: url = 'remote:%s:%s:%s' % ( proto, urllib.quote(req.env.get('REMOTE_HOST', '')), urllib.quote(req.env.get('REMOTE_USER', ''))) try: ret = repo.addchangegroup(gen, 'serve', url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() sys.stdout, sys.stderr = oldio req.respond(HTTP_OK, HGTYPE) return '%d\n%s' % (ret, val), finally: lock.release() except ValueError, inst: raise ErrorResponse(HTTP_OK, inst) except (OSError, IOError), inst: filename = getattr(inst, 'filename', '') # Don't send our filesystem layout to the client if filename.startswith(repo.root): filename = filename[len(repo.root) + 1:] else: filename = '' error = getattr(inst, 'strerror', 'Unknown error') if inst.errno == errno.ENOENT: code = HTTP_NOT_FOUND else: code = HTTP_SERVER_ERROR raise ErrorResponse(code, '%s: %s' % (error, filename))
def unbundle(repo, req): """ Because protocol.unbundle uses tempfile, we cannot use it in AppEngine environment. Here is an alternative implementation which does not use tempfiles. """ proto = os.environ.get("wsgi.url_scheme") or "http" their_heads = req.form["heads"][0].split(" ") def check_heads(): heads = map(hex, repo.heads()) return their_heads == [hex("force")] or their_heads == heads # fail early if possible if not check_heads(): length = int(req.headers.get("Content-Length", 0)) for s in util.filechunkiter(req.body_file, limit=length): # drain incoming bundle, else client will not see # response when run outside cgi script pass raise ErrorResponse(HTTP_OK, "unsynced changes") # do not lock repo until all changegroup data is # streamed. save to temporary file. fp = StringIO() length = int(req.headers["Content-Length"]) if int(length) > 50 * 1024: # Content is too long: raise ErrorResponse(HTTP_OK, "Bundled change size is greater than 50k." " Split changes using -r flag.") for s in util.filechunkiter(req.body_file, limit=length): fp.write(s) try: if not check_heads(): raise ErrorResponse(HTTP_OK, "unsynced changes") fp.seek(0) header = fp.read(6) if header.startswith("HG") and not header.startswith("HG10"): raise ValueError("unknown bundle version") elif header not in changegroup.bundletypes: raise ValueError("unknown bundle compression type") gen = changegroup.unbundle(header, fp) # send addchangegroup output to client oldio = sys.stdout, sys.stderr sys.stderr = sys.stdout = StringIO() try: url = "remote:%s:%s" % (proto, req.remote_addr) try: ret = repo.addchangegroup(gen, "serve", url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() sys.stdout, sys.stderr = oldio req.respond(HTTP_OK, protocol.HGTYPE) return ("%d\n%s" % (ret, val),) except ValueError, inst: raise ErrorResponse(HTTP_OK, inst)
def unbundle(repo, req): proto = req.env.get('wsgi.url_scheme') or 'http' their_heads = req.form['heads'][0].split(' ') def check_heads(): heads = map(hex, repo.heads()) return their_heads == [hex('force')] or their_heads == heads # fail early if possible if not check_heads(): req.drain() raise ErrorResponse(HTTP_OK, 'unsynced changes') # do not lock repo until all changegroup data is # streamed. save to temporary file. fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') fp = os.fdopen(fd, 'wb+') try: length = int(req.env['CONTENT_LENGTH']) for s in util.filechunkiter(req, limit=length): fp.write(s) try: lock = repo.lock() try: if not check_heads(): raise ErrorResponse(HTTP_OK, 'unsynced changes') fp.seek(0) header = fp.read(6) if header.startswith('HG') and not header.startswith('HG10'): raise ValueError('unknown bundle version') elif header not in changegroupmod.bundletypes: raise ValueError('unknown bundle compression type') gen = changegroupmod.unbundle(header, fp) # send addchangegroup output to client oldio = sys.stdout, sys.stderr sys.stderr = sys.stdout = cStringIO.StringIO() try: url = 'remote:%s:%s:%s' % ( proto, urllib.quote(req.env.get('REMOTE_HOST', '')), urllib.quote(req.env.get('REMOTE_USER', ''))) try: ret = repo.addchangegroup(gen, 'serve', url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() sys.stdout, sys.stderr = oldio req.respond(HTTP_OK, HGTYPE) return '%d\n%s' % (ret, val), finally: lock.release() except ValueError, inst: raise ErrorResponse(HTTP_OK, inst) except (OSError, IOError), inst: filename = getattr(inst, 'filename', '') # Don't send our filesystem layout to the client if filename.startswith(repo.root): filename = filename[len(repo.root)+1:] else: filename = '' error = getattr(inst, 'strerror', 'Unknown error') if inst.errno == errno.ENOENT: code = HTTP_NOT_FOUND else: code = HTTP_SERVER_ERROR raise ErrorResponse(code, '%s: %s' % (error, filename))
def unbundle(web, req): def bail(response, headers={}): length = int(req.env.get('CONTENT_LENGTH', 0)) for s in util.filechunkiter(req, limit=length): # drain incoming bundle, else client will not see # response when run outside cgi script pass status = headers.pop('status', HTTP_OK) req.header(headers.items()) req.respond(status, HGTYPE) req.write('0\n') req.write(response) # enforce that you can only unbundle with POST requests if req.env['REQUEST_METHOD'] != 'POST': headers = {'status': '405 Method Not Allowed'} bail('unbundle requires POST request\n', headers) return # require ssl by default, auth info cannot be sniffed and # replayed ssl_req = web.configbool('web', 'push_ssl', True) if ssl_req: if req.env.get('wsgi.url_scheme') != 'https': bail('ssl required\n') return proto = 'https' else: proto = 'http' # do not allow push unless explicitly allowed if not web.check_perm(req, 'push', False): bail('push not authorized\n', headers={'status': '401 Unauthorized'}) return their_heads = req.form['heads'][0].split(' ') def check_heads(): heads = map(hex, web.repo.heads()) return their_heads == [hex('force')] or their_heads == heads # fail early if possible if not check_heads(): bail('unsynced changes\n') return req.respond(HTTP_OK, HGTYPE) # do not lock repo until all changegroup data is # streamed. save to temporary file. fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') fp = os.fdopen(fd, 'wb+') try: length = int(req.env['CONTENT_LENGTH']) for s in util.filechunkiter(req, limit=length): fp.write(s) try: lock = web.repo.lock() try: if not check_heads(): req.write('0\n') req.write('unsynced changes\n') return fp.seek(0) header = fp.read(6) if header.startswith('HG') and not header.startswith('HG10'): raise ValueError('unknown bundle version') elif header not in changegroupmod.bundletypes: raise ValueError('unknown bundle compression type') gen = changegroupmod.unbundle(header, fp) # send addchangegroup output to client oldio = sys.stdout, sys.stderr sys.stderr = sys.stdout = cStringIO.StringIO() try: url = 'remote:%s:%s' % (proto, req.env.get('REMOTE_HOST', '')) try: ret = web.repo.addchangegroup(gen, 'serve', url) except util.Abort, inst: sys.stdout.write("abort: %s\n" % inst) ret = 0 finally: val = sys.stdout.getvalue() sys.stdout, sys.stderr = oldio req.write('%d\n' % ret) req.write(val) finally: del lock except ValueError, inst: req.write('0\n') req.write(str(inst) + '\n') except (OSError, IOError), inst: req.write('0\n') filename = getattr(inst, 'filename', '') # Don't send our filesystem layout to the client if filename.startswith(web.repo.root): filename = filename[len(web.repo.root)+1:] else: filename = '' error = getattr(inst, 'strerror', 'Unknown error') if inst.errno == errno.ENOENT: code = HTTP_NOT_FOUND else: code = HTTP_SERVER_ERROR req.respond(code) req.write('%s: %s\n' % (error, filename))