python - 在运行 Sophos 的机器上,为什么我的所有浏览器都无法从我的 Python 应用程序实时接收服务器发送的事件 (sse)?
问题描述
我的 ASGI 应用程序可以很好地向 curl 和我的手机发送事件。但是,即使服务器正在发送事件,并且标头看起来正确,但我的 Windows 机器上的 Firefox 和 Chrome 都不会收到事件,直到连接关闭。
无论我将服务器托管在 WSL、Powershell 终端还是单独的 Linux 机器上,都会发生这种情况。
但是,如果我在 repl.it 上托管服务器,这些相同的浏览器可以正常工作(请分叉并试用)。
我曾尝试摆弄 Windows 防火墙设置,但无济于事。
这是应用程序代码:
import asyncio
import datetime
async def app(scope, receive, send):
headers = [(b"content-type", b"text/html")]
if scope["path"] == "/":
body = (
"<html>"
"<body>"
"</body>"
"<script>"
" let eventSource = new EventSource('/sse');"
" eventSource.addEventListener('message', (e) => {"
" document.body.innerHTML += e.data + '<br>';"
" });"
"</script>"
"</html>"
).encode()
await send({"type": "http.response.start", "status": 200, "headers": headers})
await send({"type": "http.response.body", "body": body})
elif scope["path"] == "/sse":
headers = [
(b"content-type", b"text/event-stream"),
(b"cache-control", b"no-cache"),
(b"connection", b"keep-alive"),
]
async def body():
ongoing = True
while ongoing:
try:
payload = datetime.datetime.now()
yield f"data: {payload}\n\n".encode()
await asyncio.sleep(10)
except asyncio.CancelledError:
ongoing = False
await send({"type": "http.response.start", "status": 200, "headers": headers})
async for chunk in body():
await send({"type": "http.response.body", "body": chunk, "more_body": True})
await send({"type": "http.response.body", "body": b""})
else:
await send({"type": "http.response.start", "status": 404, "headers": headers})
await send({"type": "http.response.body", "body": b""})
这可以通过将上面的文件命名为asgi_sse.py
, 然后pip install uvicorn
, 然后使用类似的东西来运行
uvicorn asgi_sse:app
(替换daphne
或hypercorn
代替uvicorn
上面的内容,看看这些服务器如何处理应用程序。)
标题:
$ curl -I http://localhost:8000/sse
HTTP/1.1 200 OK
date: Mon, 01 Jun 2020 09:51:41 GMT
server: uvicorn
content-type: text/event-stream
cache-control: no-cache
connection: keep-alive
和回应:
$ curl http://localhost:8000/sse
data: 2020-06-01 05:52:40.735403
data: 2020-06-01 05:52:50.736378
data: 2020-06-01 05:53:00.736812
任何见解都非常受欢迎!
解决方案
我最近遇到了同样的问题,但使用的是 NodeJS + Express 应用程序。如果连接不安全,我最终会发送一个 2 兆字节的块。
if (!req.secure) {
res.write(new Array(1024 * 1024).fill(0).toString());
}
这是为了让服务器发送的事件在没有安全连接的情况下在开发环境中工作。
完整的实现:
app.use('/stream', (req, res) => {
res.set({
'Content-Type': 'text/event-stream;charset=utf-8',
'Cache-Control': 'no-cache, no-transform',
'Content-Encoding': 'none',
'Connection': 'keep-alive'
});
res.flushHeaders();
if (!req.secure) {
res.write(new Array(1024 * 1024).fill(0).toString());
}
const sendEvent = (event, data) => {
res.write(`event: ${String(event)}\n`);
res.write(`data: ${data}`);
res.write('\n\n');
res.flushHeaders();
};
const intervalId = setInterval(() => {
sendEvent('ping', new Date().toLocaleTimeString());
}, 5000);
req.on('close', () => {
clearInterval(intervalId);
});
});
推荐阅读
- sublimetext3 - SublimeText3 保持行折叠
- google-cloud-platform - Gcloud 的功能从 shell 到 cloudbuild.yaml 不同
- ios - 有没有办法通过使用 UIButton 操作增加计数来遍历 Swift 中的数组中的元素?
- c# - 通过 LINQ 从 XML 反序列化
- python - 将字典和列表值动态传递给 json 有效负载
- rest - 仅反序列化少数属性
- r - 如何保存高度很长的图片
- python - 带有 API 调用的 Django LRU_Cache
- lisp - 在 lisp 中查找最大值
- ios - HKAnchoredObejctQuery 不返回新结果+不一致