Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Fix logging of incorrect status codes for disconnected requests (#12580)
Browse files Browse the repository at this point in the history
The status code of requests must always be set, regardless of client
disconnection, otherwise they will always be logged as 200!.

Broken for `respond_with_json` in
f48792e.
Broken for `respond_with_json_bytes` in
3e58ce7.
Broken for `respond_with_html_bytes` in
ea26e9a.

Signed-off-by: Sean Quah <seanq@element.io>
  • Loading branch information
squahtx authored Apr 28, 2022
1 parent 629aa51 commit 0b684b5
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.d/12580.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a long standing bug where status codes would almost always get logged as 200!, irrespective of the actual status code, when clients disconnect before a request has finished processing.
12 changes: 9 additions & 3 deletions synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,9 @@ def respond_with_json(
Returns:
twisted.web.server.NOT_DONE_YET if the request is still active.
"""
# The response code must always be set, for logging purposes.
request.setResponseCode(code)

# could alternatively use request.notifyFinish() and flip a flag when
# the Deferred fires, but since the flag is RIGHT THERE it seems like
# a waste.
Expand All @@ -697,7 +700,6 @@ def respond_with_json(
else:
encoder = _encode_json_bytes

request.setResponseCode(code)
request.setHeader(b"Content-Type", b"application/json")
request.setHeader(b"Cache-Control", b"no-cache, no-store, must-revalidate")

Expand Down Expand Up @@ -728,13 +730,15 @@ def respond_with_json_bytes(
Returns:
twisted.web.server.NOT_DONE_YET if the request is still active.
"""
# The response code must always be set, for logging purposes.
request.setResponseCode(code)

if request._disconnected:
logger.warning(
"Not sending response to request %s, already disconnected.", request
)
return None

request.setResponseCode(code)
request.setHeader(b"Content-Type", b"application/json")
request.setHeader(b"Content-Length", b"%d" % (len(json_bytes),))
request.setHeader(b"Cache-Control", b"no-cache, no-store, must-revalidate")
Expand Down Expand Up @@ -840,6 +844,9 @@ def respond_with_html_bytes(request: Request, code: int, html_bytes: bytes) -> N
code: The HTTP response code.
html_bytes: The HTML bytes to use as the response body.
"""
# The response code must always be set, for logging purposes.
request.setResponseCode(code)

# could alternatively use request.notifyFinish() and flip a flag when
# the Deferred fires, but since the flag is RIGHT THERE it seems like
# a waste.
Expand All @@ -849,7 +856,6 @@ def respond_with_html_bytes(request: Request, code: int, html_bytes: bytes) -> N
)
return None

request.setResponseCode(code)
request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
request.setHeader(b"Content-Length", b"%d" % (len(html_bytes),))

Expand Down
14 changes: 13 additions & 1 deletion tests/handlers/test_cas.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,16 @@ def test_required_attributes(self) -> None:

def _mock_request():
"""Returns a mock which will stand in as a SynapseRequest"""
return Mock(spec=["getClientIP", "getHeader", "_disconnected"])
mock = Mock(
spec=[
"finish",
"getClientIP",
"getHeader",
"setHeader",
"setResponseCode",
"write",
]
)
# `_disconnected` musn't be another `Mock`, otherwise it will be truthy.
mock._disconnected = False
return mock
14 changes: 13 additions & 1 deletion tests/handlers/test_saml.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,4 +349,16 @@ def test_attribute_requirements(self) -> None:

def _mock_request():
"""Returns a mock which will stand in as a SynapseRequest"""
return Mock(spec=["getClientIP", "getHeader", "_disconnected"])
mock = Mock(
spec=[
"finish",
"getClientIP",
"getHeader",
"setHeader",
"setResponseCode",
"write",
]
)
# `_disconnected` musn't be another `Mock`, otherwise it will be truthy.
mock._disconnected = False
return mock

0 comments on commit 0b684b5

Please sign in to comment.