问题描述
@H_502_0@我有一个Puma应用程序,当您输入IP地址并通过单击复选框选择对其进行跟踪路由时,它将执行traceroute:
@H_502_0@下面的代码(实际上,该应用程序有更多功能,但出于这个问题,我将其简化了):
@H_502_0@ Site.erb:
class Pumatra < Sinatra::Base
get '/' do
erb :index
end
post '/run' do
params.to_s
end
get'/traceroute_results' do
@ipaddress = params[:ipaddress]
@traceroute = params[:traceroute]
if @traceroute == "on"
stdout,status = Open3.capture2("traceroute -4 -w3 #{@ipaddress}")
@traceroute_result ="<pre><code>" + stdout + "</code></pre>"
end
@traceroute_header
@traceroute_result
end
@traceroute_thread = Thread.new{traceroute()}
@traceroute_thread.join
erb :traceroute_results
end
end
@H_502_0@我的views / index.erb文件包含此内容(仅向大家显示相关位):
....
<div class="checkBox">
<label><input type="checkBox" id="traceroute"
name="traceroute">Traceroute</label>
</div>
....
<!-- Start Results block -->
<section id="results_block" style="display:none;" class="l_panel bg_color_white l_relative">
<div id="traceroute_results" style="display:none;" class="l_grid"></div>
</section>
<!-- End Results block -->
@H_502_0@一切正常。 Traceroute运行,完成后将显示结果。
但是,有时这会花一些时间才能完成,因为跳数没有响应,尽管这是正常现象,但最终确实会给您带来结果。
我的问题是:仅出于装饰性原因,我想在运行时显示输出traceroute。因此,更多的是渐进式视图,而不是最后的完整输出。
这可能吗,我将如何去做?
@H_502_0@欣赏提示,
J
@H_502_0@ 编辑:
@H_502_0@我在site.erb中尝试过
get'/traceroute_results' do
@traceroute_header = "<br>TCP Traceroute results:"
IO.popen("traceroute -4 -w3 #{@ipaddress}") do |io|
io.each do |line|
@traceroute_result = line
erb :traceroute_results
end
end
end
@H_502_0@但是现在我的应用程序未显示traceroute的任何输出:(
@H_502_0@我的traceroute_results.erb
文件:
<%= @traceroute_header %>
<%= @traceroute_result %>
@H_502_0@ 编辑:
Amadan的有用文章将我引到了我的site.erb中的这段代码:
get'/traceroute_results' do
@ipaddress = params[:ipaddress]
@traceroute = params[:traceroute]
if @traceroute == "on"
ipaddress = params[:ipaddress]
stream do |res|
res << erb(:traceroute_header)
Open3.popen3("traceroute -T -4 #{@ipaddress}") do |stdin,stdout,stderr,wait_thr|
while line = stderr.gets
res << erb(:traceroute_results,{},{ out: line })
end
end
end
end
end
@H_502_0@并将我的traceroute_results
文件更改为:
<%= out %>
@H_502_0@不幸的是,URL加载后我仍然什么也没收到。我可以告诉它执行traceroute的原因,因为返回结果需要一段时间,但是返回为空。
@H_502_0@真的很感谢任何人的帮助和耐心。
我正在用Sinatra运行Ruby Puma。任何人都可以在这样的设置中成功运行吗?我无法在Sinama中找到在puma上下文中实际可用的示例...:(
@H_502_0@谢谢,
J
解决方法
首先,请注意erb
不会发送响应;它通常成为响应,因为其结果是Ruby函数中的最后一件事,该函数隐式返回,并且Sinatra以get
块的返回值作为响应。您当前的实现呈现您的子模板,但丢弃每个结果;同时,IO.popen(...) {...}
返回nil
,这是没有响应的原因。
现在,为了在Sinatra中生成流式响应,您可以返回响应#each
的内容,或使用stream
helper为您创建这样的对象。尝试更改为以下内容:
get '/traceroute_results' do
ipaddress = params[:ipaddress]
stream do |res|
res << erb(:traceroute_header)
IO.popen("traceroute -4 -w3 #{ipaddress}") do |io|
io.each do |line|
res << erb(:traceroute_result,{},{ line: line })
end
end
end
end
__END__
@@ traceroute_result
<div><%= line %></div>
@@ traceroute_header
<h4>Traceroute results</h4>
,
Amadan's answer既正确又不完整,在使用each
时可能导致服务器上的线程不足。
我不是Sinatra开发人员,但是我编写服务器代码,因此我不得不警告不要使用each
。
each
的问题是服务器在等待each
进行迭代并返回时,服务器对该线程没有无控制权-这会阻止线程处理其他请求。
更好的解决方案是使用线程池和队列来调用traceroute
,并使用SSE / WebSockets连接将输出从队列流传输到用户。
这可以保护服务器免受DoS攻击,并允许它:
-
如果同时有太多请求到达,则显示“等待中”的消息(也许以队列的长度为准)。
-
在
traceroute
到客户端的情况下将数据流传输到客户端,而不会阻塞服务器(将数据推送到客户端)。 -
如果客户端断开连接,则取消
traceroute
。
这可以使用WebSocket本地解决方案或劫持解决方案来执行。