1 from django
.core
.handlers
.base
import BaseHandler
2 from django
.core
import signals
3 from django
.dispatch
import dispatcher
4 from django
.utils
import datastructures
5 from django
.utils
.encoding
import force_unicode
6 from django
import http
7 from pprint
import pformat
8 from shutil
import copyfileobj
9 from threading
import Lock
11 from cStringIO
import StringIO
13 from StringIO
import StringIO
15 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
18 101: 'SWITCHING PROTOCOLS',
22 203: 'NON-AUTHORITATIVE INFORMATION',
25 206: 'PARTIAL CONTENT',
26 300: 'MULTIPLE CHOICES',
27 301: 'MOVED PERMANENTLY',
33 307: 'TEMPORARY REDIRECT',
36 402: 'PAYMENT REQUIRED',
39 405: 'METHOD NOT ALLOWED',
40 406: 'NOT ACCEPTABLE',
41 407: 'PROXY AUTHENTICATION REQUIRED',
42 408: 'REQUEST TIMEOUT',
45 411: 'LENGTH REQUIRED',
46 412: 'PRECONDITION FAILED',
47 413: 'REQUEST ENTITY TOO LARGE',
48 414: 'REQUEST-URI TOO LONG',
49 415: 'UNSUPPORTED MEDIA TYPE',
50 416: 'REQUESTED RANGE NOT SATISFIABLE',
51 417: 'EXPECTATION FAILED',
52 500: 'INTERNAL SERVER ERROR',
53 501: 'NOT IMPLEMENTED',
55 503: 'SERVICE UNAVAILABLE',
56 504: 'GATEWAY TIMEOUT',
57 505: 'HTTP VERSION NOT SUPPORTED',
60 def safe_copyfileobj(fsrc
, fdst
, length
=16*1024, size
=0):
62 A version of shutil.copyfileobj that will not read more than 'size' bytes.
63 This makes it safe from clients sending more than CONTENT_LENGTH bytes of
69 buf
= fsrc
.read(min(length
, size
))
75 class WSGIRequest(http
.HttpRequest
):
76 def __init__(self
, environ
):
77 self
.environ
= environ
78 self
.path
= force_unicode(environ
['PATH_INFO'])
80 self
.method
= environ
['REQUEST_METHOD'].upper()
83 # Since this is called as part of error handling, we need to be very
84 # robust against potentially malformed input.
86 get
= pformat(self
.GET
)
88 get
= '<could not parse>'
90 post
= pformat(self
.POST
)
92 post
= '<could not parse>'
94 cookies
= pformat(self
.COOKIES
)
96 cookies
= '<could not parse>'
98 meta
= pformat(self
.META
)
100 meta
= '<could not parse>'
101 return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
102 (get
, post
, cookies
, meta
)
104 def get_full_path(self
):
105 return '%s%s' % (self
.path
, self
.environ
.get('QUERY_STRING', '') and ('?' + self
.environ
.get('QUERY_STRING', '')) or '')
108 return 'HTTPS' in self
.environ
and self
.environ
['HTTPS'] == 'on'
110 def _load_post_and_files(self
):
111 # Populates self._post and self._files
112 if self
.method
== 'POST':
113 if self
.environ
.get('CONTENT_TYPE', '').startswith('multipart'):
114 header_dict
= dict([(k
, v
) for k
, v
in self
.environ
.items() if k
.startswith('HTTP_')])
115 header_dict
['Content-Type'] = self
.environ
.get('CONTENT_TYPE', '')
116 self
._post
, self
._files
= http
.parse_file_upload(header_dict
, self
.raw_post_data
)
118 self
._post
, self
._files
= http
.QueryDict(self
.raw_post_data
, encoding
=self
._encoding
), datastructures
.MultiValueDict()
120 self
._post
, self
._files
= http
.QueryDict('', encoding
=self
._encoding
), datastructures
.MultiValueDict()
122 def _get_request(self
):
123 if not hasattr(self
, '_request'):
124 self
._request
= datastructures
.MergeDict(self
.POST
, self
.GET
)
128 if not hasattr(self
, '_get'):
129 # The WSGI spec says 'QUERY_STRING' may be absent.
130 self
._get
= http
.QueryDict(self
.environ
.get('QUERY_STRING', ''), encoding
=self
._encoding
)
133 def _set_get(self
, get
):
137 if not hasattr(self
, '_post'):
138 self
._load
_post
_and
_files
()
141 def _set_post(self
, post
):
144 def _get_cookies(self
):
145 if not hasattr(self
, '_cookies'):
146 self
._cookies
= http
.parse_cookie(self
.environ
.get('HTTP_COOKIE', ''))
149 def _set_cookies(self
, cookies
):
150 self
._cookies
= cookies
152 def _get_files(self
):
153 if not hasattr(self
, '_files'):
154 self
._load
_post
_and
_files
()
157 def _get_raw_post_data(self
):
159 return self
._raw
_post
_data
160 except AttributeError:
163 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
164 content_length
= int(self
.environ
.get('CONTENT_LENGTH', 0))
165 except ValueError: # if CONTENT_LENGTH was empty string or not an integer
167 safe_copyfileobj(self
.environ
['wsgi.input'], buf
, size
=content_length
)
168 self
._raw
_post
_data
= buf
.getvalue()
170 return self
._raw
_post
_data
172 GET
= property(_get_get
, _set_get
)
173 POST
= property(_get_post
, _set_post
)
174 COOKIES
= property(_get_cookies
, _set_cookies
)
175 FILES
= property(_get_files
)
176 REQUEST
= property(_get_request
)
177 raw_post_data
= property(_get_raw_post_data
)
179 class WSGIHandler(BaseHandler
):
182 def __call__(self
, environ
, start_response
):
183 from django
.conf
import settings
185 # Set up middleware if needed. We couldn't do this earlier, because
186 # settings weren't available.
187 if self
._request
_middleware
is None:
188 self
.initLock
.acquire()
189 # Check that middleware is still uninitialised.
190 if self
._request
_middleware
is None:
191 self
.load_middleware()
192 self
.initLock
.release()
194 dispatcher
.send(signal
=signals
.request_started
)
196 request
= WSGIRequest(environ
)
197 response
= self
.get_response(request
)
199 # Apply response middleware
200 for middleware_method
in self
._response
_middleware
:
201 response
= middleware_method(request
, response
)
204 dispatcher
.send(signal
=signals
.request_finished
)
207 status_text
= STATUS_CODE_TEXT
[response
.status_code
]
209 status_text
= 'UNKNOWN STATUS CODE'
210 status
= '%s %s' % (response
.status_code
, status_text
)
211 response_headers
= [(str(k
), str(v
)) for k
, v
in response
.headers
.items()]
212 for c
in response
.cookies
.values():
213 response_headers
.append(('Set-Cookie', str(c
.output(header
=''))))
214 start_response(status
, response_headers
)