Ticket #9886: 9886.3.diff
File 9886.3.diff, 16.3 KB (added by , 15 years ago) |
---|
-
django/core/handlers/modpython.py
=== modified file 'django/core/handlers/modpython.py'
36 36 # naughty, but also pretty harmless. 37 37 self.path_info = u'/' 38 38 self._post_parse_error = False 39 self._stream = self._req 40 self._read_started = False 39 41 40 42 def __repr__(self): 41 43 # Since this is called as part of error handling, we need to be very … … 75 77 # mod_python < 3.2.10 doesn't have req.is_https(). 76 78 return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1') 77 79 78 def _load_post_and_files(self):79 "Populates self._post and self._files"80 if self.method != 'POST':81 self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict()82 return83 84 if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'):85 self._raw_post_data = ''86 try:87 self._post, self._files = self.parse_file_upload(self.META, self._req)88 except:89 # See django.core.handlers.wsgi.WSGIHandler for an explanation90 # of what's going on here.91 self._post = http.QueryDict('')92 self._files = datastructures.MultiValueDict()93 self._post_parse_error = True94 raise95 else:96 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()97 98 80 def _get_request(self): 99 81 if not hasattr(self, '_request'): 100 82 self._request = datastructures.MergeDict(self.POST, self.GET) … … 156 138 self._meta[key] = value 157 139 return self._meta 158 140 159 def _get_raw_post_data(self):160 try:161 return self._raw_post_data162 except AttributeError:163 self._raw_post_data = self._req.read()164 return self._raw_post_data165 166 141 def _get_method(self): 167 142 return self.META['REQUEST_METHOD'].upper() 168 143 … … 172 147 FILES = property(_get_files) 173 148 META = property(_get_meta) 174 149 REQUEST = property(_get_request) 175 raw_post_data = property(_get_raw_post_data)176 150 method = property(_get_method) 177 151 178 152 class ModPythonHandler(BaseHandler): -
django/core/handlers/wsgi.py
=== modified file 'django/core/handlers/wsgi.py'
4 4 from cStringIO import StringIO 5 5 except ImportError: 6 6 from StringIO import StringIO 7 import socket 7 8 8 9 from django import http 9 10 from django.core import signals … … 57 58 505: 'HTTP VERSION NOT SUPPORTED', 58 59 } 59 60 60 def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0): 61 """ 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 64 data in the body. 65 """ 66 if not size: 67 return 68 while size > 0: 69 buf = fsrc.read(min(length, size)) 70 if not buf: 71 break 72 fdst.write(buf) 73 size -= len(buf) 61 class LimitedStream(object): 62 ''' 63 LimitedStream wraps another stream in order to not allow reading from it 64 past specified amount of bytes. 65 ''' 66 def __init__(self, stream, limit, buf_size=64 * 1024 * 1024): 67 self.stream = stream 68 self.remaining = limit 69 self.buffer = '' 70 self.buf_size = buf_size 71 72 def _read_limited(self, size=None): 73 if size is None or size > self.remaining: 74 size = self.remaining 75 if size == 0: 76 return '' 77 result = self.stream.read(size) 78 self.remaining -= len(result) 79 return result 80 81 def read(self, size=None): 82 if size is None: 83 result = self.buffer + self._read_limited() 84 self.buffer = '' 85 elif size < len(self.buffer): 86 result = self.buffer[:size] 87 self.buffer = self.buffer[size:] 88 else: # size >= len(self.buffer) 89 result = self.buffer + self._read_limited(size - len(self.buffer)) 90 self.buffer = '' 91 return result 92 93 def readline(self, size=None): 94 while '\n' not in self.buffer or \ 95 (size is not None and len(self.buffer) < size): 96 chunk = self._read_limited(self.buf_size) 97 if not chunk: 98 break 99 self.buffer += chunk 100 sio = StringIO(self.buffer) 101 line = sio.readline() 102 self.buffer = sio.read() 103 return line 74 104 75 105 class WSGIRequest(http.HttpRequest): 76 106 def __init__(self, environ): … … 93 123 self.META['SCRIPT_NAME'] = script_name 94 124 self.method = environ['REQUEST_METHOD'].upper() 95 125 self._post_parse_error = False 126 if isinstance(self.environ['wsgi.input'], socket._fileobject): 127 # Under development server 'wsgi.input' is an instance of 128 # socket._fileobject which hangs indefinitely on reading bytes past 129 # available count. To prevent this it's wrapped in LimitedStream 130 # that doesn't read past Content-Length bytes. 131 # 132 # This is not done for other kinds of inputs (like flup's FastCGI 133 # streams) beacuse they don't suffer from this problem and we can 134 # avoid using another wrapper with its own .read and .readline 135 # implementation. 136 try: 137 content_length = int(self.environ.get('CONTENT_LENGTH', 0)) 138 except (ValueError, TypeError): 139 content_length = 0 140 self._stream = LimitedStream(self.environ['wsgi.input'], content_length) 141 else: 142 self._stream = self.environ['wsgi.input'] 143 self._read_started = False 96 144 97 145 def __repr__(self): 98 146 # Since this is called as part of error handling, we need to be very … … 128 176 return 'wsgi.url_scheme' in self.environ \ 129 177 and self.environ['wsgi.url_scheme'] == 'https' 130 178 131 def _load_post_and_files(self):132 # Populates self._post and self._files133 if self.method == 'POST':134 if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):135 self._raw_post_data = ''136 try:137 self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input'])138 except:139 # An error occured while parsing POST data. Since when140 # formatting the error the request handler might access141 # self.POST, set self._post and self._file to prevent142 # attempts to parse POST data again.143 self._post = http.QueryDict('')144 self._files = datastructures.MultiValueDict()145 # Mark that an error occured. This allows self.__repr__ to146 # be explicit about it instead of simply representing an147 # empty POST148 self._post_parse_error = True149 raise150 else:151 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()152 else:153 self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict()154 155 179 def _get_request(self): 156 180 if not hasattr(self, '_request'): 157 181 self._request = datastructures.MergeDict(self.POST, self.GET) … … 187 211 self._load_post_and_files() 188 212 return self._files 189 213 190 def _get_raw_post_data(self):191 try:192 return self._raw_post_data193 except AttributeError:194 buf = StringIO()195 try:196 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)197 content_length = int(self.environ.get('CONTENT_LENGTH', 0))198 except (ValueError, TypeError):199 # If CONTENT_LENGTH was empty string or not an integer, don't200 # error out. We've also seen None passed in here (against all201 # specs, but see ticket #8259), so we handle TypeError as well.202 content_length = 0203 if content_length > 0:204 safe_copyfileobj(self.environ['wsgi.input'], buf,205 size=content_length)206 self._raw_post_data = buf.getvalue()207 buf.close()208 return self._raw_post_data209 210 214 GET = property(_get_get, _set_get) 211 215 POST = property(_get_post, _set_post) 212 216 COOKIES = property(_get_cookies, _set_cookies) 213 217 FILES = property(_get_files) 214 218 REQUEST = property(_get_request) 215 raw_post_data = property(_get_raw_post_data)216 219 217 220 class WSGIHandler(base.BaseHandler): 218 221 initLock = Lock() -
django/http/__init__.py
=== modified file 'django/http/__init__.py'
5 5 from urllib import urlencode 6 6 from urlparse import urljoin 7 7 try: 8 from cStringIO import StringIO 9 except ImportError: 10 from StringIO import StringIO 11 try: 8 12 # The mod_python version is more efficient, so try importing it first. 9 13 from mod_python.util import parse_qsl 10 14 except ImportError: … … 123 127 parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) 124 128 return parser.parse() 125 129 130 def _get_raw_post_data(self): 131 if not hasattr(self, '_raw_post_data'): 132 if self._read_started: 133 raise Exception("You cannot access raw_post_data after reading from request's data stream") 134 self._raw_post_data = self.read() 135 self._stream = StringIO(self._raw_post_data) 136 return self._raw_post_data 137 raw_post_data = property(_get_raw_post_data) 138 139 def _mark_post_parse_error(self): 140 self._post = QueryDict('') 141 self._files = MultiValueDict() 142 self._post_parse_error = True 143 144 def _load_post_and_files(self): 145 # Populates self._post and self._files 146 if self.method != 'POST': 147 self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict() 148 return 149 if self._read_started: 150 self._mark_post_parse_error() 151 return 152 153 if self.META.get('CONTENT_TYPE', '').startswith('multipart'): 154 self._raw_post_data = '' 155 try: 156 self._post, self._files = self.parse_file_upload(self.META, self) 157 except: 158 # An error occured while parsing POST data. Since when 159 # formatting the error the request handler might access 160 # self.POST, set self._post and self._file to prevent 161 # attempts to parse POST data again. 162 # Mark that an error occured. This allows self.__repr__ to 163 # be explicit about it instead of simply representing an 164 # empty POST 165 self._mark_post_parse_error() 166 raise 167 else: 168 self._post, self._files = QueryDict(self.raw_post_data, encoding=self._encoding), MultiValueDict() 169 170 ## File-like and iterator interface. 171 ## 172 ## Expects self._stream to be set to an appropriate source of bytes by 173 ## a corresponding request subclass (WSGIRequest or ModPythonRequest). 174 ## Also when request data has already been read by request.POST or 175 ## request.raw_post_data self._stream pooints to a StringIO instance 176 ## containing that data. 177 178 def read(self, *args, **kwargs): 179 self._read_started = True 180 return self._stream.read(*args, **kwargs) 181 182 def readline(self, *args, **kwargs): 183 self._read_started = True 184 return self._stream.readline(*args, **kwargs) 185 186 def xreadlines(self): 187 while True: 188 buf = self.readline() 189 if not buf: 190 break 191 yield buf 192 __iter__ = xreadlines 193 194 def readlines(self): 195 return list(iter(self)) 196 126 197 class QueryDict(MultiValueDict): 127 198 """ 128 199 A specialized MultiValueDict that takes a query string when initialized. … … 189 260 for key, value in dict.items(self): 190 261 dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) 191 262 return result 192 263 193 264 def setlist(self, key, list_): 194 265 self._assert_mutable() 195 266 key = str_to_unicode(key, self.encoding) -
docs/ref/request-response.txt
=== modified file 'docs/ref/request-response.txt'
176 176 177 177 .. attribute:: HttpRequest.raw_post_data 178 178 179 The raw HTTP POST data. This is only useful for advanced processing. Use 180 ``POST`` instead. 179 The raw HTTP POST data as a byte string. This is useful for processing 180 data in different formats than of conventional HTML forms: binary images, 181 XML payload etc. For processing form data use ``HttpRequest.POST``. 182 183 .. versionadded:: 1.2 184 185 You can also read from an HttpRequest using file-like interface, see 186 ``HttpRequest.read()`` 181 187 182 188 .. attribute:: HttpRequest.urlconf 183 189 … … 236 242 If you write your own XMLHttpRequest call (on the browser side), you'll 237 243 have to set this header manually if you want ``is_ajax()`` to work. 238 244 245 .. method:: HttpRequest.read(size=None) 246 .. method:: HttpRequest.readline() 247 .. method:: HttpRequest.readlines() 248 .. method:: HttpRequest.xreadlines() 249 .. method:: HttpRequest.__iter__() 250 251 .. versionadded:: 1.2 252 253 Methods implement file-like interface for reading from an HttpRequest 254 instance. This makes it possible to process an incoming request in a 255 streaming fashion. A common use-case is processing a big XML payload with 256 iterative parser without constructing a whole XML tree in memory. 257 258 Given this standard interface an HttpRequest instance can be passed directly 259 to an XML parser, for example ElementTree:: 260 261 import xml.etree.ElementTree as ET 262 for element in ET.iterparse(request): 263 process(element) 264 239 265 240 266 QueryDict objects 241 267 ----------------- -
tests/regressiontests/requests/tests.py
=== modified file 'tests/regressiontests/requests/tests.py'
7 7 COOKIES:{}, 8 8 META:{}> 9 9 10 >>> from StringIO import StringIO 10 11 >>> from django.core.handlers.wsgi import WSGIRequest 11 >>> print repr(WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus' }))12 >>> print repr(WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus', 'wsgi.input': StringIO()})) 12 13 <WSGIRequest 13 14 GET:<QueryDict: {}>, 14 15 POST:<QueryDict: {}>, … … 44 45 >>> request.path = '' 45 46 >>> print request.build_absolute_uri(location="/path/with:colons") 46 47 http://www.example.com/path/with:colons 48 49 50 >>> from django.core.handlers.wsgi import LimitedStream 51 >>> stream = LimitedStream(StringIO('test'), 2) 52 >>> stream.read() 53 'te' 54 >>> stream = LimitedStream(StringIO('test'), 2) 55 >>> stream.read(5) 56 'te' 57 58 59 Basic test of file-like reading from request 60 >>> request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')}) 61 >>> request.read() 62 'name=value' 63 64 65 Reading from request is allowed after accessing request contents as POST or raw_post_data 66 >>> request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')}) 67 >>> request.POST 68 <QueryDict: {u'name': [u'value']}> 69 >>> request.raw_post_data 70 'name=value' 71 >>> request.read() 72 'name=value' 73 74 Construction of POST or raw_post_data is not allowed after reading from request 75 >>> request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')}) 76 >>> request.read(2) 77 'na' 78 >>> request.raw_post_data 79 Traceback (most recent call last): 80 File "<stdin>", line 1, in ? 81 raise Exception("You cannot access raw_post_data after reading from request's data stream") 82 Exception: You cannot access raw_post_data after reading from request's data stream 83 >>> request.POST 84 <QueryDict: {}> 85 86 Request can be read by lines 87 >>> request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')}) 88 >>> [l for l in request] 89 ['name=value'] 90 47 91 """