Recognizes non-ASCII filenames in RFC 2231, and suport filename length is zero for multipart/form-data. (#1497)
* suport filename length is 0 * 1. suport filename length is zero for multipart/form-data. 2. Now recognizes non-ASCII filenames in RFC 2231, "filename*" format 3. Add some test cases in tests/test_requests.py::test_request_multipart_files. * reformat sanic/request.py
This commit is contained in:
parent
52deebaf65
commit
8a59907319
|
@ -1,11 +1,12 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import email.utils
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from cgi import parse_header
|
from cgi import parse_header
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from http.cookies import SimpleCookie
|
from http.cookies import SimpleCookie
|
||||||
from urllib.parse import parse_qs, urlunparse
|
from urllib.parse import parse_qs, unquote, urlunparse
|
||||||
|
|
||||||
from httptools import parse_url
|
from httptools import parse_url
|
||||||
|
|
||||||
|
@ -356,15 +357,28 @@ def parse_multipart_form(body, boundary):
|
||||||
)
|
)
|
||||||
|
|
||||||
if form_header_field == "content-disposition":
|
if form_header_field == "content-disposition":
|
||||||
file_name = form_parameters.get("filename")
|
|
||||||
field_name = form_parameters.get("name")
|
field_name = form_parameters.get("name")
|
||||||
|
file_name = form_parameters.get("filename")
|
||||||
|
|
||||||
|
# non-ASCII filenames in RFC2231, "filename*" format
|
||||||
|
if file_name is None and form_parameters.get("filename*"):
|
||||||
|
encoding, _, value = email.utils.decode_rfc2231(
|
||||||
|
form_parameters["filename*"]
|
||||||
|
)
|
||||||
|
file_name = unquote(value, encoding=encoding)
|
||||||
elif form_header_field == "content-type":
|
elif form_header_field == "content-type":
|
||||||
content_type = form_header_value
|
content_type = form_header_value
|
||||||
content_charset = form_parameters.get("charset", "utf-8")
|
content_charset = form_parameters.get("charset", "utf-8")
|
||||||
|
|
||||||
if field_name:
|
if field_name:
|
||||||
post_data = form_part[line_index:-4]
|
post_data = form_part[line_index:-4]
|
||||||
if file_name:
|
if file_name is None:
|
||||||
|
value = post_data.decode(content_charset)
|
||||||
|
if field_name in fields:
|
||||||
|
fields[field_name].append(value)
|
||||||
|
else:
|
||||||
|
fields[field_name] = [value]
|
||||||
|
else:
|
||||||
form_file = File(
|
form_file = File(
|
||||||
type=content_type, name=file_name, body=post_data
|
type=content_type, name=file_name, body=post_data
|
||||||
)
|
)
|
||||||
|
@ -372,12 +386,6 @@ def parse_multipart_form(body, boundary):
|
||||||
files[field_name].append(form_file)
|
files[field_name].append(form_file)
|
||||||
else:
|
else:
|
||||||
files[field_name] = [form_file]
|
files[field_name] = [form_file]
|
||||||
else:
|
|
||||||
value = post_data.decode(content_charset)
|
|
||||||
if field_name in fields:
|
|
||||||
fields[field_name].append(value)
|
|
||||||
else:
|
|
||||||
fields[field_name] = [value]
|
|
||||||
else:
|
else:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Form-data field does not have a 'name' parameter "
|
"Form-data field does not have a 'name' parameter "
|
||||||
|
|
|
@ -432,21 +432,41 @@ def test_request_string_representation(app):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"payload",
|
"payload,filename",
|
||||||
[
|
[
|
||||||
"------sanic\r\n"
|
("------sanic\r\n"
|
||||||
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
'Content-Disposition: form-data; filename="filename"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"OK\r\n"
|
"OK\r\n"
|
||||||
"------sanic--\r\n",
|
"------sanic--\r\n", "filename"),
|
||||||
"------sanic\r\n"
|
("------sanic\r\n"
|
||||||
'content-disposition: form-data; filename="filename"; name="test"\r\n'
|
'content-disposition: form-data; filename="filename"; name="test"\r\n'
|
||||||
"\r\n"
|
"\r\n"
|
||||||
'content-type: application/json; {"field": "value"}\r\n'
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
"------sanic--\r\n",
|
"------sanic--\r\n", "filename"),
|
||||||
|
("------sanic\r\n"
|
||||||
|
'Content-Disposition: form-data; filename=""; name="test"\r\n'
|
||||||
|
"\r\n"
|
||||||
|
"OK\r\n"
|
||||||
|
"------sanic--\r\n", ""),
|
||||||
|
("------sanic\r\n"
|
||||||
|
'content-disposition: form-data; filename=""; name="test"\r\n'
|
||||||
|
"\r\n"
|
||||||
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
|
"------sanic--\r\n", ""),
|
||||||
|
("------sanic\r\n"
|
||||||
|
'Content-Disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
||||||
|
"\r\n"
|
||||||
|
"OK\r\n"
|
||||||
|
"------sanic--\r\n", "filename_\u00A0_test"),
|
||||||
|
("------sanic\r\n"
|
||||||
|
'content-disposition: form-data; filename*="utf-8\'\'filename_%C2%A0_test"; name="test"\r\n'
|
||||||
|
"\r\n"
|
||||||
|
'content-type: application/json; {"field": "value"}\r\n'
|
||||||
|
"------sanic--\r\n", "filename_\u00A0_test"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_request_multipart_files(app, payload):
|
def test_request_multipart_files(app, payload, filename):
|
||||||
@app.route("/", methods=["POST"])
|
@app.route("/", methods=["POST"])
|
||||||
async def post(request):
|
async def post(request):
|
||||||
return text("OK")
|
return text("OK")
|
||||||
|
@ -454,7 +474,7 @@ def test_request_multipart_files(app, payload):
|
||||||
headers = {"content-type": "multipart/form-data; boundary=----sanic"}
|
headers = {"content-type": "multipart/form-data; boundary=----sanic"}
|
||||||
|
|
||||||
request, _ = app.test_client.post(data=payload, headers=headers)
|
request, _ = app.test_client.post(data=payload, headers=headers)
|
||||||
assert request.files.get("test").name == "filename"
|
assert request.files.get("test").name == filename
|
||||||
|
|
||||||
|
|
||||||
def test_request_multipart_file_with_json_content_type(app):
|
def test_request_multipart_file_with_json_content_type(app):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user