From 513166456a50550e3693b83439eb33b2495c9116 Mon Sep 17 00:00:00 2001 From: Correl Roush Date: Wed, 13 Jan 2021 14:33:39 -0500 Subject: [PATCH] Parse mimetype from content-type OpenAPI objects expect lowercased mimetypes, with parameters stripped. --- pyproject.toml | 1 + tests/test_util.py | 16 ++++++++++++++++ tornado_openapi3/requests.py | 6 ++++-- tornado_openapi3/responses.py | 3 ++- tornado_openapi3/util.py | 10 ++++++++++ 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/test_util.py create mode 100644 tornado_openapi3/util.py diff --git a/pyproject.toml b/pyproject.toml index 0de10a2..d971a0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ packages = [ python = "^3.7" tornado = "^4 || ^5 || ^6" openapi-core = "^0.13.4" +ietfparse = "^1.7.0" [tool.poetry.dev-dependencies] black = { version = "*", allow-prereleases = true } diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 0000000..d77e08a --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,16 @@ +import unittest + +from tornado_openapi3 import util + + +class TestMimetypeParsing(unittest.TestCase): + def test_parameters_are_stripped(self) -> None: + self.assertEqual("text/html", util.parse_mimetype("text/html; charset=utf-8")) + + def test_mimetype_is_lowercase(self) -> None: + self.assertEqual("text/html", util.parse_mimetype("TEXT/HTML")) + + def test_type_suffix_is_preserved(self) -> None: + self.assertEqual( + "application/custom+json", util.parse_mimetype("application/custom+json") + ) diff --git a/tornado_openapi3/requests.py b/tornado_openapi3/requests.py index 259f40c..0af3581 100644 --- a/tornado_openapi3/requests.py +++ b/tornado_openapi3/requests.py @@ -12,6 +12,8 @@ from tornado.httpclient import HTTPRequest # type: ignore from tornado.httputil import HTTPServerRequest # type: ignore from werkzeug.datastructures import ImmutableMultiDict, Headers +from .util import parse_mimetype + class TornadoRequestFactory: @classmethod @@ -44,8 +46,8 @@ class TornadoRequestFactory: query=query_arguments, header=Headers(request.headers.get_all()) ), body=request.body if request.body else b"", - mimetype=request.headers.get( - "Content-Type", "application/x-www-form-urlencoded" + mimetype=parse_mimetype( + request.headers.get("Content-Type", "application/x-www-form-urlencoded") ), ) diff --git a/tornado_openapi3/responses.py b/tornado_openapi3/responses.py index fc87f4a..89365fe 100644 --- a/tornado_openapi3/responses.py +++ b/tornado_openapi3/responses.py @@ -6,12 +6,13 @@ from openapi_core.validation.response import validators # type: ignore from tornado.httpclient import HTTPResponse # type: ignore from .requests import TornadoRequestFactory +from .util import parse_mimetype class TornadoResponseFactory: @classmethod def create(cls, response: HTTPResponse) -> OpenAPIResponse: - mimetype = response.headers.get("Content-Type", "text/html") + mimetype = parse_mimetype(response.headers.get("Content-Type", "text/html")) return OpenAPIResponse( data=response.body if response.body else b"", status_code=response.code, diff --git a/tornado_openapi3/util.py b/tornado_openapi3/util.py new file mode 100644 index 0000000..8d39246 --- /dev/null +++ b/tornado_openapi3/util.py @@ -0,0 +1,10 @@ +import ietfparse.headers + + +def parse_mimetype(content_type: str) -> str: + parsed = ietfparse.headers.parse_content_type(content_type) + return "{}/{}{}".format( + parsed.content_type, + parsed.content_subtype, + "+{}".format(parsed.content_suffix) if parsed.content_suffix else "", + )