pyGrowler / Growler

A micro web-framework using asyncio coroutines and chained middleware.
http://www.growler.rocks
Apache License 2.0
686 stars 29 forks source link

Crash on request.get_body() #14

Closed yalosev closed 7 years ago

yalosev commented 7 years ago

Greetings. I`m trying to use some code from your examples:

import asyncio

from growler import App
from growler.middleware import (Logger, Static, StringRenderer)

loop = asyncio.get_event_loop()

app = App('GrowlerServer', loop=loop)
app.use(Logger())

@app.post('/data')
def post_test(req, res):
    print(req.get_body())

Server = app.create_server(host='127.0.0.1', port=4000)

loop.run_forever()

but when I call this handler: curl -H "Content-Type: application/json" -X POST -d '{"somekey": "somevalue"}' http://127.0.0.1:4000/data I`m getting this errors in console:

ERROR:growler.router:140393215106800:Event loop is running. ERROR:growler.middleware_chain:140393229054640:Event loop is running. And then app exits with code 0.

Python - 3.5.2 asyncio (3.4.3) growler (0.7.5)

Do you have some advice, to aboid this problem. Cause it seems, like your framework is very interesting and perfect for my purposes. But this problem ruins all idea ;)

Thank you.

akubera commented 7 years ago

Ohh, yeah looks like get_body() was a failed experiment - bridging a non-coroutine to a coroutine.

The Problem:

In short, to speed up processing the request, the HTTP headers are read and then the middleware is processed immediately. The res.body attribute is a Future, allowing the backend to cache the (potentially large) file while the webapp is running, until the body is requested. This was the purpose of get_body(), to get the value of the future without having your function be a coroutine. The problem is, there is no way to do that. I was trying to start a new loop, obviously it didn't like that.

The Solution:

Coroutines are supported as Growler middleware, and you're using python3.5 so it's super easy!

Make post_test asynchronous by doing something like:

@app.post('/data')
async def post_test(req, res):
    print(await req.body)
    res.send_text("OK!\n")

That should work for you (also add the res.send_text so the curl command doesn't print the 404 error).

akubera commented 7 years ago

For python 3.4 compatibility, this works:

@app.post('/data')
@asyncio.coroutine
def post_test(req, res):
    data = yield from req.body
    print(data)
    res.send_text("OK")

I'm thrilled you think Growler can help you with your project, but please note that it is still a baby project and is missing a lot of functionality. I'd really appreciate any other questions or bugs you find.

yalosev commented 7 years ago

Thank you for the answer. I see that it`s baby project. I need just a few things, but with a lot of middlewares. Tbh flask middlewares are awful, while yours are look like node or golang one. Thank you one more time.

whodes commented 6 years ago

I'm running into a similar problem, but instead of crashing I'm just waiting forever for the body to load. This happens when I post all different types of files to my POST endpoint and of various of sizes. It works quickly for smaller files but when I send pdfs and pngs that are only 320K it hangs.