5 except ImportError: |
5 except ImportError: |
6 from StringIO import StringIO |
6 from StringIO import StringIO |
7 |
7 |
8 from django import http |
8 from django import http |
9 from django.core import signals |
9 from django.core import signals |
10 from django.core.handlers.base import BaseHandler |
10 from django.core.handlers import base |
11 from django.dispatch import dispatcher |
11 from django.core.urlresolvers import set_script_prefix |
12 from django.utils import datastructures |
12 from django.utils import datastructures |
13 from django.utils.encoding import force_unicode |
13 from django.utils.encoding import force_unicode |
14 |
14 |
15 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
15 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html |
16 STATUS_CODE_TEXT = { |
16 STATUS_CODE_TEXT = { |
72 fdst.write(buf) |
72 fdst.write(buf) |
73 size -= len(buf) |
73 size -= len(buf) |
74 |
74 |
75 class WSGIRequest(http.HttpRequest): |
75 class WSGIRequest(http.HttpRequest): |
76 def __init__(self, environ): |
76 def __init__(self, environ): |
|
77 script_name = base.get_script_name(environ) |
|
78 path_info = force_unicode(environ.get('PATH_INFO', u'/')) |
|
79 if not path_info or path_info == script_name: |
|
80 # Sometimes PATH_INFO exists, but is empty (e.g. accessing |
|
81 # the SCRIPT_NAME URL without a trailing slash). We really need to |
|
82 # operate as if they'd requested '/'. Not amazingly nice to force |
|
83 # the path like this, but should be harmless. |
|
84 # |
|
85 # (The comparison of path_info to script_name is to work around an |
|
86 # apparent bug in flup 1.0.1. Se Django ticket #8490). |
|
87 path_info = u'/' |
77 self.environ = environ |
88 self.environ = environ |
78 self.path = force_unicode(environ['PATH_INFO']) |
89 self.path_info = path_info |
|
90 self.path = '%s%s' % (script_name, path_info) |
79 self.META = environ |
91 self.META = environ |
|
92 self.META['PATH_INFO'] = path_info |
|
93 self.META['SCRIPT_NAME'] = script_name |
80 self.method = environ['REQUEST_METHOD'].upper() |
94 self.method = environ['REQUEST_METHOD'].upper() |
|
95 self._post_parse_error = False |
81 |
96 |
82 def __repr__(self): |
97 def __repr__(self): |
83 # Since this is called as part of error handling, we need to be very |
98 # Since this is called as part of error handling, we need to be very |
84 # robust against potentially malformed input. |
99 # robust against potentially malformed input. |
85 try: |
100 try: |
86 get = pformat(self.GET) |
101 get = pformat(self.GET) |
87 except: |
102 except: |
88 get = '<could not parse>' |
103 get = '<could not parse>' |
89 try: |
104 if self._post_parse_error: |
90 post = pformat(self.POST) |
|
91 except: |
|
92 post = '<could not parse>' |
105 post = '<could not parse>' |
|
106 else: |
|
107 try: |
|
108 post = pformat(self.POST) |
|
109 except: |
|
110 post = '<could not parse>' |
93 try: |
111 try: |
94 cookies = pformat(self.COOKIES) |
112 cookies = pformat(self.COOKIES) |
95 except: |
113 except: |
96 cookies = '<could not parse>' |
114 cookies = '<could not parse>' |
97 try: |
115 try: |
110 |
128 |
111 def _load_post_and_files(self): |
129 def _load_post_and_files(self): |
112 # Populates self._post and self._files |
130 # Populates self._post and self._files |
113 if self.method == 'POST': |
131 if self.method == 'POST': |
114 if self.environ.get('CONTENT_TYPE', '').startswith('multipart'): |
132 if self.environ.get('CONTENT_TYPE', '').startswith('multipart'): |
115 header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')]) |
133 self._raw_post_data = '' |
116 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '') |
134 try: |
117 self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data) |
135 self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input']) |
|
136 except: |
|
137 # An error occured while parsing POST data. Since when |
|
138 # formatting the error the request handler might access |
|
139 # self.POST, set self._post and self._file to prevent |
|
140 # attempts to parse POST data again. |
|
141 self._post = http.QueryDict('') |
|
142 self._files = datastructures.MultiValueDict() |
|
143 # Mark that an error occured. This allows self.__repr__ to |
|
144 # be explicit about it instead of simply representing an |
|
145 # empty POST |
|
146 self._post_parse_error = True |
|
147 raise |
118 else: |
148 else: |
119 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() |
149 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict() |
120 else: |
150 else: |
121 self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict() |
151 self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict() |
122 |
152 |
161 except AttributeError: |
191 except AttributeError: |
162 buf = StringIO() |
192 buf = StringIO() |
163 try: |
193 try: |
164 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd) |
194 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd) |
165 content_length = int(self.environ.get('CONTENT_LENGTH', 0)) |
195 content_length = int(self.environ.get('CONTENT_LENGTH', 0)) |
166 except ValueError: # if CONTENT_LENGTH was empty string or not an integer |
196 except (ValueError, TypeError): |
|
197 # If CONTENT_LENGTH was empty string or not an integer, don't |
|
198 # error out. We've also seen None passed in here (against all |
|
199 # specs, but see ticket #8259), so we handle TypeError as well. |
167 content_length = 0 |
200 content_length = 0 |
168 if content_length > 0: |
201 if content_length > 0: |
169 safe_copyfileobj(self.environ['wsgi.input'], buf, |
202 safe_copyfileobj(self.environ['wsgi.input'], buf, |
170 size=content_length) |
203 size=content_length) |
171 self._raw_post_data = buf.getvalue() |
204 self._raw_post_data = buf.getvalue() |
177 COOKIES = property(_get_cookies, _set_cookies) |
210 COOKIES = property(_get_cookies, _set_cookies) |
178 FILES = property(_get_files) |
211 FILES = property(_get_files) |
179 REQUEST = property(_get_request) |
212 REQUEST = property(_get_request) |
180 raw_post_data = property(_get_raw_post_data) |
213 raw_post_data = property(_get_raw_post_data) |
181 |
214 |
182 class WSGIHandler(BaseHandler): |
215 class WSGIHandler(base.BaseHandler): |
183 initLock = Lock() |
216 initLock = Lock() |
184 request_class = WSGIRequest |
217 request_class = WSGIRequest |
185 |
218 |
186 def __call__(self, environ, start_response): |
219 def __call__(self, environ, start_response): |
187 from django.conf import settings |
220 from django.conf import settings |
193 # Check that middleware is still uninitialised. |
226 # Check that middleware is still uninitialised. |
194 if self._request_middleware is None: |
227 if self._request_middleware is None: |
195 self.load_middleware() |
228 self.load_middleware() |
196 self.initLock.release() |
229 self.initLock.release() |
197 |
230 |
198 dispatcher.send(signal=signals.request_started) |
231 set_script_prefix(base.get_script_name(environ)) |
|
232 signals.request_started.send(sender=self.__class__) |
199 try: |
233 try: |
200 try: |
234 try: |
201 request = self.request_class(environ) |
235 request = self.request_class(environ) |
202 except UnicodeDecodeError: |
236 except UnicodeDecodeError: |
203 response = http.HttpResponseBadRequest() |
237 response = http.HttpResponseBadRequest() |
207 # Apply response middleware |
241 # Apply response middleware |
208 for middleware_method in self._response_middleware: |
242 for middleware_method in self._response_middleware: |
209 response = middleware_method(request, response) |
243 response = middleware_method(request, response) |
210 response = self.apply_response_fixes(request, response) |
244 response = self.apply_response_fixes(request, response) |
211 finally: |
245 finally: |
212 dispatcher.send(signal=signals.request_finished) |
246 signals.request_finished.send(sender=self.__class__) |
213 |
247 |
214 try: |
248 try: |
215 status_text = STATUS_CODE_TEXT[response.status_code] |
249 status_text = STATUS_CODE_TEXT[response.status_code] |
216 except KeyError: |
250 except KeyError: |
217 status_text = 'UNKNOWN STATUS CODE' |
251 status_text = 'UNKNOWN STATUS CODE' |