From 1191ee0ada19d258e61815bc8526098153ef6802 Mon Sep 17 00:00:00 2001 From: nikto_b Date: Mon, 4 Aug 2025 22:58:21 +0300 Subject: [PATCH] Split serialization and de-serialization based on `Accept` header appearance --- src/turbosloth/app.py | 25 +++++++++++++++++++++++-- src/turbosloth/types.py | 3 +++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/turbosloth/app.py b/src/turbosloth/app.py index 2175ddf..990f84f 100644 --- a/src/turbosloth/app.py +++ b/src/turbosloth/app.py @@ -15,7 +15,7 @@ from .interfaces.serialized import SerializedResponse, SerializedRequest from .interfaces.serialized.text import TextSerializedResponse from .req_schema import UnwrappedRequest from .router import Router, Route -from .types import HandlerType, InternalHandlerType, ContentType +from .types import HandlerType, InternalHandlerType, ContentType, Accept from .internal_types import Scope, Receive, Send, MethodType, QTYPE, BTYPE, PTYPE, HTYPE from breakshaft.convertor import ConvRepo @@ -47,6 +47,26 @@ class HTTPApp(ASGIApp): charset = properties.get('charset') if charset is None: + # TODO: extract default charsets based on content type + if contenttype == 'application/json': + charset = 'utf-8' + else: + charset = 'latin1' + + return ContentType(contenttype, charset) + + def extract_accept_type(self, req: BasicRequest, ct: ContentType) -> Accept: + contenttype_header = req.headers.get('Accept') + if contenttype_header is None: + return ct + + properties: dict[str, str] + contenttype, properties = parse_content_type(contenttype_header) + + charset = properties.get('charset') + + if charset is None: + # TODO: extract default charsets based on content type if contenttype == 'application/json': charset = 'utf-8' else: @@ -58,7 +78,7 @@ class HTTPApp(ASGIApp): ser = self.serialize_selector.select(ct.contenttype, ct.charset) return ser.req.deserialize(req, ct.charset) - def serialize_response(self, req: BasicRequest, sresp: SerializedResponse, ct: ContentType) -> BasicResponse: + def serialize_response(self, req: BasicRequest, sresp: SerializedResponse, ct: Accept) -> BasicResponse: ser = self.serialize_selector.select(ct.contenttype, ct.charset) sresponser = ser.resp @@ -217,6 +237,7 @@ class SlothApp(HTTPApp, WSApp, LifespanApp, MethodRoutersApp): return req.body self.inj_repo.add_injector(self.extract_content_type) + self.inj_repo.add_injector(self.extract_accept_type) self.inj_repo.add_injector(self.serialize_request) self.inj_repo.add_injector(self.serialize_response) diff --git a/src/turbosloth/types.py b/src/turbosloth/types.py index 7613ce4..743b7f3 100644 --- a/src/turbosloth/types.py +++ b/src/turbosloth/types.py @@ -16,3 +16,6 @@ type InternalHandlerType = Callable[[Send, BasicRequest], Awaitable[None]] class ContentType: contenttype: str charset: str + + +type Accept = ContentType