Skip to content

Commit af0a9f9

Browse files
committed
added live responses which can be written and read in separate threads
1 parent d4433a3 commit af0a9f9

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

Diff for: actionpack/lib/action_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'abstract_controller'
22
require 'action_dispatch'
3+
require 'action_controller/metal/live'
34

45
module ActionController
56
extend ActiveSupport::Autoload

Diff for: actionpack/lib/action_controller/metal/live.rb

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require 'action_dispatch/http/response'
2+
3+
module ActionController
4+
module Live
5+
class Response < ActionDispatch::Response
6+
class Buffer < ActionDispatch::Response::Buffer # :nodoc:
7+
def initialize(response)
8+
@response = response
9+
@buf = Queue.new
10+
end
11+
12+
def write(string)
13+
unless @response.committed?
14+
@response.headers["Cache-Control"] = "no-cache"
15+
@response.headers.delete("Content-Length")
16+
end
17+
18+
super
19+
end
20+
21+
def each
22+
while str = @buf.pop
23+
yield str
24+
end
25+
end
26+
27+
def close
28+
super
29+
@buf.push nil
30+
end
31+
end
32+
33+
private
34+
35+
def build_buffer(response, body)
36+
buf = Buffer.new response
37+
body.each { |part| buf.write part }
38+
buf
39+
end
40+
end
41+
end
42+
end

Diff for: actionpack/test/controller/live_stream_test.rb

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'abstract_unit'
2+
3+
module ActionController
4+
class StreamingResponseTest < ActionController::TestCase
5+
class TestController < ActionController::Base
6+
def self.controller_path
7+
'test'
8+
end
9+
10+
def basic_stream
11+
%w{ hello world }.each do |word|
12+
response.stream.write word
13+
response.stream.write "\n"
14+
end
15+
response.stream.close
16+
end
17+
end
18+
19+
tests TestController
20+
21+
def test_write_to_stream
22+
get :basic_stream
23+
assert_equal "hello\nworld\n", @response.body
24+
end
25+
end
26+
end

Diff for: actionpack/test/dispatch/live_response_test.rb

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'abstract_unit'
2+
require 'active_support/concurrency/latch'
3+
4+
module ActionController
5+
module Live
6+
class ResponseTest < ActiveSupport::TestCase
7+
def setup
8+
@response = Live::Response.new
9+
end
10+
11+
def test_parallel
12+
latch = ActiveSupport::Concurrency::Latch.new
13+
14+
t = Thread.new {
15+
@response.stream.write 'foo'
16+
latch.await
17+
@response.stream.close
18+
}
19+
20+
@response.each do |part|
21+
assert_equal 'foo', part
22+
latch.release
23+
end
24+
assert t.join
25+
end
26+
27+
def test_setting_body_populates_buffer
28+
@response.body = 'omg'
29+
@response.close
30+
assert_equal ['omg'], @response.body_parts
31+
end
32+
end
33+
end
34+
end
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require 'thread'
2+
require 'monitor'
3+
4+
module ActiveSupport
5+
module Concurrency
6+
class Latch
7+
def initialize(count = 1)
8+
@count = count
9+
@lock = Monitor.new
10+
@cv = @lock.new_cond
11+
end
12+
13+
def release
14+
@lock.synchronize do
15+
@count -= 1 if @count > 0
16+
@cv.broadcast if @count.zero?
17+
end
18+
end
19+
20+
def await
21+
@lock.synchronize do
22+
@cv.wait_while { @count > 0 }
23+
end
24+
end
25+
end
26+
end
27+
end

0 commit comments

Comments
 (0)