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

Commit

Permalink
Use a producer to stream back responses
Browse files Browse the repository at this point in the history
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 Producer; then the timeout is only started once
all of the content is sent over the TCP connection.

However, we then need to hook the completion logging in via notifyFinish (which
is a much better way of doing things anyway), in order to correctly read the
response length.
  • Loading branch information
richvdh committed Aug 15, 2018
1 parent 614e6d5 commit 6f3bf1b
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions synapse/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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
Expand All @@ -42,6 +43,11 @@
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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down

0 comments on commit 6f3bf1b

Please sign in to comment.