我的ASGI应用程序可以很好地将事件发送到curl和我的手机上。然而,即使服务器正在发送事件,并且头文件看起来是正确的,我的Windows机器上的Firefox和Chrome都没有收到事件,直到连接关闭。
无论我是在WSL、Powershell终端还是在单独的Linux盒子上托管服务器,这种情况都会发生。
然而,如果我将服务器托管在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
欢迎提供任何见解!
我公司已经开启了Sophos Endpoint Security的网页防护功能。根据 此条目在 Sophos 的社区缓冲和扫描 textevent-stream 内容,以防止恶意软件。因此出现了意外的缓冲。
我找到了两个变通的办法。
上述问题不仅仅是我的代码的问题,也不是Uvicorn、Hypercorn或ASGI的问题。事实上,我甚至尝试了aiohttp的实现,结果也是一样的。然而,当我尝试了 a Go实例实施 上交所,以及 另一个在Node.js中 工作得很好,不需要任何变通方法。我看到的唯一区别是Go的实现在每个事件后都使用了一个flush方法。我不确定为什么ASGI和aiohttp不公开某种刷新方法,或者,如果他们公开了,为什么我找不到它。如果它们有,是否会使这些变通方法变得没有必要?我不确定。
下面是更新后的代码,让它与Sophos一起工作,并检查是否通过https服务。
async def app(scope, receive, send):
headers = [(b"content-type", b"text/html")]
if scope["path"] == "/":
body = (
"<!DOCTYPE html>"
"<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})
if scope["scheme"] != "https": # Sophos will buffer, so send 2MB first
two_meg_chunk = "." * 2048 ** 2
await send(
{
"type": "http.response.body",
"body": f": {two_meg_chunk}\n\n".encode(),
"more_body": True,
}
)
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""})