florimondmanca / msgpack-asgi Goto Github PK
View Code? Open in Web Editor NEWDrop-in MessagePack support for ASGI applications and frameworks
Home Page: https://pypi.org/project/msgpack-asgi
License: MIT License
Drop-in MessagePack support for ASGI applications and frameworks
Home Page: https://pypi.org/project/msgpack-asgi
License: MIT License
This library seemed to hit the spot for drop-in support for msgpack with FastAPI. I am using the following to enable the msgpack interface:
app.add_middleware(MessagePackMiddleware)
Unfortunately, large client requests are failing. Running uvicorn
with --log-level trace
I see that the request is being chunked:
TRACE: 127.0.0.1:42088 - Connection made
TRACE: 10.60.1.118:0 - ASGI [4] Started scope={'type': 'http', 'asgi': {'version': '3.0', 'spec_version': '2.1'}, 'http_version': '1.0', 'server': ('127.0.0.1', 8901), 'client': ('10.60.1.118', 0), 'scheme': 'https', 'method': 'POST', 'root_path': '/app/<redacted>', 'path': '<redacted> 'raw_path': b'/<redacted>', 'query_string': b'', 'headers': '<...>'}
TRACE: 10.60.1.118:0 - ASGI [4] Receive {'type': 'http.request', 'body': '<65150 bytes>', 'more_body': True}
TRACE: 10.60.1.118:0 - ASGI [4] Receive {'type': 'http.request', 'body': '<65482 bytes>', 'more_body': True}
TRACE: 10.60.1.118:0 - ASGI [4] Send {'type': 'http.response.start', 'status': 400, 'headers': '<...>'}
INFO: 10.60.1.118:0 - "POST<redacted> HTTP/1.0" 400 Bad Request
TRACE: 10.60.1.118:0 - ASGI [4] Send {'type': 'http.response.body', 'body': '<45 bytes>'}
TRACE: 10.60.1.118:0 - ASGI [4] Completed
TRACE: 127.0.0.1:42088 - Connection lost
Requests under 64k work fine. JSON requests of any size are also fine.
The request is being sent from the client as a regular POST.
I assume this is related the comments about more_body
not being implemented in the source code.
I am still getting up to speed with ASGI. Is this something that should be fixed with this middleware, or should I look at figuring out how to increase the buffer size elsewhere?
ormsgpack is a faster alternative to python-msgpack and since speed is critical/required in Web/ASGI application I think it would be nice to have support for using ormsgpack!
My current idea is to provide ormsgpack as an extra/optional dependency to msgpack-asgi
(installed like pip install msgpack-asgi[ormsgpack]
?) and provide two more classes to the public API ORMessagePackMiddleware
and ORMessagePackResponse
if ormsgpack can be imported.
I will happily create a PR and start working on this feature if this is something can be added to the project! ๐
#20 was merged, but it needs a docs update before releasing. Opening this issue to track this.
We can mention a few possible alternatives, and provide a customization example for each, such as:
ormsgpack
- https://github.com/aviramha/ormsgpackmsgspec
- https://jcristharif.com/msgspecAs evidenced by msgpack/msgpack#194, there still is no clear answer on the "proper" MIME type for msgpack. Fluent-bit's HTTP output plugin uses application/msgpack
for the content-type
instead of aapplication/x-msgpack
, so the msgpack-asgi middleware doesn't attempt to unpack requests from fluent-bit POST's
I cannot get msgpack-asgi running with FastAPI. Any request with msgpack-bytes is returned with a "422 Unprocessable Entity" error. The following minimal example will show the problem:
from pathlib import Path
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from msgpack_asgi import MessagePackMiddleware
class Foo(BaseModel):
bar: int
app = FastAPI()
app.add_middleware(MessagePackMiddleware)
@app.post("/")
def index(thing: Foo):
return thing
if __name__ == "__main__":
uvicorn.run(f"{Path(__file__).stem}:app", host="0.0.0.0", port=5001, log_level="debug", reload=True)
The data is sent with the following snippet:
import requests
import msgpack
url = "http://127.0.0.1:5001/"
headers = {"content-type": "application/x-msgpack"}
data_raw = {"bar": 23}
data_packed = msgpack.packb(data_raw)
response_json = requests.post(url, json=data_raw)
response_msgpack = requests.post(url, data=data_packed, headers=headers)
Resulting in:
INFO: 127.0.0.1:54682 - "POST / HTTP/1.1" 200 OK
INFO: 127.0.0.1:54684 - "POST / HTTP/1.1" 422 Unprocessable Entity
So the data is accepted as json but refused as msgpack-bytes. May there be an incompatibility with newser versions of FastAPI or pydantic? Or am I just using this completely wrong?
class MessagePackMiddleware:
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
responder = _MessagePackResponder(self.app)
await responder(scope, receive, send)
return
await self.app(scope, receive, send)
I think the current vesion of the msgpack middleware has a serious implementaion flaw that will cause errors when parallel requests are processed.
During each request, some request scoped variables, like receive
, send
, should_decode_from_msgpack_to_json
, initial_message
etc, are stored on the middleware instance itself:
self.should_decode_from_msgpack_to_json = (
"application/x-msgpack" in headers.get("content-type", "")
)
# Take an initial guess, although we eventually may not
# be able to do the conversion.
self.should_encode_from_json_to_msgpack = (
"application/x-msgpack" in headers.getlist("accept")
)
self.receive = receive
self.send = send
The problem is that there is only one instance of the middleware, but multiple parallel requests are normally in progress, so these variables will get mixed up between the requests. When for example receive_with_msgpack
is called to process a request, self.receive
could already have been overwritten by a subsequent request.
The proper way to pass request scoped values between the various instance methods would be to use request or function scoped storage, like scope
, wrapped function or partial function.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.