The most important rule about web development is “Do not trust the user”. This is especially true for incoming request data on the input stream. With WSGI this is actually a bit harder than you would expect. Because of that Werkzeug wraps the request stream for you to save you from the most prominent problems with it.
Missing EOF Marker on Input Stream
The input stream has no end-of-file marker. If you would call the
read()
method on the [UNKNOWN NODE title_reference] stream you would cause your
application to hang on conforming servers. This is actually intentional
however painful. Werkzeug solves that problem by wrapping the input
stream in a special LimitedStream
. The input stream is exposed
on the request objects as stream
. This one is either
an empty stream (if the form data was parsed) or a limited stream with
the contents of the input stream.
When does Werkzeug Parse?
Werkzeug parses the incoming data under the following situations:
- you access either
form
,files
, orstream
and the request method was [UNKNOWN NODE title_reference] or [UNKNOWN NODE title_reference]. - if you call
parse_form_data()
.
These calls are not interchangeable. If you invoke parse_form_data()
you must not use the request object or at least not the attributes that
trigger the parsing process.
This is also true if you read from the [UNKNOWN NODE title_reference] stream before the parsing.
General rule: Leave the WSGI input stream alone. Especially in WSGI middlewares. Use either the parsing functions or the request object. Do not mix multiple WSGI utility libraries for form data parsing or anything else that works on the input stream.
How does it Parse?
The standard Werkzeug parsing behavior handles three cases:
- input content type was [UNKNOWN NODE title_reference]. In this situation the
stream
will be empty andform
will contain the regular [UNKNOWN NODE title_reference] / [UNKNOWN NODE title_reference] data,files
will contain the uploaded files asFileStorage
objects. - input content type was [UNKNOWN NODE title_reference]. Then the
stream
will be empty andform
will contain the regular [UNKNOWN NODE title_reference] / [UNKNOWN NODE title_reference] data andfiles
will be empty. - the input content type was neither of them,
stream
points to aLimitedStream
with the input data for further processing.
Special note on the get_data
method: Calling this
loads the full request data into memory. This is only safe to do if the
max_content_length
is set. Also you can either
read the stream or call get_data()
.
Limiting Request Data
To avoid being the victim of a DDOS attack you can set the maximum
accepted content length and request field sizes. The BaseRequest
class has two attributes for that: max_content_length
and max_form_memory_size
.
The first one can be used to limit the total content length. For example
by setting it to 1024 * 1024 * 16
the request won’t accept more than
16MB of transmitted data.
Because certain data can’t be moved to the hard disk (regular post data)
whereas temporary files can, there is a second limit you can set. The
max_form_memory_size
limits the size of [UNKNOWN NODE title_reference]
transmitted form data. By setting it to 1024 * 1024 * 2
you can make
sure that all in memory-stored fields are not more than 2MB in size.
This however does not affect in-memory stored files if the [UNKNOWN NODE title_reference] used returns a in-memory file.
How to extend Parsing?
Modern web applications transmit a lot more than multipart form data or
url encoded data. Extending the parsing capabilities by subclassing
the BaseRequest
is simple. The following example implements
parsing for incoming JSON data:
from werkzeug.utils import cached_property
from werkzeug.wrappers import Request
from simplejson import loads
class JSONRequest(Request):
# accept up to 4MB of transmitted data.
max_content_length = 1024 * 1024 * 4
@cached_property
def json(self):
if self.headers.get('content-type') == 'application/json':
return loads(self.data)