mercurial/hgweb/wsgicgi.py
author Alexis S. L. Carvalho <alexis@cecm.usp.br>
Fri, 16 Feb 2007 05:10:43 -0200
changeset 4095 6fa7a2d0fc2e
parent 4076 5a89c61c189c
child 4496 7605da1c3b5c
permissions -rw-r--r--
hgweb: catch util.Abort raised by addchangegroup Right now, if a pretxnchangegroup hook fails, we send some HTML error message to the client and the transaction is not rolled back (issue499). Catching util.Abort allows us to send a decent message to the client and for some reason makes the rollback complete. This patch is not perfect since it doesn't fix the reason why the transaction wasn't rolled back (maybe some circular references?). Also, the transaction is aborted only after we've sent the response back to the client and the "transaction aborted" message ends up in the logs of the web server.

# 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, incorporated herein by reference.
#
# This was originally copied from the public domain code at
# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side

import os, sys
from mercurial import util

def launch(application):
    util.set_binary(sys.stdout)

    environ = dict(os.environ.items())
    environ['wsgi.input'] = sys.stdin
    environ['wsgi.errors'] = sys.stderr
    environ['wsgi.version'] = (1, 0)
    environ['wsgi.multithread'] = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once'] = True

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

    headers_set = []
    headers_sent = []
    out = sys.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' % status)
            for header in response_headers:
                out.write('%s: %s\r\n' % header)
            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

    result = application(environ, start_response)
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result,'close'):
            result.close()