wip: attempt to replace websockets by SSE
Websockets were unnecessary, as we only need 1-way communication. Instead, we switched to server-sent events. But there are a couple of problems: 1. We still can't stop the server while a websocket connection is open 2. SSE make it difficult to format messages So this is not a big win for now...
This commit is contained in:
parent
a2e3018de4
commit
0565781c15
@ -9,9 +9,9 @@ import typing as t
|
||||
import aiofiles
|
||||
from quart import (
|
||||
Quart,
|
||||
make_response,
|
||||
render_template,
|
||||
request,
|
||||
websocket,
|
||||
redirect,
|
||||
url_for,
|
||||
)
|
||||
@ -118,10 +118,7 @@ class TutorCli:
|
||||
"""
|
||||
Sets the stop flag, whic is monitored by all subprocess.Popen commands.
|
||||
"""
|
||||
app.logger.info(
|
||||
"Stopping Tutor command: %s...",
|
||||
self.command,
|
||||
)
|
||||
app.logger.info("Stopping Tutor command: %s...", self.command)
|
||||
self._stop_flag.set()
|
||||
|
||||
async def iter_logs(self) -> t.AsyncGenerator[str, None]:
|
||||
@ -273,11 +270,9 @@ class TutorCliPool:
|
||||
replaced by another one, previous logs are not deleted. New ones are simply
|
||||
appended.
|
||||
"""
|
||||
while True:
|
||||
if cls.INSTANCE:
|
||||
async for log in cls.INSTANCE.iter_logs():
|
||||
yield log
|
||||
await asyncio.sleep(SHORT_SLEEP_SECONDS)
|
||||
while cls.INSTANCE:
|
||||
async for log in cls.INSTANCE.iter_logs():
|
||||
yield log
|
||||
|
||||
|
||||
app = Quart(
|
||||
@ -341,27 +336,46 @@ async def tutor_cli() -> WerkzeugResponse:
|
||||
# ["config", "printvalue", "POUAC"],
|
||||
# ["local", "launch", "--non-interactive"],
|
||||
)
|
||||
return redirect(url_for("tutor_logs"))
|
||||
return redirect(url_for("tutor_cli_logs"))
|
||||
|
||||
|
||||
@app.post("/tutor/cli/stop")
|
||||
async def tutor_cli_stop() -> WerkzeugResponse:
|
||||
TutorCliPool.stop()
|
||||
return redirect(url_for("tutor_logs"))
|
||||
return redirect(url_for("tutor_cli_logs"))
|
||||
|
||||
|
||||
@app.get("/tutor/logs")
|
||||
async def tutor_logs() -> str:
|
||||
return await render_template("tutor_logs.html", **shared_template_context())
|
||||
async def tutor_cli_logs() -> str:
|
||||
return await render_template("tutor_cli_logs.html", **shared_template_context())
|
||||
|
||||
|
||||
@app.websocket("/tutor/logs/stream")
|
||||
async def tutor_logs_stream() -> None:
|
||||
async for content in TutorCliPool.iter_logs():
|
||||
try:
|
||||
await websocket.send(content)
|
||||
except asyncio.CancelledError:
|
||||
return
|
||||
@app.get("/tutor/cli/logs/stream")
|
||||
async def tutor_cli_logs_stream() -> None:
|
||||
# Websockets were not working for us in dev mode, we were unable to stop the server
|
||||
# as long as there were open connection. We only need single-direction
|
||||
# communication, so we use server-sent events
|
||||
# https://github.com/pallets/quart/issues/333
|
||||
# https://quart.palletsprojects.com/en/latest/how_to_guides/server_sent_events.html
|
||||
async def send_events():
|
||||
while True:
|
||||
# TODO this is again causing the stream to never stop...
|
||||
async for data in TutorCliPool.iter_logs():
|
||||
event = f"data: {data}\nevent: logs\n"
|
||||
# TODO encode one way or another to be able to send EOL characters and other weird chars
|
||||
yield event.encode()
|
||||
await asyncio.sleep(SHORT_SLEEP_SECONDS)
|
||||
|
||||
response = await make_response(
|
||||
send_events(),
|
||||
{
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
"Transfer-Encoding": "chunked",
|
||||
},
|
||||
)
|
||||
response.timeout = None
|
||||
return response
|
||||
|
||||
|
||||
def shared_template_context() -> dict[str, t.Any]:
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
<link href="{{ url_for('static', filename='/css/dash.css') }}" rel="stylesheet">
|
||||
<!-- TODO self-host -->
|
||||
<script src="https://unpkg.com/htmx.org@2.0.3/dist/htmx.min.js"></script>
|
||||
<!-- WS extension https://htmx.org/extensions/ws/ TODO self host -->
|
||||
<script src="https://unpkg.com/htmx-ext-ws@2.0.1/ws.js"></script>
|
||||
<!-- SSE extension https://htmx.org/extensions/sse/ TODO self host and move to dedicated page-->
|
||||
<script src="https://unpkg.com/htmx-ext-sse@2.2.2/sse.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -31,7 +31,7 @@
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li>Configuration</li>
|
||||
<li><a href="{{ url_for('tutor_logs') }}">Command logs</a></li>
|
||||
<li><a href="{{ url_for('tutor_cli_logs') }}">Command logs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
22
tutordash/server/templates/tutor_cli_logs.html
Normal file
22
tutordash/server/templates/tutor_cli_logs.html
Normal file
@ -0,0 +1,22 @@
|
||||
{% extends 'index.html' %}
|
||||
|
||||
{% block workspace_header %}Tutor command logs{% endblock %}
|
||||
|
||||
{% block workspace_content %}
|
||||
<pre id="tutor-log" hx-ext="sse" sse-connect="{{ url_for('tutor_cli_logs_stream') }}" sse-swap="logs"></pre>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// TODO fix me
|
||||
htmx.on("htmx:sseBeforeMessage", function(evt) {
|
||||
// Don't swap content, we want to append
|
||||
evt.preventDefault();
|
||||
|
||||
// Note that HTML is automatically escaped
|
||||
const text = document.createTextNode(evt.detail.data);
|
||||
evt.detail.elt.appendChild(text);
|
||||
evt.detail.elt.scrollTop = evt.detail.elt.scrollHeight;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -1,20 +0,0 @@
|
||||
{% extends 'index.html' %}
|
||||
|
||||
{% block workspace_header %}Tutor command logs{% endblock %}
|
||||
|
||||
{% block workspace_content %}
|
||||
<pre id="tutor-log" hx-ext="ws" ws-connect="{{ url_for('tutor_logs_stream') }}"></pre>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
htmx.on("htmx:wsAfterMessage", function(evt) {
|
||||
const logs = document.getElementById("tutor-log");
|
||||
|
||||
// Note that HTML is automatically escaped
|
||||
const text = document.createTextNode(evt.detail.message);
|
||||
logs.appendChild(text);
|
||||
logs.scrollTop = logs.scrollHeight;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user