问题描述
我正在尝试编写一个API,该API在Rails 6中使用ActionController::Live::SSE
来传递服务器发送的事件。为了理解如何最好地编写测试,我从copying the trivial example seen here开始:
my_controller.rb
:
class MyController < ApplicationController
include ActionController::Live
def capture
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream)
3.times do
sse.write({message: "Awaiting confirmation ..."})
sleep 2
end
fake_response = { #The response as hash.
"annotation_id"=>nil,"domain"=>"some.random.com","id"=>2216354,"path"=>"/flummoxer/","protocol"=>"https",}
sse.write(fake_response,event: 'successful capture')
rescue => e
sse.write(e.message,event: 'something broke: ')
ensure
response.stream.close
end
end
当我向该端点发送curl请求(无论是POST还是GET)时,响应全部以一个块的形式到达,而不是作为单独的响应到达
$ curl -i -X GET -H "Content-Type: application/json" -d '{"url": "https://some.random.com/flummoxer"}' http://localhost:3000/capture
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
ETag: W/"a24048695d2feca40232467f0fbb410a"
X-Request-Id: 648a5229-a43d-40d3-82fd-1c4ea6fe19cc
X-Runtime: 24.082528
transfer-encoding: chunked
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
event: successful capture
data: {"annotation_id":null,"domain":"some.random.com","id":2216354,"path":"/flummoxer/","protocol":"https"}
在我的测试中,尝试解析服务器的响应失败会很容易看出这一点:
MultiJson::ParseError: 783: unexpected token at 'data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
event: successful capture
data: {"annotation_id":null,"protocol":"https"}
'
我的服务器是Puma,所以it's not because I'm using Thin,as seen in this answer。
我在做什么错?如果您提出要求,我将提供其他可能有用的信息。
更新:answers to this question建议将-N
和Accept:text/event-stream
标头都添加到请求中。这样做不会改变我上面描述的行为-在触发response.stream.close
的调用之前,不会发送对请求的响应。
更新2:我还尝试破解SSE#write
方法以在broadcast()
上调用Mutex::ConditionVariable
来强制发送消息。从某种意义上说,它可以立即发送数据,但是它具有卷曲请求的副作用,认为该消息流已关闭,因此不会再发送不是流的消息。
更新3:我还修改了development.rb
使其包含config.allow_concurrency = true
,如here所示。上述行为没有任何变化。
解决方法
我遇到了一个基本的“出书” Rails 5 SSE应用程序类似的问题。事实证明,该问题是机架更新导致的流缓冲。 https://github.com/rack/rack/issues/1619此处提供了更多信息,并通过添加
进行了修复config.middleware.delete Rack :: ETag
在config / application.rb
中