Skip to content

Commit

Permalink
Merge pull request matrix-org#574 from matrix-org/markjh/connection_c…
Browse files Browse the repository at this point in the history
…losed

Catch the exceptions thrown by twisted when you write to a closed con…
  • Loading branch information
NegativeMjark committed Feb 12, 2016
2 parents ec0f383 + 58c9f20 commit 66f9a49
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 11 deletions.
21 changes: 20 additions & 1 deletion synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,29 @@ def respond_with_json_bytes(request, code, json_bytes, send_cors=False,
"Origin, X-Requested-With, Content-Type, Accept")

request.write(json_bytes)
request.finish()
finish_request(request)
return NOT_DONE_YET


def finish_request(request):
""" Finish writing the response to the request.
Twisted throws a RuntimeException if the connection closed before the
response was written but doesn't provide a convenient or reliable way to
determine if the connection was closed. So we catch and log the RuntimeException
You might think that ``request.notifyFinish`` could be used to tell if the
request was finished. However the deferred it returns won't fire if the
connection was already closed, meaning we'd have to have called the method
right at the start of the request. By the time we want to write the response
it will already be too late.
"""
try:
request.finish()
except RuntimeError as e:
logger.info("Connection disconnected before response was written: %r", e)


def _request_user_agent_is_curl(request):
user_agents = request.requestHeaders.getRawHeaders(
"User-Agent", default=[]
Expand Down
10 changes: 6 additions & 4 deletions synapse/rest/client/v1/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

from synapse.api.errors import SynapseError, LoginError, Codes
from synapse.types import UserID
from synapse.http.server import finish_request

from base import ClientV1RestServlet, client_path_patterns

import simplejson as json
Expand Down Expand Up @@ -263,7 +265,7 @@ def on_POST(self, request):
'?status=authenticated&access_token=' +
token + '&user_id=' + user_id + '&ava=' +
urllib.quote(json.dumps(saml2_auth.ava)))
request.finish()
finish_request(request)
defer.returnValue(None)
defer.returnValue((200, {"status": "authenticated",
"user_id": user_id, "token": token,
Expand All @@ -272,7 +274,7 @@ def on_POST(self, request):
request.redirect(urllib.unquote(
request.args['RelayState'][0]) +
'?status=not_authenticated')
request.finish()
finish_request(request)
defer.returnValue(None)
defer.returnValue((200, {"status": "not_authenticated"}))

Expand Down Expand Up @@ -309,7 +311,7 @@ def on_GET(self, request):
"service": "%s?%s" % (hs_redirect_url, client_redirect_url_param)
})
request.redirect("%s?%s" % (self.cas_server_url, service_param))
request.finish()
finish_request(request)


class CasTicketServlet(ClientV1RestServlet):
Expand Down Expand Up @@ -362,7 +364,7 @@ def handle_cas_response(self, request, cas_response_body, client_redirect_url):
redirect_url = self.add_login_token_to_redirect_url(client_redirect_url,
login_token)
request.redirect(redirect_url)
request.finish()
finish_request(request)

def add_login_token_to_redirect_url(self, url, token):
url_parts = list(urlparse.urlparse(url))
Expand Down
5 changes: 3 additions & 2 deletions synapse/rest/client/v2_alpha/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from synapse.api.constants import LoginType
from synapse.api.errors import SynapseError
from synapse.api.urls import CLIENT_V2_ALPHA_PREFIX
from synapse.http.server import finish_request
from synapse.http.servlet import RestServlet

from ._base import client_v2_patterns
Expand Down Expand Up @@ -130,7 +131,7 @@ def on_GET(self, request, stagetype):
request.setHeader(b"Content-Length", b"%d" % (len(html_bytes),))

request.write(html_bytes)
request.finish()
finish_request(request)
defer.returnValue(None)
else:
raise SynapseError(404, "Unknown auth stage type")
Expand Down Expand Up @@ -176,7 +177,7 @@ def on_POST(self, request, stagetype):
request.setHeader(b"Content-Length", b"%d" % (len(html_bytes),))

request.write(html_bytes)
request.finish()
finish_request(request)

defer.returnValue(None)
else:
Expand Down
4 changes: 2 additions & 2 deletions synapse/rest/media/v0/content_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from synapse.http.server import respond_with_json_bytes
from synapse.http.server import respond_with_json_bytes, finish_request

from synapse.util.stringutils import random_string
from synapse.api.errors import (
Expand Down Expand Up @@ -144,7 +144,7 @@ def render_GET(self, request):
# after the file has been sent, clean up and finish the request
def cbFinished(ignored):
f.close()
request.finish()
finish_request(request)
d.addCallback(cbFinished)
else:
respond_with_json_bytes(
Expand Down
4 changes: 2 additions & 2 deletions synapse/rest/media/v1/base_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .thumbnailer import Thumbnailer

from synapse.http.matrixfederationclient import MatrixFederationHttpClient
from synapse.http.server import respond_with_json
from synapse.http.server import respond_with_json, finish_request
from synapse.util.stringutils import random_string
from synapse.api.errors import (
cs_error, Codes, SynapseError
Expand Down Expand Up @@ -238,7 +238,7 @@ def _respond_with_file(self, request, media_type, file_path,
with open(file_path, "rb") as f:
yield FileSender().beginFileTransfer(f, request)

request.finish()
finish_request(request)
else:
self._respond_404(request)

Expand Down

0 comments on commit 66f9a49

Please sign in to comment.