Middleware not preserving state properly
When using a middleware that performs an asynchronous operation, the endpoint /api/v1/my_resource_problem stalls and never responds. In contrast, the endpoints /api/v1/my_resource and /api/v1/my_resource_functional work as expected.
Steps to Reproduce
-
Run the following code:
from socketify import App, AppOptions, OpCode, CompressOptions, middleware, AppListenOptions import faulthandler import aiosqlite faulthandler.enable() class SimpleDatabase: def __init__(self, db_path: str = ":memory:"): self.db_path = db_path async def get_data(self): async with aiosqlite.connect(self.db_path) as db: async with db.execute("SELECT RANDOM()") as cursor: row = await cursor.fetchone() return row[0] if row else None simple_database = SimpleDatabase() async def problem_middleware(res, req, data=None): my_random_number = await simple_database.get_data() return {"hello":"world"} async def functional_middleware(res, req, data=None): return {"hello":"world"} async def post_data(res, req, data=None): data = await res.get_json() print(data) res.cork_end({"success": True}) def make_app(app): app.post("/api/v1/my_resource", post_data) app.post("/api/v1/my_resource_problem", middleware(problem_middleware, post_data)) app.post("/api/v1/my_resource_functional", middleware(functional_middleware, post_data)) from typing import Dict, Any, Callable, List, Optional, Union, Literal app = App() make_app(app) app.listen(AppListenOptions(port=5555, host="0.0.0.0"), lambda config: print(f"Listening on port http://{config.host}:%d now\n" % (config.port))) app.run() -
Send a POST request with valid JSON data to the following endpoints:
-
/api/v1/my_resource -
/api/v1/my_resource_functional -
/api/v1/my_resource_problem
-
Expected Behavior
All endpoints should respond with (and print the posted data in the stdout):
{
"success": True
}
Actual Behavior
-
/api/v1/my_resourceand/api/v1/my_resource_functionalrespond correctly. -
/api/v1/my_resource_problemstalls and never replies.
Additional Information
It appears that await res.get_json() enters an infinite loop when a previous middleware performs an asynchronous call, such as my_random_number = await simple_database.get_data().
Environment
- socketify Version: 0.0.31
Note: same behavior happens on data = await res.get_data().
Actually the problem seems to happen inside the .get_data() (it is called from the .get_json().
also, it seems that a call to res.get_data() or res.get_json() works, just before an asynchronous call.
When posting on the problematic endpoint, it will successfully print a valid json once, and then will hang, this modified middleware can show this behavior:
async def problem_middleware(res, req, data=None):
data = await res.get_json() # Will not hang
print(data) # Will print normally
my_random_number = await simple_database.get_data()
data = await res.get_json() # Will Hang forever
print(data)
return {"hello":"world"}
The workaround is the get all data beforehand and pass it around middleware calls, before any asynchronous call
this will be a tracking issue until I fix this properly. real fix is pause the readable side only of the socket when going async and not waiting the body, and resume again when reading or after the writable side ends. Also if we dont have any more body to receive we should not hang.