mercurial/hgweb/wsgicgi.py
author Boris Feld <boris.feld@octobus.net>
Wed, 19 Sep 2018 21:02:47 +0200
changeset 39886 debc4ee597e7
parent 37747 2d5b5bcc3b9f
child 43076 2372284d9457
permissions -rw-r--r--
revlog: add a callback "tracking" duplicate node addition If a changegroup contains node already added to the repository, they will be skipped. Skipping them is the right behavior (we don't need to store things twice), but it can hide some information to the code doing the unbundle (eg: shelve looking for the tip of the bundle). The first step to improve this situation is to add a low level callback. We do not need this tracking on all revlog, so actual tracking will be added in the next changeset.

# hgweb/wsgicgi.py - CGI->WSGI translator
#
# Copyright 2006 Eric Hopper <hopper@omnifarious.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
#
# This was originally copied from the public domain code at
# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side

from __future__ import absolute_import

import os

from .. import (
    pycompat,
)

from ..utils import (
    procutil,
)

from . import (
    common,
)

def launch(application):
    procutil.setbinary(procutil.stdin)
    procutil.setbinary(procutil.stdout)

    environ = dict(os.environ.iteritems()) # re-exports
    environ.setdefault(r'PATH_INFO', '')
    if environ.get(r'SERVER_SOFTWARE', r'').startswith(r'Microsoft-IIS'):
        # IIS includes script_name in PATH_INFO
        scriptname = environ[r'SCRIPT_NAME']
        if environ[r'PATH_INFO'].startswith(scriptname):
            environ[r'PATH_INFO'] = environ[r'PATH_INFO'][len(scriptname):]

    stdin = procutil.stdin
    if environ.get(r'HTTP_EXPECT', r'').lower() == r'100-continue':
        stdin = common.continuereader(stdin, procutil.stdout.write)

    environ[r'wsgi.input'] = stdin
    environ[r'wsgi.errors'] = procutil.stderr
    environ[r'wsgi.version'] = (1, 0)
    environ[r'wsgi.multithread'] = False
    environ[r'wsgi.multiprocess'] = True
    environ[r'wsgi.run_once'] = True

    if environ.get(r'HTTPS', r'off').lower() in (r'on', r'1', r'yes'):
        environ[r'wsgi.url_scheme'] = r'https'
    else:
        environ[r'wsgi.url_scheme'] = r'http'

    headers_set = []
    headers_sent = []
    out = procutil.stdout

    def write(data):
        if not headers_set:
            raise AssertionError("write() before start_response()")

        elif not headers_sent:
            # Before the first output, send the stored headers
            status, response_headers = headers_sent[:] = headers_set
            out.write('Status: %s\r\n' % pycompat.bytesurl(status))
            for hk, hv in response_headers:
                out.write('%s: %s\r\n' % (pycompat.bytesurl(hk),
                                          pycompat.bytesurl(hv)))
            out.write('\r\n')

        out.write(data)
        out.flush()

    def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[0](exc_info[1], exc_info[2])
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        headers_set[:] = [status, response_headers]
        return write

    content = application(environ, start_response)
    try:
        for chunk in content:
            write(chunk)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        getattr(content, 'close', lambda: None)()