trac-ticketlinks / trac / web /

jonas 26623b3 
cmlenz 3d428a4 
jonas 7c60ab2 
cmlenz 47c3427 
cmlenz 3d428a4 

cmlenz 47c3427 
cmlenz 3d428a4 

cmlenz 47c3427 
cmlenz 3d428a4 

cboos 49efcf2 
cboos 7b2decc 
cmlenz 3d428a4 

jomae c1326d7 

cmlenz 3d428a4 

cmlenz ab030d6 

cmlenz 3d428a4 

cboos 61d81e5 

cmlenz 3d428a4 
cboos 61d81e5 
cmlenz 3d428a4 

rblank 9cfb00e 
cmlenz 3d428a4 

cboos 49efcf2 

cboos 7b2decc 
cboos 49efcf2 
cboos 7b2decc 
rblank 19f69ee 
cboos 49efcf2 

cboos 61d81e5 

cmlenz 3d428a4 

cboos 61d81e5 

cmlenz 3d428a4 
cboos 49efcf2 

cboos 7b2decc 

cboos 49efcf2 

cmlenz 3d428a4 

cmlenz 86698aa 
cmlenz 3d428a4 

# -*- coding: utf-8 -*-
# Copyright (C) 2005-2009 Edgewall Software
# Copyright (C) 2005-2006 Christopher Lenz <>
# All rights reserved.
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at
# Author: Christopher Lenz <>

import errno
import socket
import sys
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from SocketServer import ForkingMixIn, ThreadingMixIn
import urllib

class _ErrorsWrapper(object):

    def __init__(self, logfunc):
        self.logfunc = logfunc

    def flush(self):

    def write(self, msg):

    def writelines(self, seq):
        map(self.write, seq)

class _FileWrapper(object):
    """Wrapper for sending a file as response."""

    def __init__(self, fileobj, blocksize=None):
        self.fileobj = fileobj
        self.blocksize = blocksize =
        if hasattr(fileobj, 'close'):
            self.close = fileobj.close

    def __iter__(self):
        return self

    def next(self):
        data =
        if not data:
            raise StopIteration
        return data

class WSGIGateway(object):
    """Abstract base class for WSGI servers or gateways."""

    wsgi_version = (1, 0)
    wsgi_multithread = True
    wsgi_multiprocess = True
    wsgi_run_once = False
    wsgi_file_wrapper = _FileWrapper

    def __init__(self, environ, stdin=sys.stdin, stderr=sys.stderr):
        """Initialize the gateway object."""
        environ['wsgi.version'] = self.wsgi_version
        environ['wsgi.url_scheme'] = 'http'
        if environ.get('HTTPS', '').lower() in ('yes', 'on', '1'):
            environ['wsgi.url_scheme'] = 'https'
        elif environ.get('HTTP_X_FORWARDED_PROTO', '').lower() == 'https':
            environ['wsgi.url_scheme'] = 'https'
        environ['wsgi.input'] = stdin
        environ['wsgi.errors'] = stderr
        environ['wsgi.multithread'] = self.wsgi_multithread
        environ['wsgi.multiprocess'] = self.wsgi_multiprocess
        environ['wsgi.run_once'] = self.wsgi_run_once
        if self.wsgi_file_wrapper is not None:
            environ['wsgi.file_wrapper'] = self.wsgi_file_wrapper
        self.environ = environ

        self.headers_set = []
        self.headers_sent = []

    def run(self, application):
        """Start the gateway with the given WSGI application."""
        response = application(self.environ, self._start_response)
            if self.wsgi_file_wrapper is not None \
                    and isinstance(response, self.wsgi_file_wrapper) \
                    and hasattr(self, '_sendfile'):
                for chunk in response:
                    if chunk:
                if not self.headers_sent:
            if hasattr(response, 'close'):

    def _start_response(self, status, headers, exc_info=None):
        """Callback for starting a HTTP response."""
        if exc_info:
                if self.headers_sent: # Re-raise original exception
                    raise exc_info[0], exc_info[1], exc_info[2]
                exc_info = None # avoid dangling circular ref
            assert not self.headers_set, 'Response already started'

        self.headers_set = [status, headers]
        return self._write

    def _write(self, data):
        """Callback for writing data to the response.
        Concrete subclasses must implement this method."""
        raise NotImplementedError

class WSGIRequestHandler(BaseHTTPRequestHandler):

    def setup_environ(self):
        self.raw_requestline = self.rfile.readline()
        if (self.rfile.closed or              # disconnect
                not self.raw_requestline or   # empty request
                not self.parse_request()):    # invalid request
            self.close_connection = 1
            # note that in the latter case, an error code has already been sent

        environ = self.server.environ.copy()
        environ['SERVER_PROTOCOL'] = self.request_version
        environ['REQUEST_METHOD'] = self.command

        if '?' in self.path:
            path_info, query_string = self.path.split('?', 1)
            path_info, query_string = self.path, ''
        environ['PATH_INFO'] = urllib.unquote(path_info)
        environ['QUERY_STRING'] = query_string

        host = self.address_string()
        if host != self.client_address[0]:
            environ['REMOTE_HOST'] = host
        environ['REMOTE_ADDR'] = self.client_address[0]

        if self.headers.typeheader is None:
            environ['CONTENT_TYPE'] = self.headers.type
            environ['CONTENT_TYPE'] = self.headers.typeheader

        length = self.headers.getheader('content-length')
        if length:
            environ['CONTENT_LENGTH'] = length

        for name, value in [header.split(':', 1) for header
                            in self.headers.headers]:
            name = name.replace('-', '_').upper()
            value = value.strip()
            if name in environ:
                # skip content length, type, etc.
            if 'HTTP_' + name in environ:
                # comma-separate multiple headers
                environ['HTTP_' + name] += ',' + value
                environ['HTTP_' + name] = value

        return environ

    def handle_one_request(self):
            environ = self.setup_environ()
        except (IOError, socket.error), e:
            environ = None
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, 10053, 10054):
                # client disconnect
                self.close_connection = 1
        if environ: 
            gateway = self.server.gateway(self, environ)
        # else we had no request or a bad request: we simply exit (#3043)

    def finish(self):
        """We need to help the garbage collector a little."""
        self.wfile = None
        self.rfile = None

class WSGIServerGateway(WSGIGateway):

    def __init__(self, handler, environ):
        WSGIGateway.__init__(self, environ, handler.rfile,
                             _ErrorsWrapper(lambda x: handler.log_error('%s', x)))
        self.handler = handler

    def _write(self, data):
        assert self.headers_set, 'Response not started'
        if self.handler.wfile.closed:
            return # don't write to an already closed file (fix for #1183)

            if not self.headers_sent:
                status, headers = self.headers_sent = self.headers_set
                for name, value in headers:
                    self.handler.send_header(name, value)
        except (IOError, socket.error), e:
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, 10053, 10054):
                # client disconnect
                self.handler.close_connection = 1

class WSGIServer(HTTPServer):

    def __init__(self, server_address, application, gateway=WSGIServerGateway,
        HTTPServer.__init__(self, server_address, request_handler)

        self.application = application

        gateway.wsgi_multithread = isinstance(self, ThreadingMixIn)
        gateway.wsgi_multiprocess = isinstance(self, ForkingMixIn)
        self.gateway = gateway

        self.environ = {'SERVER_NAME': self.server_name,
                        'SERVER_PORT': str(self.server_port),
                        'SCRIPT_NAME': ''}