From 2769dc6367ba8fa502b04c4f223eaa5f9ed01315 Mon Sep 17 00:00:00 2001 From: Kevin Chung Date: Wed, 13 May 2020 19:33:01 -0400 Subject: [PATCH] =?UTF-8?q?Have=20EventManagers=20yield=20before=20timer?= =?UTF-8?q?=20code=20to=20force=20SSE=20response=20head=E2=80=A6=20(#1400)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * EventManagers should send an initial ping event to force `text/event-steam` header to be set --- CTFd/utils/events/__init__.py | 8 ++++++++ tests/utils/test_events.py | 33 +++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CTFd/utils/events/__init__.py b/CTFd/utils/events/__init__.py index 745505c9..3af5e01f 100644 --- a/CTFd/utils/events/__init__.py +++ b/CTFd/utils/events/__init__.py @@ -52,6 +52,10 @@ class EventManager(object): self.clients.append(q) while True: try: + # Immediately yield a ping event to force Response headers to be set + # or else some reverse proxies will incorrectly buffer SSE + yield ServerSentEvent(data="", type="ping") + with Timeout(10): message = q[channel].get() yield ServerSentEvent(**message) @@ -76,6 +80,10 @@ class RedisEventManager(EventManager): pubsub = self.client.pubsub() pubsub.subscribe(channel) try: + # Immediately yield a ping event to force Response headers to be set + # or else some reverse proxies will incorrectly buffer SSE + yield ServerSentEvent(data="", type="ping") + with Timeout(10) as timeout: for message in pubsub.listen(): if message["type"] == "message": diff --git a/tests/utils/test_events.py b/tests/utils/test_events.py index 13a54767..eacd210c 100644 --- a/tests/utils/test_events.py +++ b/tests/utils/test_events.py @@ -35,11 +35,18 @@ def test_event_manager_subscription(): fake_queue.return_value = saved_event event_manager = EventManager() - for message in event_manager.subscribe(): - assert message.to_dict() == saved_event - assert message.__str__().startswith("event:notification\ndata:") - assert len(event_manager.clients) == 1 - break + events = event_manager.subscribe() + message = next(events) + assert isinstance(message, ServerSentEvent) + assert message.to_dict() == {"data": "", "type": "ping"} + assert message.__str__().startswith("event:ping") + assert len(event_manager.clients) == 1 + + message = next(events) + assert isinstance(message, ServerSentEvent) + assert message.to_dict() == saved_event + assert message.__str__().startswith("event:notification\ndata:") + assert len(event_manager.clients) == 1 def test_event_manager_publish(): @@ -144,11 +151,17 @@ def test_redis_event_manager_subscription(): with patch.object(redis.client.PubSub, "listen") as fake_pubsub_listen: fake_pubsub_listen.return_value = [saved_event] event_manager = RedisEventManager() - for message in event_manager.subscribe(): - assert isinstance(message, ServerSentEvent) - assert message.to_dict() == saved_data - assert message.__str__().startswith("event:notification\ndata:") - break + + events = event_manager.subscribe() + message = next(events) + assert isinstance(message, ServerSentEvent) + assert message.to_dict() == {"data": "", "type": "ping"} + assert message.__str__().startswith("event:ping") + + message = next(events) + assert isinstance(message, ServerSentEvent) + assert message.to_dict() == saved_data + assert message.__str__().startswith("event:notification\ndata:") destroy_ctfd(app)