-
-
Notifications
You must be signed in to change notification settings - Fork 99
Ensure response headers are being sent before closing the response #75
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
Ensure response headers are being sent before closing the response #75
Conversation
959fd81 to
ebedc24
Compare
Codecov Report
@@ Coverage Diff @@
## master #75 +/- ##
========================================
+ Coverage 65.67% 66% +0.33%
========================================
Files 15 15
Lines 2753 2780 +27
========================================
+ Hits 1808 1835 +27
Misses 945 945 |
|
Original PR authors/reviewers seem to be: @moigagoo @ronin8600 @Lawouach |
|
Thanks for the PR. |
482f05b to
562913c
Compare
|
@webknjaz I added a test, I verified that it fails without this patch - and it passes with the patch :) - Let me know how this can be improved. |
562913c to
c17bff6
Compare
|
@josejulio first of all, linters in CI fail: https://travis-ci.org/cherrypy/cheroot/jobs/340814348#L484 P.S. Also, it's possible to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plus code needs to bypass linters
cheroot/test/test_core.py
Outdated
|
|
||
| def test_send_header_before_closing(testing_server_close): | ||
| """Tests we are actually sending the headers before closing the response.""" | ||
| testing_server_close.server_client.get('/') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about checking http response headers in client?
resp_status_line, resp_headers, resp_body = testing_server_close.server_client.get('/')There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, maybe my description is wrong here or i didn't understand well.
The headers are always sent, the problem is that the close [1] method is being call before the headers are being sent.
This is a problem in cherrypy, because the on_end_request is being called before the headers are actually sent, anything sent to the client from this hook will reach before the headers, thus the headers will be malformed. I don't think I can test this from the client side unless i can get a raw response, but i would still need to send something on that close [1] method.
It's done in a similar fashion in pep-0333 [2] (talking about the fix, not the tests)
try:
for data in result:
if data: # don't send headers until body appears
write(data)
if not headers_sent:
write('') # send headers now if body was empty
finally:
if hasattr(result, 'close'):
result.close()[1] https://github.com/cherrypy/cheroot/pull/75/files#diff-08d927a6f63bcf494ed88cca2a6873d0R144
[2] https://www.python.org/dev/peps/pep-0333/
cheroot/test/test_core.py
Outdated
| def close(self): | ||
| """Hook for close, tests if headers already sent.""" | ||
| self.sent_headers_before_closing = \ | ||
| self.req is not None and self.req.sent_headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be overengineered. Please see my comment below.
376de01 to
bb2ddde
Compare
|
@webknjaz I fixed linting issues and tried to simplify the test. Here is the output of the test without the fix (commit: 742b1b9) |
|
|
||
| if (self.ready and not self.sent_headers): | ||
| self.sent_headers = True | ||
| self.send_headers() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why completely remove this? How will it affect non-WSGI plain HTTP server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I though that adding it to self.server.gateway(self).respond() would cover it
I suppose I can add it back to ensure we are always sending the headers in case the gateway is not sending the headers.
cheroot/test/test_core.py
Outdated
|
|
||
| def close(self): | ||
| """Hook for close, write hello.""" | ||
| self.req.write('hello'.encode('utf-8')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b'hello' would look nicer
cheroot/wsgi.py
Outdated
| self.write(chunk) | ||
| finally: | ||
| # Send headers if not already sent | ||
| if not self.req.sent_headers: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always paired with "if not sent" check. Try moving it inside the method and maybe rename it into smth like ensure_headers_sent or so
|
@josejulio sorry for late reply, I've added some thoughts on this |
Better late than never :). |
Moved the test if the headers are sent inside the ensure_headers_sent (renamed from write_headers) Puts back a "send headers" test.
|
@webknjaz Addressed the comments, travis is green and this is ready for other review round, thanks! |
cheroot/server.py
Outdated
| self.rfile = KnownLengthRFile(self.conn.rfile, cl) | ||
|
|
||
| self.server.gateway(self).respond() | ||
| self.ensure_headers_sent() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you've lost ready check here. Could be:
self.ready and self.ensure_headers_sent()Is this intentional? What is the effect of a such change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not intentional, missed that, thanks for spotting it!
|
@webknjaz Forgot to say that addressed your latest comments, thanks |
|
LGTM! Thank you @josejulio! |
|
@josejulio This code should reach PYPI soon, when this build is complete: https://travis-ci.org/cherrypy/cheroot/builds/362369631 It should become available at https://pypi.org/project/Cheroot/6.1.0/ in about 30 min from now. |
|
Awesome, thank you! |
Fixes cherrypy/cherrypy#1404
Bugfix
#)cherrypy/cherrypy#1404
on_end_request hook is being call before sending a response on cherrypy
on_end_request hook is being call after sending a response on cherrypy
I found Lawouach/WebSocket-for-Python#158 which lead me to this "PR" https://bitbucket.org/cherrypy/cherrypy/pull-requests/121/fixing-issue-1404-on_end_request-hook-is/diff
I ported that to cheroot and addressed the last comment from "Sylvain Hellegouarch"