App Engine Python SDK version 1.7.4 (2)
[gae.git] / python / lib / django_1_4 / django / core / servers / basehttp.py
blob8d4ceabfc32347c6cdea491a51a1c546f6609aa5
1 """
2 HTTP server that implements the Python WSGI protocol (PEP 333, rev 1.21).
4 Based on wsgiref.simple_server which is part of the standard library since 2.5.
6 This is a simple server for use in testing or debugging Django apps. It hasn't
7 been reviewed for security issues. DON'T USE IT FOR PRODUCTION USE!
8 """
10 import os
11 import socket
12 import sys
13 import traceback
14 import urllib
15 import urlparse
16 from SocketServer import ThreadingMixIn
17 from wsgiref import simple_server
18 from wsgiref.util import FileWrapper # for backwards compatibility
20 import django
21 from django.core.exceptions import ImproperlyConfigured
22 from django.core.management.color import color_style
23 from django.core.wsgi import get_wsgi_application
24 from django.utils.importlib import import_module
25 from django.utils._os import safe_join
26 from django.views import static
28 from django.contrib.staticfiles import handlers
30 __all__ = ['WSGIServer', 'WSGIRequestHandler']
33 def get_internal_wsgi_application():
34 """
35 Loads and returns the WSGI application as configured by the user in
36 ``settings.WSGI_APPLICATION``. With the default ``startproject`` layout,
37 this will be the ``application`` object in ``projectname/wsgi.py``.
39 This function, and the ``WSGI_APPLICATION`` setting itself, are only useful
40 for Django's internal servers (runserver, runfcgi); external WSGI servers
41 should just be configured to point to the correct application object
42 directly.
44 If settings.WSGI_APPLICATION is not set (is ``None``), we just return
45 whatever ``django.core.wsgi.get_wsgi_application`` returns.
47 """
48 from django.conf import settings
49 app_path = getattr(settings, 'WSGI_APPLICATION')
50 if app_path is None:
51 return get_wsgi_application()
52 module_name, attr = app_path.rsplit('.', 1)
53 try:
54 mod = import_module(module_name)
55 except ImportError, e:
56 raise ImproperlyConfigured(
57 "WSGI application '%s' could not be loaded; "
58 "could not import module '%s': %s" % (app_path, module_name, e))
59 try:
60 app = getattr(mod, attr)
61 except AttributeError, e:
62 raise ImproperlyConfigured(
63 "WSGI application '%s' could not be loaded; "
64 "can't find '%s' in module '%s': %s"
65 % (app_path, attr, module_name, e))
67 return app
70 class WSGIServerException(Exception):
71 pass
74 class ServerHandler(simple_server.ServerHandler, object):
75 error_status = "500 INTERNAL SERVER ERROR"
77 def write(self, data):
78 """'write()' callable as specified by PEP 333"""
80 assert isinstance(data, str), "write() argument must be string"
82 if not self.status:
83 raise AssertionError("write() before start_response()")
85 elif not self.headers_sent:
86 # Before the first output, send the stored headers
87 self.bytes_sent = len(data) # make sure we know content-length
88 self.send_headers()
89 else:
90 self.bytes_sent += len(data)
92 # XXX check Content-Length and truncate if too many bytes written?
94 # If data is too large, socket will choke, so write chunks no larger
95 # than 32MB at a time.
96 length = len(data)
97 if length > 33554432:
98 offset = 0
99 while offset < length:
100 chunk_size = min(33554432, length)
101 self._write(data[offset:offset+chunk_size])
102 self._flush()
103 offset += chunk_size
104 else:
105 self._write(data)
106 self._flush()
108 def error_output(self, environ, start_response):
109 super(ServerHandler, self).error_output(environ, start_response)
110 return ['\n'.join(traceback.format_exception(*sys.exc_info()))]
113 class WSGIServer(simple_server.WSGIServer, object):
114 """BaseHTTPServer that implements the Python WSGI protocol"""
116 def __init__(self, *args, **kwargs):
117 if kwargs.pop('ipv6', False):
118 self.address_family = socket.AF_INET6
119 super(WSGIServer, self).__init__(*args, **kwargs)
121 def server_bind(self):
122 """Override server_bind to store the server name."""
123 try:
124 super(WSGIServer, self).server_bind()
125 except Exception, e:
126 raise WSGIServerException(e)
127 self.setup_environ()
130 class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
132 def __init__(self, *args, **kwargs):
133 from django.conf import settings
134 self.admin_media_prefix = urlparse.urljoin(settings.STATIC_URL, 'admin/')
135 # We set self.path to avoid crashes in log_message() on unsupported
136 # requests (like "OPTIONS").
137 self.path = ''
138 self.style = color_style()
139 super(WSGIRequestHandler, self).__init__(*args, **kwargs)
141 def get_environ(self):
142 env = self.server.base_environ.copy()
143 env['SERVER_PROTOCOL'] = self.request_version
144 env['REQUEST_METHOD'] = self.command
145 if '?' in self.path:
146 path,query = self.path.split('?',1)
147 else:
148 path,query = self.path,''
150 env['PATH_INFO'] = urllib.unquote(path)
151 env['QUERY_STRING'] = query
152 env['REMOTE_ADDR'] = self.client_address[0]
154 if self.headers.typeheader is None:
155 env['CONTENT_TYPE'] = self.headers.type
156 else:
157 env['CONTENT_TYPE'] = self.headers.typeheader
159 length = self.headers.getheader('content-length')
160 if length:
161 env['CONTENT_LENGTH'] = length
163 for h in self.headers.headers:
164 k,v = h.split(':',1)
165 k=k.replace('-','_').upper(); v=v.strip()
166 if k in env:
167 continue # skip content length, type,etc.
168 if 'HTTP_'+k in env:
169 env['HTTP_'+k] += ','+v # comma-separate multiple headers
170 else:
171 env['HTTP_'+k] = v
172 return env
174 def log_message(self, format, *args):
175 # Don't bother logging requests for admin images or the favicon.
176 if (self.path.startswith(self.admin_media_prefix)
177 or self.path == '/favicon.ico'):
178 return
180 msg = "[%s] %s\n" % (self.log_date_time_string(), format % args)
182 # Utilize terminal colors, if available
183 if args[1][0] == '2':
184 # Put 2XX first, since it should be the common case
185 msg = self.style.HTTP_SUCCESS(msg)
186 elif args[1][0] == '1':
187 msg = self.style.HTTP_INFO(msg)
188 elif args[1] == '304':
189 msg = self.style.HTTP_NOT_MODIFIED(msg)
190 elif args[1][0] == '3':
191 msg = self.style.HTTP_REDIRECT(msg)
192 elif args[1] == '404':
193 msg = self.style.HTTP_NOT_FOUND(msg)
194 elif args[1][0] == '4':
195 msg = self.style.HTTP_BAD_REQUEST(msg)
196 else:
197 # Any 5XX, or any other response
198 msg = self.style.HTTP_SERVER_ERROR(msg)
200 sys.stderr.write(msg)
203 class AdminMediaHandler(handlers.StaticFilesHandler):
205 WSGI middleware that intercepts calls to the admin media directory, as
206 defined by the STATIC_URL setting, and serves those images.
207 Use this ONLY LOCALLY, for development! This hasn't been tested for
208 security and is not super efficient.
210 This is pending for deprecation since 1.3.
212 def get_base_dir(self):
213 return os.path.join(django.__path__[0], 'contrib', 'admin', 'static', 'admin')
215 def get_base_url(self):
216 from django.conf import settings
217 return urlparse.urljoin(settings.STATIC_URL, 'admin/')
219 def file_path(self, url):
221 Returns the path to the media file on disk for the given URL.
223 The passed URL is assumed to begin with ``self.base_url``. If the
224 resulting file path is outside the media directory, then a ValueError
225 is raised.
227 relative_url = url[len(self.base_url[2]):]
228 relative_path = urllib.url2pathname(relative_url)
229 return safe_join(self.base_dir, relative_path)
231 def serve(self, request):
232 document_root, path = os.path.split(self.file_path(request.path))
233 return static.serve(request, path, document_root=document_root)
235 def _should_handle(self, path):
237 Checks if the path should be handled. Ignores the path if:
239 * the host is provided as part of the base_url
240 * the request's path isn't under the base path
242 return path.startswith(self.base_url[2]) and not self.base_url[1]
245 def run(addr, port, wsgi_handler, ipv6=False, threading=False):
246 server_address = (addr, port)
247 if threading:
248 httpd_cls = type('WSGIServer', (ThreadingMixIn, WSGIServer), {})
249 else:
250 httpd_cls = WSGIServer
251 httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
252 httpd.set_app(wsgi_handler)
253 httpd.serve_forever()