From: Travis Beauvais Date: Sat, 28 Sep 2013 19:53:16 +0000 (-0700) Subject: Support argument origin X-Git-Tag: v3.2.0b1~79^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d45711ab631302f40f6ac157ae01a39f3cbbed49;p=thirdparty%2Ftornado.git Support argument origin --- diff --git a/tornado/httpserver.py b/tornado/httpserver.py old mode 100644 new mode 100755 index cacb514e4..eba184a5f --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -29,6 +29,7 @@ from __future__ import absolute_import, division, print_function, with_statement import socket import ssl import time +import copy from tornado.escape import native_str, parse_qs_bytes from tornado import httputil @@ -336,7 +337,7 @@ class HTTPConnection(object): if self._request.method in ("POST", "PATCH", "PUT"): httputil.parse_body_arguments( self._request.headers.get("Content-Type", ""), data, - self._request.arguments, self._request.files) + self._request.arguments, self._request.body_arguments, self._request.files) self.request_callback(self._request) @@ -457,6 +458,8 @@ class HTTPRequest(object): self.path, sep, self.query = uri.partition('?') self.arguments = parse_qs_bytes(self.query, keep_blank_values=True) + self.query_arguments = copy.deepcopy(self.arguments) + self.body_arguments = {} def supports_http_1_1(self): """Returns True if this request supports HTTP/1.1 semantics""" diff --git a/tornado/httputil.py b/tornado/httputil.py old mode 100644 new mode 100755 index 3e7337d99..afd1bb243 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -310,7 +310,7 @@ def _int_or_none(val): return int(val) -def parse_body_arguments(content_type, body, arguments, files): +def parse_body_arguments(content_type, body, arguments, body_arguments, files): """Parses a form request body. Supports ``application/x-www-form-urlencoded`` and @@ -324,18 +324,19 @@ def parse_body_arguments(content_type, body, arguments, files): for name, values in uri_arguments.items(): if values: arguments.setdefault(name, []).extend(values) + body_arguments.setdefault(name, []).extend(values) elif content_type.startswith("multipart/form-data"): fields = content_type.split(";") for field in fields: k, sep, v = field.strip().partition("=") if k == "boundary" and v: - parse_multipart_form_data(utf8(v), body, arguments, files) + parse_multipart_form_data(utf8(v), body, arguments, body_arguments, files) break else: gen_log.warning("Invalid multipart/form-data") -def parse_multipart_form_data(boundary, data, arguments, files): +def parse_multipart_form_data(boundary, data, arguments, body_arguments, files): """Parses a ``multipart/form-data`` body. The ``boundary`` and ``data`` parameters are both byte strings. @@ -379,6 +380,7 @@ def parse_multipart_form_data(boundary, data, arguments, files): content_type=ctype)) else: arguments.setdefault(name, []).append(value) + body_arguments.setdefault(name, []).append(value) def format_timestamp(ts): diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py old mode 100644 new mode 100755 index 1e84da76f..7a7f75542 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -74,8 +74,9 @@ Content-Disposition: form-data; name="files"; filename="ab.txt" Foo --1234--""".replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b"Foo") @@ -89,8 +90,9 @@ Content-Disposition: form-data; name=files; filename=ab.txt Foo --1234--""".replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b"Foo") @@ -114,8 +116,9 @@ Foo --1234--""" % filename.replace('\\', '\\\\').replace('"', '\\"') data = utf8(data.replace("\n", "\r\n")) args = {} + body_args = {} files = {} - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) file = files["files"][0] self.assertEqual(file["filename"], filename) self.assertEqual(file["body"], b"Foo") @@ -128,8 +131,9 @@ Content-Disposition: form-data; name="files"; filename="ab.txt" Foo --1234--'''.replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} - parse_multipart_form_data(b'"1234"', data, args, files) + parse_multipart_form_data(b'"1234"', data, args, body_args, files) file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b"Foo") @@ -141,9 +145,10 @@ Foo Foo --1234--'''.replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} with ExpectLog(gen_log, "multipart/form-data missing headers"): - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) self.assertEqual(files, {}) def test_invalid_content_disposition(self): @@ -154,9 +159,10 @@ Content-Disposition: invalid; name="files"; filename="ab.txt" Foo --1234--'''.replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} with ExpectLog(gen_log, "Invalid multipart/form-data"): - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) self.assertEqual(files, {}) def test_line_does_not_end_with_correct_line_break(self): @@ -166,9 +172,10 @@ Content-Disposition: form-data; name="files"; filename="ab.txt" Foo--1234--'''.replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} with ExpectLog(gen_log, "Invalid multipart/form-data"): - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) self.assertEqual(files, {}) def test_content_disposition_header_without_name_parameter(self): @@ -179,9 +186,10 @@ Content-Disposition: form-data; filename="ab.txt" Foo --1234--""".replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} with ExpectLog(gen_log, "multipart/form-data value missing name"): - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) self.assertEqual(files, {}) def test_data_after_final_boundary(self): @@ -196,8 +204,9 @@ Foo --1234-- """.replace(b"\n", b"\r\n") args = {} + body_args = {} files = {} - parse_multipart_form_data(b"1234", data, args, files) + parse_multipart_form_data(b"1234", data, args, body_args, files) file = files["files"][0] self.assertEqual(file["filename"], "ab.txt") self.assertEqual(file["body"], b"Foo") diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 08093c79b..f7e06d709 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -288,7 +288,7 @@ class EchoHandler(RequestHandler): if type(value) != bytes_type: raise Exception("incorrect type for value: %r" % type(value)) - for value in self.get_arguments(key): + for value in self.get_arguments(key, self.request.arguments): if type(value) != unicode_type: raise Exception("incorrect type for value: %r" % type(value)) diff --git a/tornado/web.py b/tornado/web.py old mode 100644 new mode 100755 index 140fc1c44..52e9137dd --- a/tornado/web.py +++ b/tornado/web.py @@ -348,14 +348,45 @@ class RequestHandler(object): The returned value is always unicode. """ - args = self.get_arguments(name, strip=strip) + return self._get_argument(name, self.request.arguments, default, strip) + + def get_body_argument(self, name, default=_ARG_DEFAULT, strip=True): + """Returns the value of the argument with the given name + from the request body. + + If default is not provided, the argument is considered to be + required, and we raise a `MissingArgumentError` if it is missing. + + If the argument appears in the url more than once, we return the + last value. + + The returned value is always unicode. + """ + return self._get_argument(name, self.request.body_arguments, default, strip) + + def get_query_argument(self, name, default=_ARG_DEFAULT, strip=True): + """Returns the value of the argument with the given name + from the request query string. + + If default is not provided, the argument is considered to be + required, and we raise a `MissingArgumentError` if it is missing. + + If the argument appears in the url more than once, we return the + last value. + + The returned value is always unicode. + """ + return self._get_argument(name, self.request.query_arguments, default, strip) + + def _get_argument(self, name, source, default=_ARG_DEFAULT, strip=True): + args = self.get_arguments(name, source, strip=strip) if not args: if default is self._ARG_DEFAULT: raise MissingArgumentError(name) return default return args[-1] - def get_arguments(self, name, strip=True): + def get_arguments(self, name, source, strip=True): """Returns a list of the arguments with the given name. If the argument is not present, returns an empty list. @@ -364,7 +395,7 @@ class RequestHandler(object): """ values = [] - for v in self.request.arguments.get(name, []): + for v in source.get(name, []): v = self.decode_argument(v, name=name) if isinstance(v, unicode_type): # Get rid of any weird control chars (unless decoding gave diff --git a/tornado/wsgi.py b/tornado/wsgi.py old mode 100644 new mode 100755 index 5e25a5647..f85e86cf9 --- a/tornado/wsgi.py +++ b/tornado/wsgi.py @@ -33,6 +33,7 @@ from __future__ import absolute_import, division, print_function, with_statement import sys import time +import copy import tornado from tornado import escape @@ -142,11 +143,14 @@ class HTTPRequest(object): self.path += urllib_parse.quote(from_wsgi_str(environ.get("PATH_INFO", ""))) self.uri = self.path self.arguments = {} + self.query_arguments = {} + self.body_arguments = {} self.query = environ.get("QUERY_STRING", "") if self.query: self.uri += "?" + self.query self.arguments = parse_qs_bytes(native_str(self.query), keep_blank_values=True) + self.query_arguments = copy.deepcopy(self.arguments) self.version = "HTTP/1.1" self.headers = httputil.HTTPHeaders() if environ.get("CONTENT_TYPE"): @@ -171,7 +175,7 @@ class HTTPRequest(object): # Parse request body self.files = {} httputil.parse_body_arguments(self.headers.get("Content-Type", ""), - self.body, self.arguments, self.files) + self.body, self.arguments, self.body_arguments, self.files) self._start_time = time.time() self._finish_time = None