Fixed a small issue with RFC 5987 headers arriving as unicode in parser

Also added API docs regarding header encoding, incl support for RFC 5987
for filename fields in Content-Disposition headers in multipart/form-data
parts, incl. an example of an upload request with a utf-8 encoded filename.
This commit is contained in:
Gina Häußge 2016-09-07 18:25:17 +02:00
parent 13728c231c
commit 2b22d26eac
4 changed files with 65 additions and 16 deletions

View file

@ -161,18 +161,10 @@ Upload file
Content-Disposition: form-data; name="file"; filename="whistle_v2.gcode"
Content-Type: application/octet-stream
;Generated with Cura_SteamEngine 13.11.2
M109 T0 S220.000000
T0
;Sliced at: Wed 11-12-2013 16:53:12
;Basic settings: Layer height: 0.2 Walls: 0.8 Fill: 20
;Print time: #P_TIME#
;Filament used: #F_AMNT#m #F_WGHT#g
;Filament cost: #F_COST#
;M190 S70 ;Uncomment to add your own bed temperature line
;M109 S220 ;Uncomment to add your own temperature line
G21 ;metric values
G90 ;absolute positioning
G21
G90
...
------WebKitFormBoundaryDeC2E3iWbTv1PwMC
Content-Disposition: form-data; name="select"
@ -188,7 +180,7 @@ Upload file
HTTP/1.1 200 OK
Content-Type: application/json
Location: http://example.com/api/files/sdcard/whistle_.gcode
Location: http://example.com/api/files/sdcard/whistle_v2.gcode
{
"files": {
@ -208,6 +200,46 @@ Upload file
}
}
},
"done": false
}
**Example with UTF-8 encoded filename following RFC 5987**
.. sourcecode:: http
POST /api/files/local HTTP/1.1
Host: example.com
X-Api-Key: abcdef...
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDeC2E3iWbTv1PwMC
------WebKitFormBoundaryDeC2E3iWbTv1PwMC
Content-Disposition: form-data; name="file"; filename*=utf-8''20mm-%C3%BCml%C3%A4ut-b%C3%B6x.gcode
Content-Type: application/octet-stream
M109 T0 S220.000000
T0
G21
G90
...
------WebKitFormBoundaryDeC2E3iWbTv1PwMC--
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
Location: http://example.com/api/files/local/20mm-umlaut-box.gcode
{
"files": {
"local": {
"name": "20mm-umlaut-box",
"origin": "local",
"refs": {
"resource": "http://example.com/api/files/local/whistle_v2.gcode",
"download": "http://example.com/downloads/files/local/whistle_v2.gcode"
}
}
},
"done": true
}

View file

@ -74,7 +74,19 @@ Encoding
OctoPrint uses UTF-8 as charset.
.. _sec-api-cross-origin:
That also includes headers in ``multipart/form-data`` requests, in order to allow the full UTF-8 range of characters
for uploaded filenames. If a ``multipart/form-data`` sub header cannot be decoded as UTF-8, OctoPrint will also attempt
to decode it as ISO-8859-1.
Additionally, OctoPrint supports replacing the ``filename`` field in the ``Content-Disposition`` header of a
multipart field with a ``filename*`` field following `RFC 5987, Section 3.2 <https://tools.ietf.org/html/rfc5987#section-3.2>`_,
which allows defining the charset used for encoding the filename. If both ``filename`` and ``filename*`` fields are
present, following the recommendation of the RFC ``filename*`` will be used.
For an example on how to send a request utilizing RFC 5987 for the ``filename*`` attribute, see the second example
in :ref:`Upload file <sec-api-fileops-uploadfile>`.
.. _sec-api-general-crossorigin:
Cross-origin requests
=====================

View file

@ -303,7 +303,7 @@ class UploadStorageFallbackHandler(tornado.web.RequestHandler):
filename = _extended_header_value(filename)
except:
# parse error, this is not RFC 5987 compliant after all
self._logger.warn("extended filename* value {!r} is not RFC 5987 compliant")
self._logger.warn("extended filename* value {!r} is not RFC 5987 compliant".format(filename))
self.send_error(400)
return
else:
@ -494,11 +494,11 @@ def _extended_header_value(value):
# RFC 5987 section 3.2
from urllib import unquote
encoding, _, value = value.split("'", 2)
return unquote(value).decode(encoding)
return unquote(octoprint.util.to_str(value, encoding="iso-8859-1")).decode(encoding)
else:
# no encoding provided, strip potentially present quotes and call it a day
return _strip_value_quotes(value)
return octoprint.util.to_unicode(_strip_value_quotes(value), encoding="utf-8")
class WsgiInputContainer(object):

View file

@ -76,9 +76,14 @@ class StripValueQuotesTest(unittest.TestCase):
class ExtendedHeaderValueTest(unittest.TestCase):
@data(
("", u""),
(u"", u""),
(None, None),
(u'"quoted-string"', u"quoted-string"),
(u'"qüöted-string"', u"qüöted-string"),
(u"iso-8859-1'en'%A3%20rates", u"£ rates"),
(u"UTF-8''%c2%a3%20and%20%e2%82%ac%20rates", u"£ and € rates"),
('"quoted-string"', u"quoted-string"),
('"qüöted-string"', u"qüöted-string"),
("iso-8859-1'en'%A3%20rates", u"£ rates"),
("UTF-8''%c2%a3%20and%20%e2%82%ac%20rates", u"£ and € rates")
)