view mercurial/hgweb/wsgicgi.py @ 41138:8ddc5d8bea25

tests: support passing testcase after .t paths that have path separators This probably could have been implemented by changing the regex above this bit of code, but I wasn't sure if it would end up handling various OSes correctly, so I decided to go with this version instead. Previously: $ tests/run-tests.py tests/test-ssh.t -l running 2 tests using 2 parallel processes .. # Ran 2 tests, 0 skipped, 0 failed. $ tests/run-tests.py tests/test-ssh.t#sshv1 -l running 0 tests using 0 parallel processes # Ran 0 tests, 0 skipped, 0 failed. Now: $ tests/run-tests.py tests/test-ssh.t -l running 2 tests using 2 parallel processes .. # Ran 2 tests, 0 skipped, 0 failed. $ tests/run-tests.py tests/test-ssh.t#sshv1 -l running 1 tests using 1 parallel processes . # Ran 1 tests, 0 skipped, 0 failed. Differential Revision: https://phab.mercurial-scm.org/D5535
author Kyle Lippincott <spectral@google.com>
date Tue, 08 Jan 2019 17:52:39 -0800
parents 2d5b5bcc3b9f
children 2372284d9457
line wrap: on
line source

# 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)()