Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

223 lines (190 sloc) 7.089 kb
#!/usr/bin/env python
from __future__ import absolute_import, division, with_statement
from tornado.httputil import url_concat, parse_multipart_form_data, HTTPHeaders
from tornado.escape import utf8
from tornado.testing import LogTrapTestCase
from tornado.util import b
import logging
import unittest
class TestUrlConcat(unittest.TestCase):
def test_url_concat_no_query_params(self):
url = url_concat(
"https://localhost/path",
[('y', 'y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?y=y&z=z")
def test_url_concat_encode_args(self):
url = url_concat(
"https://localhost/path",
[('y', '/y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?y=%2Fy&z=z")
def test_url_concat_trailing_q(self):
url = url_concat(
"https://localhost/path?",
[('y', 'y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?y=y&z=z")
def test_url_concat_q_with_no_trailing_amp(self):
url = url_concat(
"https://localhost/path?x",
[('y', 'y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?x&y=y&z=z")
def test_url_concat_trailing_amp(self):
url = url_concat(
"https://localhost/path?x&",
[('y', 'y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?x&y=y&z=z")
def test_url_concat_mult_params(self):
url = url_concat(
"https://localhost/path?a=1&b=2",
[('y', 'y'), ('z', 'z')],
)
self.assertEqual(url, "https://localhost/path?a=1&b=2&y=y&z=z")
def test_url_concat_no_params(self):
url = url_concat(
"https://localhost/path?r=1&t=2",
[],
)
self.assertEqual(url, "https://localhost/path?r=1&t=2")
class MultipartFormDataTest(LogTrapTestCase):
def test_file_upload(self):
data = b("""\
--1234
Content-Disposition: form-data; name="files"; filename="ab.txt"
Foo
--1234--""").replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
file = files["files"][0]
self.assertEqual(file["filename"], "ab.txt")
self.assertEqual(file["body"], b("Foo"))
def test_unquoted_names(self):
# quotes are optional unless special characters are present
data = b("""\
--1234
Content-Disposition: form-data; name=files; filename=ab.txt
Foo
--1234--""").replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
file = files["files"][0]
self.assertEqual(file["filename"], "ab.txt")
self.assertEqual(file["body"], b("Foo"))
def test_special_filenames(self):
filenames = ['a;b.txt',
'a"b.txt',
'a";b.txt',
'a;"b.txt',
'a";";.txt',
'a\\"b.txt',
'a\\b.txt',
]
for filename in filenames:
logging.info("trying filename %r", filename)
data = """\
--1234
Content-Disposition: form-data; name="files"; filename="%s"
Foo
--1234--""" % filename.replace('\\', '\\\\').replace('"', '\\"')
data = utf8(data.replace("\n", "\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
file = files["files"][0]
self.assertEqual(file["filename"], filename)
self.assertEqual(file["body"], b("Foo"))
def test_boundary_starts_and_ends_with_quotes(self):
data = b('''\
--1234
Content-Disposition: form-data; name="files"; filename="ab.txt"
Foo
--1234--''').replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b('"1234"'), data, args, files)
file = files["files"][0]
self.assertEqual(file["filename"], "ab.txt")
self.assertEqual(file["body"], b("Foo"))
def test_missing_headers(self):
data = b('''\
--1234
Foo
--1234--''').replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
self.assertEqual(files, {})
def test_invalid_content_disposition(self):
data = b('''\
--1234
Content-Disposition: invalid; name="files"; filename="ab.txt"
Foo
--1234--''').replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
self.assertEqual(files, {})
def test_line_does_not_end_with_correct_line_break(self):
data = b('''\
--1234
Content-Disposition: form-data; name="files"; filename="ab.txt"
Foo--1234--''').replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
self.assertEqual(files, {})
def test_content_disposition_header_without_name_parameter(self):
data = b("""\
--1234
Content-Disposition: form-data; filename="ab.txt"
Foo
--1234--""").replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
self.assertEqual(files, {})
def test_data_after_final_boundary(self):
# The spec requires that data after the final boundary be ignored.
# http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
# In practice, some libraries include an extra CRLF after the boundary.
data = b("""\
--1234
Content-Disposition: form-data; name="files"; filename="ab.txt"
Foo
--1234--
""").replace(b("\n"), b("\r\n"))
args = {}
files = {}
parse_multipart_form_data(b("1234"), data, args, files)
file = files["files"][0]
self.assertEqual(file["filename"], "ab.txt")
self.assertEqual(file["body"], b("Foo"))
class HTTPHeadersTest(unittest.TestCase):
def test_multi_line(self):
# Lines beginning with whitespace are appended to the previous line
# with any leading whitespace replaced by a single space.
# Note that while multi-line headers are a part of the HTTP spec,
# their use is strongly discouraged.
data = """\
Foo: bar
baz
Asdf: qwer
\tzxcv
Foo: even
more
lines
""".replace("\n", "\r\n")
headers = HTTPHeaders.parse(data)
self.assertEqual(headers["asdf"], "qwer zxcv")
self.assertEqual(headers.get_list("asdf"), ["qwer zxcv"])
self.assertEqual(headers["Foo"], "bar baz,even more lines")
self.assertEqual(headers.get_list("foo"), ["bar baz", "even more lines"])
self.assertEqual(sorted(list(headers.get_all())),
[("Asdf", "qwer zxcv"),
("Foo", "bar baz"),
("Foo", "even more lines")])
Jump to Line
Something went wrong with that request. Please try again.