Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Response' object has no attribute 'status_code' in wsgi.py with websockets #1210

Open
bclark8923 opened this issue Feb 16, 2016 · 39 comments · Fixed by #1406 · May be fixed by #3126
Open

'Response' object has no attribute 'status_code' in wsgi.py with websockets #1210

bclark8923 opened this issue Feb 16, 2016 · 39 comments · Fixed by #1406 · May be fixed by #3126
Assignees
Labels
Discussion Feedback Requested help wanted Open for everyone. You do not need permission to work on these. May need familiarity with codebase. Investigation

Comments

@bclark8923
Copy link

I start my app using

gunicorn --worker-class eventlet -w 1 server:app --bind="127.0.0.1:5000"

And when using Flack-SocketIO for websockets I often get an error with Gunicorn not returning from a function properly

[2016-01-30 10:20:53 -0800] [7330] [ERROR] Error handling request
Traceback (most recent call last):
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/workers/async.py", line 52, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/workers/async.py", line 114, in handle_request
    resp.close()
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 423, in close
    self.send_headers()
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 316, in send_headers
    tosend = self.default_headers()
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 297, in default_headers
    elif self.should_close():
  File "/Users/brianclark/Desktop/Projects/HDP/application/api/venv/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 230, in should_close
    if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'

Any advice on configurations I could use to fix this or is it a bug?

@benoitc
Copy link
Owner

benoitc commented Mar 28, 2016

@bclark8923 do you have any application that could help to reproduce the issue?

@bclark8923
Copy link
Author

Yup! What would you like me to do?

On Monday, March 28, 2016, Benoit Chesneau notifications@github.com wrote:

@bclark8923 https://github.com/bclark8923 do you have any application
that could help to reproduce the issue?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#1210 (comment)

Thanks,
Brian Clark
(248) 990 5616
www.hdphealth.com

Get to know me on Facebook https://facebook.com/bclark8923 & Twitter
https://twitter.com/blaurenceclark!

@benoitc
Copy link
Owner

benoitc commented Mar 28, 2016

@bclark8923 if it can be streamed so eventually i could include some part in a test that would help :)

Also which version of gunicorn are you using?

@bclark8923
Copy link
Author

Yes please! Email me bclark8923@gmail.com and 19.x of some sort can check
soon

On Monday, March 28, 2016, Benoit Chesneau notifications@github.com wrote:

@bclark8923 https://github.com/bclark8923 if it can be streamed so
eventually i could include some part in a test that would help :)

Also which version of gunicorn are you using?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#1210 (comment)

Thanks,
Brian Clark
(248) 990 5616
www.hdphealth.com

Get to know me on Facebook https://facebook.com/bclark8923 & Twitter
https://twitter.com/blaurenceclark!

@bclark8923
Copy link
Author

Using version 19.4.5

@kramer65
Copy link

kramer65 commented Apr 15, 2016

I'm having the same issue. I'm using gunicorn version 18.0.

Any fix available already?

@benoitc
Copy link
Owner

benoitc commented Apr 15, 2016

if anyone have z code to reproduce it thatt would help :)

@kramer65
Copy link

@benoitc - That makes sense.. ;-)

I'll fix something up next week and let you know!

@kramer65
Copy link

kramer65 commented Apr 15, 2016

@benoitc - Alright, I fixed something up before the weekend after all.

You can find the repo with instructions in the readme here: https://github.com/kramer65/gunicorn-error

btw: I'm using gunicorn version 18.0

@tilgovi
Copy link
Collaborator

tilgovi commented Apr 18, 2016

Probably something needs to return gunicorn.workers.async.ALREADY_HANDLED or gunicorn will try to log the request as soon as the application returns from its WSGI handler. Gunicorn needs to know, instead, that the request is now handled entirely by the application.

@tilgovi
Copy link
Collaborator

tilgovi commented Apr 18, 2016

Both of the examples in the "examples/websocket" directory return this constant from their WSGI handler.

@kramer65
Copy link

@benoitc - Does the example to reproduce the error help you with finding the error? Can I be of any other assistance to help this be resolved?

@benoitc
Copy link
Owner

benoitc commented May 2, 2016

@kramer65 yes I reproduced the error.

So Gunicorn is a pure WSGI engine and is expecting a WSGI response containing a status:
https://github.com/benoitc/gunicorn/blob/master/gunicorn/http/wsgi.py#L242

(related to this code: https://github.com/benoitc/gunicorn/blob/master/gunicorn/workers/async.py#L103-L119)

As @tilgovi said you can bypass the response handling from gunicorn by returning ALREADY_HANDLED

As far as I read the code of flash socketio it is wrapping the flask application in socketio.Middleware which itself inheit from engineio.middleware.Middleware:
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/middleware.py

which return the wsgi application or their own own handler if it find the path:
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/middleware.py#L45-L52

So it return socketio.Server.handle_request on the websocket path:
https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/flask_socketio/__init__.py#L144

which itself return engineio.Server.handle_request (lot of circular deps in this project...):
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/server.py#L184

At this point it appears that neither socket.handle_get_request or socket. handle_post_request set the status:
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/socket.py#L69-L96

And then an error may happen here:
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/server.py#L251-L252

since it can return a response without having set a status or anything. I can see that the upgrade use the same websocket object as in our example:
https://github.com/benoitc/gunicorn/blob/master/examples/websocket/websocket.py

I think that instead of returning the final r here:
https://github.com/miguelgrinberg/python-engineio/blob/master/engineio/server.py#L251-L252

It should probably return ALREADY_HANDLED like there:
https://github.com/benoitc/gunicorn/blob/master/examples/websocket/websocket.py#L115

Just a guess anyway since that code is quite difficult to read. Hopefully it will helps.

@benoitc
Copy link
Owner

benoitc commented May 2, 2016

@kramer65 let me know about the status of this ticket for you anyway :)

@miguelgrinberg
Copy link

miguelgrinberg commented May 7, 2016

@benoitc the ALREADY_HANDLED constant is specific to gunicorn. Eventlet has its own version of this constant, defined in a different way: https://github.com/eventlet/eventlet/blob/2cd5f1d9aea53efb4526e7185017bdcc84732588/eventlet/wsgi.py#L69.

The r value that you were following through the code returns evenlet's ALREADY_HANDLED when the websocket connection ends. This is handled automatically by eventlet: https://github.com/eventlet/eventlet/blob/2cd5f1d9aea53efb4526e7185017bdcc84732588/eventlet/websocket.py#L135.

Gunicorn should (I think) recognize eventlet's ALREADY_HANDLED constant in addition to its own and handle it in the same way. I think that will address this problem.

@lequangdzung
Copy link

It's great if you can fix this issue. I'm running flask-socketio & gunicorn on my production and don't want to lost data.

@harm-devries
Copy link

I am experiencing similar problems, and was wondering if there is any news on this issue?

@kramer65
Copy link

kramer65 commented Jun 30, 2016

@benoitc - Do you think the suggestion as made by @miguelgrinberg above (to let Gunicorn recognize eventlet's ALREADY_HANDLED constant in addition to its own and handle it in the same way) is a good idea?

@tilgovi
Copy link
Collaborator

tilgovi commented Jun 30, 2016

@kramer65 if you can find a way to cleanly add support for that, I think it makes sense for the eventlet worker to handle it.

Right now the logic to handle this is a bit buried inside gunicorn.workers.async.AsyncWorker#handle_request but maybe that could be handled with indirection via static property that the eventlet worker can override, or by calling an instance method to check already handled by any worker-specific logic that might exist, with the base class doing as it does now.

@tilgovi tilgovi added the help wanted Open for everyone. You do not need permission to work on these. May need familiarity with codebase. label Jun 30, 2016
@kramer65
Copy link

@tilgovi - It has been a while since you send me your message, but I wanted to respond to this after all. The problem is that I kinda hit my limit in what I can do for this. I really have no clue how to proceed to solve this.

In the meantime my logs are filled with error messages like the one below, so I do have every reason to put some energy in having this solved. I just don't fully understand what the exact cause of the problem is, let alone that I understand how I could solve this.

If anybody wants to get into contact I would be happy to discuss the issue, and then I might be able to be of a bit more help than I am now.

2016-08-23 08:07:16 [2185] [ERROR] Error handling request
Traceback (most recent call last):
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/workers/async.py", line 45, in handle
    self.handle_request(listener, req, client, addr)
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/workers/async.py", line 102, in handle_request
    resp.close()
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 369, in close
    self.send_headers()
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 284, in send_headers
    tosend = self.default_headers()
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 265, in default_headers
    elif self.should_close():
  File "/var/www/imd/venv/local/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 198, in should_close
    if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'

@marklofff
Copy link

did one fixed it ?.....

@kramer65
Copy link

@qwexvf - Unfortunately I didn't. My logs are still filling up with errors. :-(

Anybody else?

@marklofff
Copy link

@kramer65 i see. i've been waiting this for ages, if anyone has the fix please share with us! thanks!

@Hum4n01d
Copy link

any updates?

@stefaang
Copy link
Contributor

stefaang commented Dec 6, 2016

I'm sure there are cleaner ways to fix this, but at least it's a start.

@marklofff
Copy link

@stefaang thanks im gonna try thought it!

@eddie-dunn
Copy link

When will the fix for this be released on PyPI?

@benoitc
Copy link
Owner

benoitc commented Feb 17, 2017 via email

@jocms
Copy link

jocms commented Feb 20, 2017

Has it been released?

@eddie-dunn
Copy link

@juliedavila
Copy link

@benoitc any word on the update being pushed to pypi?

@benoitc
Copy link
Owner

benoitc commented Feb 22, 2017

@defionscode i will make a release in on thursday morning (tomorrow).

@juliedavila
Copy link

juliedavila commented Feb 22, 2017 via email

@ghost
Copy link

ghost commented Mar 2, 2017

Has this fix been released?

@benoitc
Copy link
Owner

benoitc commented Mar 2, 2017

@Decker108 status is tracked by #1471 . It will be released today...

@nicgirault
Copy link

This bug is still happening on my side with the latest version 19 and 20 of gunicorn. Was it supposed to be fixed @benoitc ?

@andyDoucette
Copy link

andyDoucette commented Oct 26, 2023

Please re-open. This bug is still very much active, even with gunicorn==21.2.0.

@tilgovi tilgovi reopened this Dec 28, 2023
@tilgovi
Copy link
Collaborator

tilgovi commented Dec 28, 2023

Re-opened and assigned to me.

@tilgovi tilgovi self-assigned this Dec 28, 2023
tilgovi added a commit that referenced this issue Dec 28, 2023
A new protocol, such as WebSockets or HTTP/2, may manage the framing of
multiple messages or requests of that protocol over a single connection.

- Close connections after completion of any upgraded response.

- Close connections after a response with status code less than 200. Any
  such response should be only for a protocol upgrade since applications
  never see any `Expect` header.

- Ensure that the `status_code` member always exists on instances of the
  `Response` class (close #1210).
@tilgovi tilgovi linked a pull request Dec 28, 2023 that will close this issue
tilgovi added a commit that referenced this issue Dec 28, 2023
A new protocol, such as WebSockets or HTTP/2, may manage the framing of
multiple messages or requests of that protocol over a single connection.
Once the WSGI callable returns, the connection should close.

- Close connections after completion of any upgraded response.

- Close connections after a response with status code less than 200. Any
  such response should be only for a protocol upgrade since applications
  never see any `Expect` header.

- Ensure that the `status_code` member always exists on instances of the
  `Response` class (close #1210).
tilgovi added a commit that referenced this issue Dec 28, 2023
A new protocol, such as WebSockets or HTTP/2, may manage the framing of
multiple messages or requests of that protocol over a single connection,
but the connection should close once the WSGI callable returns.

- Close connections after completion of any upgraded response.

- Close connections after a response with status code less than 200. Any
  such response should be only for a protocol upgrade since applications
  never see any `Expect` header.

- Ensure that the `status_code` member always exists on instances of the
  `Response` class (close #1210).
tilgovi added a commit that referenced this issue Dec 28, 2023
A new protocol, such as WebSockets or HTTP/2, may manage the framing of
multiple messages or requests of that protocol over a single connection,
but the connection should close once the WSGI callable returns.

- Close connections after completion of any upgraded response.

- Close connections after a response with status code less than 200. Any
  such response should be only for a protocol upgrade since applications
  never see any `Expect` header.

- Ensure that the `status_code` member always exists on instances of the
  `Response` class (close #1210).
@tilgovi
Copy link
Collaborator

tilgovi commented Dec 28, 2023

I think this could happen if the application raises an exception when handling a request before it starts a response. Gunicorn tries to figure out if the response was started and the headers should be sent but the status code is not defined yet. I believe I've fixed this issue in #3126 while fixing an unrelated issue with protocol upgrades (e.g. WebSockets).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Discussion Feedback Requested help wanted Open for everyone. You do not need permission to work on these. May need familiarity with codebase. Investigation
Projects
None yet