web/lib/django/core/servers/basehttp.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
BaseHTTPServer that implements the Python WSGI protocol (PEP 333, rev 1.21).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
Adapted from wsgiref.simple_server: http://svn.eby-sarna.com/wsgiref/
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
This is a simple server for use in testing or debugging Django apps. It hasn't
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
been reviewed for security issues. Don't use it for production use.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
import mimetypes
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
import os
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
import re
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
import stat
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
import sys
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
import urllib
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
from django.core.management.color import color_style
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
from django.utils.http import http_date
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
from django.utils._os import safe_join
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
__version__ = "0.1"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
__all__ = ['WSGIServer','WSGIRequestHandler']
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
server_version = "WSGIServer/" + __version__
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
sys_version = "Python/" + sys.version.split()[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
software_version = server_version + ' ' + sys_version
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
class WSGIServerException(Exception):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
    pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
class FileWrapper(object):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
    """Wrapper to convert file-like objects to iterables"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
    def __init__(self, filelike, blksize=8192):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
        self.filelike = filelike
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
        self.blksize = blksize
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
        if hasattr(filelike,'close'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
            self.close = filelike.close
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    def __getitem__(self,key):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
        data = self.filelike.read(self.blksize)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
        if data:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
            return data
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
        raise IndexError
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    def __iter__(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
        return self
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
    def next(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        data = self.filelike.read(self.blksize)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
        if data:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
            return data
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
        raise StopIteration
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
# Regular expression that matches `special' characters in parameters, the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
# existence of which force quoting of the parameter value.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
def _formatparam(param, value=None, quote=1):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    """Convenience function to format and return a key=value pair.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    This will quote the value if needed or if quote is true.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    if value is not None and len(value) > 0:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        if quote or tspecials.search(value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
            value = value.replace('\\', '\\\\').replace('"', r'\"')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
            return '%s="%s"' % (param, value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
            return '%s=%s' % (param, value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        return param
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
class Headers(object):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
    """Manage a collection of HTTP response headers"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
    def __init__(self,headers):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
        if not isinstance(headers, list):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
            raise TypeError("Headers must be a list of name/value tuples")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
        self._headers = headers
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
    def __len__(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
        """Return the total number of headers, including duplicates."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        return len(self._headers)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
    def __setitem__(self, name, val):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        """Set the value of a header."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
        del self[name]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
        self._headers.append((name, val))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
    def __delitem__(self,name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
        """Delete all occurrences of a header, if present.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
        Does *not* raise an exception if the header is missing.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
        name = name.lower()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        self._headers[:] = [kv for kv in self._headers if kv[0].lower()<>name]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
    def __getitem__(self,name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
        """Get the first header value for 'name'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        Return None if the header is missing instead of raising an exception.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        Note that if the header appeared multiple times, the first exactly which
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        occurrance gets returned is undefined.  Use getall() to get all
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        the values matching a header field name.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        return self.get(name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
    def has_key(self, name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
        """Return true if the message contains the header."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
        return self.get(name) is not None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
    __contains__ = has_key
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
    def get_all(self, name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
        """Return a list of all the values for the named field.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
        These will be sorted in the order they appeared in the original header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
        list or were added to this instance, and may contain duplicates.  Any
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
        fields deleted and re-inserted are always appended to the header list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
        If no fields exist with the given name, returns an empty list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
        name = name.lower()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
        return [kv[1] for kv in self._headers if kv[0].lower()==name]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
    def get(self,name,default=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        """Get the first header value for 'name', or return 'default'"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        name = name.lower()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
        for k,v in self._headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
            if k.lower()==name:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
                return v
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
        return default
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
    def keys(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        """Return a list of all the header field names.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
        These will be sorted in the order they appeared in the original header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        list, or were added to this instance, and may contain duplicates.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
        Any fields deleted and re-inserted are always appended to the header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
        return [k for k, v in self._headers]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
    def values(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
        """Return a list of all header values.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
        These will be sorted in the order they appeared in the original header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
        list, or were added to this instance, and may contain duplicates.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
        Any fields deleted and re-inserted are always appended to the header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
        list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
        return [v for k, v in self._headers]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
    def items(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
        """Get all the header fields and values.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
        These will be sorted in the order they were in the original header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
        list, or were added to this instance, and may contain duplicates.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
        Any fields deleted and re-inserted are always appended to the header
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
        list.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
        return self._headers[:]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
    def __repr__(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
        return "Headers(%s)" % `self._headers`
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
    def __str__(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
        """str() returns the formatted headers, complete with end line,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
        suitable for direct HTTP transmission."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
        return '\r\n'.join(["%s: %s" % kv for kv in self._headers]+['',''])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
    def setdefault(self,name,value):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
        """Return first matching header value for 'name', or 'value'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
        If there is no header named 'name', add a new header with name 'name'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
        and value 'value'."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
        result = self.get(name)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
        if result is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
            self._headers.append((name,value))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
            return value
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
            return result
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
    def add_header(self, _name, _value, **_params):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        """Extended header setting.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
        _name is the header field to add.  keyword arguments can be used to set
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
        additional parameters for the header field, with underscores converted
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
        to dashes.  Normally the parameter will be added as key="value" unless
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
        value is None, in which case only the key will be added.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
        Example:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
        h.add_header('content-disposition', 'attachment', filename='bud.gif')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
        Note that unlike the corresponding 'email.Message' method, this does
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
        *not* handle '(charset, language, value)' tuples: all values must be
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        strings or None.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
        parts = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
        if _value is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
            parts.append(_value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
        for k, v in _params.items():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
            if v is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
                parts.append(k.replace('_', '-'))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
                parts.append(_formatparam(k.replace('_', '-'), v))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
        self._headers.append((_name, "; ".join(parts)))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
def guess_scheme(environ):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
    """Return a guess for whether 'wsgi.url_scheme' should be 'http' or 'https'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
    if environ.get("HTTPS") in ('yes','on','1'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
        return 'https'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
        return 'http'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
_hop_headers = {
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
    'connection':1, 'keep-alive':1, 'proxy-authenticate':1,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
    'proxy-authorization':1, 'te':1, 'trailers':1, 'transfer-encoding':1,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
    'upgrade':1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
def is_hop_by_hop(header_name):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
    """Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
    return header_name.lower() in _hop_headers
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
class ServerHandler(object):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
    """Manage the invocation of a WSGI application"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
    # Configuration parameters; can override per-subclass or per-instance
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
    wsgi_version = (1,0)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
    wsgi_multithread = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
    wsgi_multiprocess = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
    wsgi_run_once = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
    origin_server = True    # We are transmitting direct to client
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
    http_version  = "1.0"   # Version that should be used for response
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
    server_software = software_version
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
    # os_environ is used to supply configuration from the OS environment:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
    # by default it's a copy of 'os.environ' as of import time, but you can
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
    # override this in e.g. your __init__ method.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
    os_environ = dict(os.environ.items())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
    # Collaborator classes
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
    wsgi_file_wrapper = FileWrapper     # set to None to disable
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
    headers_class = Headers             # must be a Headers-like class
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
    # Error handling (also per-subclass or per-instance)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
    traceback_limit = None  # Print entire traceback to self.get_stderr()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
    error_status = "500 INTERNAL SERVER ERROR"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
    error_headers = [('Content-Type','text/plain')]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
    # State variables (don't mess with these)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
    status = result = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
    headers_sent = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
    headers = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
    bytes_sent = 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
    def __init__(self, stdin, stdout, stderr, environ, multithread=True,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
        multiprocess=False):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
        self.stdin = stdin
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
        self.stdout = stdout
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
        self.stderr = stderr
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
        self.base_env = environ
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
        self.wsgi_multithread = multithread
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
        self.wsgi_multiprocess = multiprocess
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
    def run(self, application):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
        """Invoke the application"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
        # Note to self: don't move the close()!  Asynchronous servers shouldn't
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
        # call close() from finish_response(), so if you close() anywhere but
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   275
        # the double-error branch here, you'll break asynchronous servers by
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   276
        # prematurely closing.  Async servers must return from 'run()' without
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   277
        # closing if there might still be output to iterate over.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   278
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   279
            self.setup_environ()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   280
            self.result = application(self.environ, self.start_response)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   281
            self.finish_response()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   282
        except:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   283
            try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   284
                self.handle_error()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   285
            except:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   286
                # If we get an error handling an error, just give up already!
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   287
                self.close()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   288
                raise   # ...and let the actual server figure it out.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   289
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   290
    def setup_environ(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   291
        """Set up the environment for one request"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   292
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   293
        env = self.environ = self.os_environ.copy()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   294
        self.add_cgi_vars()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   295
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   296
        env['wsgi.input']        = self.get_stdin()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   297
        env['wsgi.errors']       = self.get_stderr()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   298
        env['wsgi.version']      = self.wsgi_version
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   299
        env['wsgi.run_once']     = self.wsgi_run_once
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   300
        env['wsgi.url_scheme']   = self.get_scheme()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   301
        env['wsgi.multithread']  = self.wsgi_multithread
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   302
        env['wsgi.multiprocess'] = self.wsgi_multiprocess
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   303
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   304
        if self.wsgi_file_wrapper is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   305
            env['wsgi.file_wrapper'] = self.wsgi_file_wrapper
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   306
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   307
        if self.origin_server and self.server_software:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   308
            env.setdefault('SERVER_SOFTWARE',self.server_software)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   309
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   310
    def finish_response(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   311
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   312
        Send any iterable data, then close self and the iterable
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   313
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   314
        Subclasses intended for use in asynchronous servers will want to
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   315
        redefine this method, such that it sets up callbacks in the event loop
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   316
        to iterate over the data, and to call 'self.close()' once the response
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   317
        is finished.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   318
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   319
        if not self.result_is_file() or not self.sendfile():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   320
            for data in self.result:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   321
                self.write(data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   322
            self.finish_content()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   323
        self.close()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   324
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   325
    def get_scheme(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   326
        """Return the URL scheme being used"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   327
        return guess_scheme(self.environ)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   328
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   329
    def set_content_length(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   330
        """Compute Content-Length or switch to chunked encoding if possible"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   331
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   332
            blocks = len(self.result)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   333
        except (TypeError, AttributeError, NotImplementedError):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   334
            pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   335
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   336
            if blocks==1:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   337
                self.headers['Content-Length'] = str(self.bytes_sent)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   338
                return
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   339
        # XXX Try for chunked encoding if origin server and client is 1.1
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   340
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   341
    def cleanup_headers(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   342
        """Make any necessary header changes or defaults
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   343
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   344
        Subclasses can extend this to add other defaults.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   345
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   346
        if 'Content-Length' not in self.headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   347
            self.set_content_length()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   348
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   349
    def start_response(self, status, headers,exc_info=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   350
        """'start_response()' callable as specified by PEP 333"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   351
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   352
        if exc_info:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   353
            try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   354
                if self.headers_sent:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   355
                    # Re-raise original exception if headers sent
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   356
                    raise exc_info[0], exc_info[1], exc_info[2]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   357
            finally:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   358
                exc_info = None        # avoid dangling circular ref
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   359
        elif self.headers is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   360
            raise AssertionError("Headers already set!")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   361
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   362
        assert isinstance(status, str),"Status must be a string"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   363
        assert len(status)>=4,"Status must be at least 4 characters"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   364
        assert int(status[:3]),"Status message must begin w/3-digit code"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   365
        assert status[3]==" ", "Status message must have a space after code"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   366
        if __debug__:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   367
            for name,val in headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   368
                assert isinstance(name, str),"Header names must be strings"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   369
                assert isinstance(val, str),"Header values must be strings"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   370
                assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   371
        self.status = status
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   372
        self.headers = self.headers_class(headers)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   373
        return self.write
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   374
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   375
    def send_preamble(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   376
        """Transmit version/status/date/server, via self._write()"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   377
        if self.origin_server:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   378
            if self.client_is_modern():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   379
                self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   380
                if 'Date' not in self.headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   381
                    self._write(
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   382
                        'Date: %s\r\n' % http_date()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   383
                    )
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   384
                if self.server_software and 'Server' not in self.headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   385
                    self._write('Server: %s\r\n' % self.server_software)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   386
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   387
            self._write('Status: %s\r\n' % self.status)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   388
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   389
    def write(self, data):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   390
        """'write()' callable as specified by PEP 333"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   391
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   392
        assert isinstance(data, str), "write() argument must be string"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   393
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   394
        if not self.status:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   395
            raise AssertionError("write() before start_response()")
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   396
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   397
        elif not self.headers_sent:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   398
            # Before the first output, send the stored headers
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   399
            self.bytes_sent = len(data)    # make sure we know content-length
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   400
            self.send_headers()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   401
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   402
            self.bytes_sent += len(data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   403
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   404
        # XXX check Content-Length and truncate if too many bytes written?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   405
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   406
        # If data is too large, socket will choke, so write chunks no larger
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   407
        # than 32MB at a time.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   408
        length = len(data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   409
        if length > 33554432:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   410
            offset = 0
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   411
            while offset < length:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   412
                chunk_size = min(33554432, length)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   413
                self._write(data[offset:offset+chunk_size])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   414
                self._flush()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   415
                offset += chunk_size
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   416
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   417
            self._write(data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   418
            self._flush()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   419
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   420
    def sendfile(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   421
        """Platform-specific file transmission
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   422
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   423
        Override this method in subclasses to support platform-specific
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   424
        file transmission.  It is only called if the application's
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   425
        return iterable ('self.result') is an instance of
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   426
        'self.wsgi_file_wrapper'.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   427
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   428
        This method should return a true value if it was able to actually
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   429
        transmit the wrapped file-like object using a platform-specific
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   430
        approach.  It should return a false value if normal iteration
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   431
        should be used instead.  An exception can be raised to indicate
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   432
        that transmission was attempted, but failed.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   433
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   434
        NOTE: this method should call 'self.send_headers()' if
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   435
        'self.headers_sent' is false and it is going to attempt direct
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   436
        transmission of the file1.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   437
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   438
        return False   # No platform-specific transmission by default
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   439
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   440
    def finish_content(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   441
        """Ensure headers and content have both been sent"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   442
        if not self.headers_sent:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   443
            self.headers['Content-Length'] = "0"
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   444
            self.send_headers()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   445
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   446
            pass # XXX check if content-length was too short?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   447
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   448
    def close(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   449
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   450
            self.request_handler.log_request(self.status.split(' ',1)[0], self.bytes_sent)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   451
        finally:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   452
            try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   453
                if hasattr(self.result,'close'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   454
                    self.result.close()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   455
            finally:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   456
                self.result = self.headers = self.status = self.environ = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   457
                self.bytes_sent = 0; self.headers_sent = False
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   458
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   459
    def send_headers(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   460
        """Transmit headers to the client, via self._write()"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   461
        self.cleanup_headers()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   462
        self.headers_sent = True
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   463
        if not self.origin_server or self.client_is_modern():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   464
            self.send_preamble()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   465
            self._write(str(self.headers))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   466
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   467
    def result_is_file(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   468
        """True if 'self.result' is an instance of 'self.wsgi_file_wrapper'"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   469
        wrapper = self.wsgi_file_wrapper
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   470
        return wrapper is not None and isinstance(self.result,wrapper)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   471
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   472
    def client_is_modern(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   473
        """True if client can accept status and headers"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   474
        return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   475
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   476
    def log_exception(self,exc_info):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   477
        """Log the 'exc_info' tuple in the server log
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   478
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   479
        Subclasses may override to retarget the output or change its format.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   480
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   481
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   482
            from traceback import print_exception
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   483
            stderr = self.get_stderr()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   484
            print_exception(
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   485
                exc_info[0], exc_info[1], exc_info[2],
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   486
                self.traceback_limit, stderr
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   487
            )
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   488
            stderr.flush()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   489
        finally:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   490
            exc_info = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   491
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   492
    def handle_error(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   493
        """Log current error, and send error output to client if possible"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   494
        self.log_exception(sys.exc_info())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   495
        if not self.headers_sent:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   496
            self.result = self.error_output(self.environ, self.start_response)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   497
            self.finish_response()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   498
        # XXX else: attempt advanced recovery techniques for HTML or text?
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   499
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   500
    def error_output(self, environ, start_response):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   501
        import traceback
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   502
        start_response(self.error_status, self.error_headers[:], sys.exc_info())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   503
        return ['\n'.join(traceback.format_exception(*sys.exc_info()))]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   504
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   505
    # Pure abstract methods; *must* be overridden in subclasses
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   506
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   507
    def _write(self,data):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   508
        self.stdout.write(data)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   509
        self._write = self.stdout.write
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   510
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   511
    def _flush(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   512
        self.stdout.flush()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   513
        self._flush = self.stdout.flush
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   514
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   515
    def get_stdin(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   516
        return self.stdin
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   517
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   518
    def get_stderr(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   519
        return self.stderr
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   520
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   521
    def add_cgi_vars(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   522
        self.environ.update(self.base_env)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   523
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   524
class WSGIServer(HTTPServer):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   525
    """BaseHTTPServer that implements the Python WSGI protocol"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   526
    application = None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   527
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   528
    def server_bind(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   529
        """Override server_bind to store the server name."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   530
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   531
            HTTPServer.server_bind(self)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   532
        except Exception, e:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   533
            raise WSGIServerException(e)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   534
        self.setup_environ()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   535
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   536
    def setup_environ(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   537
        # Set up base environment
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   538
        env = self.base_environ = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   539
        env['SERVER_NAME'] = self.server_name
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   540
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   541
        env['SERVER_PORT'] = str(self.server_port)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   542
        env['REMOTE_HOST']=''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   543
        env['CONTENT_LENGTH']=''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   544
        env['SCRIPT_NAME'] = ''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   545
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   546
    def get_app(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   547
        return self.application
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   548
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   549
    def set_app(self,application):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   550
        self.application = application
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   551
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   552
class WSGIRequestHandler(BaseHTTPRequestHandler):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   553
    server_version = "WSGIServer/" + __version__
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   554
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   555
    def __init__(self, *args, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   556
        from django.conf import settings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   557
        self.admin_media_prefix = settings.ADMIN_MEDIA_PREFIX
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   558
        # We set self.path to avoid crashes in log_message() on unsupported
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   559
        # requests (like "OPTIONS").
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   560
        self.path = ''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   561
        self.style = color_style()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   562
        BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   563
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   564
    def get_environ(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   565
        env = self.server.base_environ.copy()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   566
        env['SERVER_PROTOCOL'] = self.request_version
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   567
        env['REQUEST_METHOD'] = self.command
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   568
        if '?' in self.path:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   569
            path,query = self.path.split('?',1)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   570
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   571
            path,query = self.path,''
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   572
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   573
        env['PATH_INFO'] = urllib.unquote(path)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   574
        env['QUERY_STRING'] = query
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   575
        env['REMOTE_ADDR'] = self.client_address[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   576
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   577
        if self.headers.typeheader is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   578
            env['CONTENT_TYPE'] = self.headers.type
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   579
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   580
            env['CONTENT_TYPE'] = self.headers.typeheader
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   581
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   582
        length = self.headers.getheader('content-length')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   583
        if length:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   584
            env['CONTENT_LENGTH'] = length
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   585
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   586
        for h in self.headers.headers:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   587
            k,v = h.split(':',1)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   588
            k=k.replace('-','_').upper(); v=v.strip()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   589
            if k in env:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   590
                continue                    # skip content length, type,etc.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   591
            if 'HTTP_'+k in env:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   592
                env['HTTP_'+k] += ','+v     # comma-separate multiple headers
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   593
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   594
                env['HTTP_'+k] = v
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   595
        return env
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   596
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   597
    def get_stderr(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   598
        return sys.stderr
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   599
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   600
    def handle(self):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   601
        """Handle a single HTTP request"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   602
        self.raw_requestline = self.rfile.readline()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   603
        if not self.parse_request(): # An error code has been sent, just exit
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   604
            return
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   605
        handler = ServerHandler(self.rfile, self.wfile, self.get_stderr(), self.get_environ())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   606
        handler.request_handler = self      # backpointer for logging
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   607
        handler.run(self.server.get_app())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   608
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   609
    def log_message(self, format, *args):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   610
        # Don't bother logging requests for admin images or the favicon.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   611
        if self.path.startswith(self.admin_media_prefix) or self.path == '/favicon.ico':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   612
            return
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   613
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   614
        msg = "[%s] %s\n" % (self.log_date_time_string(), format % args)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   615
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   616
        # Utilize terminal colors, if available
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   617
        if args[1][0] == '2':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   618
            # Put 2XX first, since it should be the common case
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   619
            msg = self.style.HTTP_SUCCESS(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   620
        elif args[1][0] == '1':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   621
            msg = self.style.HTTP_INFO(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   622
        elif args[1] == '304':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   623
            msg = self.style.HTTP_NOT_MODIFIED(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   624
        elif args[1][0] == '3':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   625
            msg = self.style.HTTP_REDIRECT(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   626
        elif args[1] == '404':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   627
            msg = self.style.HTTP_NOT_FOUND(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   628
        elif args[1][0] == '4':
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   629
            msg = self.style.HTTP_BAD_REQUEST(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   630
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   631
            # Any 5XX, or any other response
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   632
            msg = self.style.HTTP_SERVER_ERROR(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   633
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   634
        sys.stderr.write(msg)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   635
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   636
class AdminMediaHandler(object):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   637
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   638
    WSGI middleware that intercepts calls to the admin media directory, as
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   639
    defined by the ADMIN_MEDIA_PREFIX setting, and serves those images.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   640
    Use this ONLY LOCALLY, for development! This hasn't been tested for
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   641
    security and is not super efficient.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   642
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   643
    def __init__(self, application, media_dir=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   644
        from django.conf import settings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   645
        self.application = application
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   646
        if not media_dir:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   647
            import django
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   648
            self.media_dir = \
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   649
                os.path.join(django.__path__[0], 'contrib', 'admin', 'media')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   650
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   651
            self.media_dir = media_dir
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   652
        self.media_url = settings.ADMIN_MEDIA_PREFIX
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   653
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   654
    def file_path(self, url):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   655
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   656
        Returns the path to the media file on disk for the given URL.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   657
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   658
        The passed URL is assumed to begin with ADMIN_MEDIA_PREFIX.  If the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   659
        resultant file path is outside the media directory, then a ValueError
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   660
        is raised.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   661
        """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   662
        # Remove ADMIN_MEDIA_PREFIX.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   663
        relative_url = url[len(self.media_url):]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   664
        relative_path = urllib.url2pathname(relative_url)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   665
        return safe_join(self.media_dir, relative_path)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   666
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   667
    def __call__(self, environ, start_response):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   668
        import os.path
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   669
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   670
        # Ignore requests that aren't under ADMIN_MEDIA_PREFIX. Also ignore
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   671
        # all requests if ADMIN_MEDIA_PREFIX isn't a relative URL.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   672
        if self.media_url.startswith('http://') or self.media_url.startswith('https://') \
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   673
            or not environ['PATH_INFO'].startswith(self.media_url):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   674
            return self.application(environ, start_response)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   675
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   676
        # Find the admin file and serve it up, if it exists and is readable.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   677
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   678
            file_path = self.file_path(environ['PATH_INFO'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   679
        except ValueError: # Resulting file path was not valid.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   680
            status = '404 NOT FOUND'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   681
            headers = {'Content-type': 'text/plain'}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   682
            output = ['Page not found: %s' % environ['PATH_INFO']]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   683
            start_response(status, headers.items())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   684
            return output
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   685
        if not os.path.exists(file_path):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   686
            status = '404 NOT FOUND'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   687
            headers = {'Content-type': 'text/plain'}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   688
            output = ['Page not found: %s' % environ['PATH_INFO']]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   689
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   690
            try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   691
                fp = open(file_path, 'rb')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   692
            except IOError:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   693
                status = '401 UNAUTHORIZED'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   694
                headers = {'Content-type': 'text/plain'}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   695
                output = ['Permission denied: %s' % environ['PATH_INFO']]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   696
            else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   697
                # This is a very simple implementation of conditional GET with
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   698
                # the Last-Modified header. It makes media files a bit speedier
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   699
                # because the files are only read off disk for the first
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   700
                # request (assuming the browser/client supports conditional
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   701
                # GET).
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   702
                mtime = http_date(os.stat(file_path)[stat.ST_MTIME])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   703
                headers = {'Last-Modified': mtime}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   704
                if environ.get('HTTP_IF_MODIFIED_SINCE', None) == mtime:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   705
                    status = '304 NOT MODIFIED'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   706
                    output = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   707
                else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   708
                    status = '200 OK'
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   709
                    mime_type = mimetypes.guess_type(file_path)[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   710
                    if mime_type:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   711
                        headers['Content-Type'] = mime_type
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   712
                    output = [fp.read()]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   713
                    fp.close()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   714
        start_response(status, headers.items())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   715
        return output
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   716
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   717
def run(addr, port, wsgi_handler):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   718
    server_address = (addr, port)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   719
    httpd = WSGIServer(server_address, WSGIRequestHandler)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   720
    httpd.set_app(wsgi_handler)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   721
    httpd.serve_forever()