summary refs log tree commit diff
diff options
context:
space:
mode:
authorRichard van der Hoff <richard@matrix.org>2018-08-14 16:13:17 +0100
committerRichard van der Hoff <richard@matrix.org>2018-08-15 15:04:16 +0100
commitafcd655ab67611e3bbac3bc992574baa464bc030 (patch)
treeaf5746c92e5596ff56ff65f021ef30a4834c36b3
parentMerge pull request #3687 from matrix-org/neilj/admin_email (diff)
downloadsynapse-afcd655ab67611e3bbac3bc992574baa464bc030.tar.xz
Use a producer to stream back responses
The problem with dumping all of the json response into the Request object at
once is that doing so starts the timeout for the next request to be received:
so if it takes longer than 60s to stream back the response to the client, the
client never gets it.

The correct solution is to use a Producer; then the timeout is only started
once all of the content is sent over the TCP connection.
-rw-r--r--synapse/http/server.py17
1 files changed, 13 insertions, 4 deletions
diff --git a/synapse/http/server.py b/synapse/http/server.py
index 6dacb31037..5733abb5dc 100644
--- a/synapse/http/server.py
+++ b/synapse/http/server.py
@@ -27,6 +27,7 @@ from twisted.internet import defer
 from twisted.python import failure
 from twisted.web import resource, server
 from twisted.web.server import NOT_DONE_YET
+from twisted.web.static import NoRangeStaticProducer
 from twisted.web.util import redirectTo
 
 import synapse.events
@@ -42,6 +43,11 @@ from synapse.util.caches import intern_dict
 from synapse.util.logcontext import LoggingContext, PreserveLoggingContext
 from synapse.util.metrics import Measure
 
+if PY3:
+    from io import BytesIO
+else:
+    from cStringIO import StringIO as BytesIO
+
 logger = logging.getLogger(__name__)
 
 HTML_ERROR_TEMPLATE = """<!DOCTYPE html>
@@ -413,8 +419,7 @@ def respond_with_json(request, code, json_object, send_cors=False,
         return
 
     if pretty_print:
-        json_bytes = (encode_pretty_printed_json(json_object) + "\n"
-                      ).encode("utf-8")
+        json_bytes = encode_pretty_printed_json(json_object) + b"\n"
     else:
         if canonical_json or synapse.events.USE_FROZEN_DICTS:
             # canonicaljson already encodes to bytes
@@ -450,8 +455,12 @@ def respond_with_json_bytes(request, code, json_bytes, send_cors=False,
     if send_cors:
         set_cors_headers(request)
 
-    request.write(json_bytes)
-    finish_request(request)
+    # todo: we can almost certainly avoid this copy and encode the json straight into
+    # the bytesIO, but it would involve faffing around with string->bytes wrappers.
+    bytes_io = BytesIO(json_bytes)
+
+    producer = NoRangeStaticProducer(request, bytes_io)
+    producer.start()
     return NOT_DONE_YET