celluloid-eventsource 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.rspec +1 -1
- data/.travis.yml +5 -0
- data/README.md +32 -6
- data/Rakefile +6 -1
- data/celluloid-eventsource.gemspec +8 -7
- data/lib/celluloid/eventsource.rb +116 -50
- data/lib/celluloid/eventsource/response_parser.rb +14 -2
- data/lib/celluloid/eventsource/version.rb +1 -1
- data/log/.gitignore +1 -0
- data/spec/celluloid/eventsource/response_parser_spec.rb +17 -6
- data/spec/celluloid/eventsource_spec.rb +95 -8
- data/spec/spec_helper.rb +88 -0
- metadata +37 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3dbbbc109063bca1fc5bb81a4034ca39438f48f
|
4
|
+
data.tar.gz: ac911968885e272b5b6e1e3d669e526196a304e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12975ec688913ea7370198f848b910bfc2f5290c370714cf39303122c6ee1a7fbb9095a689306273be2f56098d20a05a3423531a6cc5fb02c56437de30e8b67a
|
7
|
+
data.tar.gz: 067be06d38ce78d5c95e8105031363cbfa206d3efec7f72aeab245de75baaaa8a6fe41b9dec915501b567af63f9d28638d58806552ee0631dedc6c0c4561c3d5
|
data/.gitignore
CHANGED
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Celluloid::Eventsource
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/celluloid-eventsource)
|
4
|
+
[](https://codeclimate.com/github/Tonkpils/celluloid-eventsource)
|
5
|
+
[](https://travis-ci.org/Tonkpils/celluloid-eventsource)
|
6
|
+
|
3
7
|
#### Under Development!! Use at your own risk :)
|
4
8
|
|
5
9
|
An EventSource client based off Celluloid::IO.
|
@@ -38,20 +42,42 @@ These can be assigned at initialize time
|
|
38
42
|
|
39
43
|
```ruby
|
40
44
|
es = Celluloid::EventSource.new("http://example.com/") do |conn|
|
41
|
-
conn.
|
42
|
-
puts "
|
45
|
+
conn.on_open do
|
46
|
+
puts "Connection was made"
|
47
|
+
end
|
48
|
+
|
49
|
+
conn.on_message do |event|
|
50
|
+
puts "Message: #{event.data}"
|
51
|
+
end
|
52
|
+
|
53
|
+
conn.on_error do |message|
|
54
|
+
puts "Error message #{message}"
|
55
|
+
end
|
56
|
+
|
57
|
+
conn.on(:time) do |event|
|
58
|
+
puts "The time is #{event.data}"
|
43
59
|
end
|
44
60
|
end
|
45
61
|
```
|
46
62
|
|
47
|
-
|
63
|
+
To close the connection `#close` will shut the socket connection but keep the actor alive.
|
64
|
+
|
65
|
+
### Event Handlers
|
66
|
+
|
67
|
+
Event handlers should be added when initializing the eventsource.
|
68
|
+
|
69
|
+
**Warning**
|
70
|
+
To change event handlers after initializing there is a [Gotcha](https://github.com/celluloid/celluloid/wiki/Gotchas).
|
71
|
+
Celluloid sends messages to actors through thread-safe proxies.
|
72
|
+
|
73
|
+
To get around this, use `wrapped_object` to set the handler on the actor but be aware of the concequences.
|
48
74
|
|
49
75
|
```ruby
|
50
|
-
es.
|
51
|
-
puts "Message: #{message}"
|
52
|
-
end
|
76
|
+
es.wrapped_object.on_messsage { |message| puts "Different #{message}" }
|
53
77
|
```
|
54
78
|
|
79
|
+
This same concept applies for changing the `url` of the eventsource.
|
80
|
+
|
55
81
|
## Contributing
|
56
82
|
|
57
83
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -18,12 +18,13 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency 'celluloid-io', '~> 0.15'
|
22
|
-
spec.add_dependency 'http_parser.rb', '~> 0.
|
21
|
+
spec.add_dependency 'celluloid-io', '~> 0.15.0'
|
22
|
+
spec.add_dependency 'http_parser.rb', '~> 0.6.0'
|
23
23
|
|
24
|
-
spec.add_development_dependency "
|
25
|
-
spec.add_development_dependency "
|
26
|
-
spec.add_development_dependency "
|
27
|
-
spec.add_development_dependency "
|
28
|
-
spec.add_development_dependency "
|
24
|
+
spec.add_development_dependency "timers"
|
25
|
+
spec.add_development_dependency "reel", '>= 0.5.0'
|
26
|
+
spec.add_development_dependency "rspec", '~> 3.0.0'
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.6.0"
|
28
|
+
spec.add_development_dependency "rake", '~> 10.1.0'
|
29
|
+
spec.add_development_dependency "pry", '~> 0.9.0'
|
29
30
|
end
|
@@ -1,54 +1,99 @@
|
|
1
1
|
require "celluloid/eventsource/version"
|
2
2
|
require 'celluloid/io'
|
3
3
|
require 'celluloid/eventsource/response_parser'
|
4
|
+
require 'uri'
|
4
5
|
|
5
6
|
module Celluloid
|
6
7
|
class EventSource
|
7
|
-
include
|
8
|
+
include Celluloid::IO
|
8
9
|
|
9
10
|
attr_reader :url, :with_credentials
|
11
|
+
attr_reader :ready_state
|
10
12
|
|
11
13
|
CONNECTING = 0
|
12
14
|
OPEN = 1
|
13
15
|
CLOSED = 2
|
14
16
|
|
15
|
-
|
17
|
+
execute_block_on_receiver :initialize
|
16
18
|
|
17
|
-
def initialize(
|
18
|
-
|
19
|
-
|
19
|
+
def initialize(uri, options = {})
|
20
|
+
self.url = uri
|
21
|
+
options = options.dup
|
20
22
|
@ready_state = CONNECTING
|
21
23
|
@with_credentials = options.delete(:with_credentials) { false }
|
24
|
+
@headers = default_request_headers.merge(options.fetch(:headers, {}))
|
22
25
|
|
23
|
-
@
|
24
|
-
@
|
26
|
+
@event_type_buffer = ""
|
27
|
+
@last_event_id_buffer = ""
|
28
|
+
@data_buffer = ""
|
25
29
|
|
26
|
-
@
|
27
|
-
@on_error = ->(message) {}
|
28
|
-
@on_message = ->(message) {}
|
30
|
+
@last_event_id = String.new
|
29
31
|
|
30
|
-
@
|
32
|
+
@reconnect_timeout = 10
|
33
|
+
@on = { open: ->{}, message: ->(_) {}, error: ->(_) {} }
|
31
34
|
@parser = ResponseParser.new
|
32
35
|
|
33
36
|
yield self if block_given?
|
34
37
|
|
35
|
-
async.
|
38
|
+
async.listen
|
39
|
+
end
|
40
|
+
|
41
|
+
def url=(uri)
|
42
|
+
@url = URI(uri)
|
36
43
|
end
|
37
44
|
|
38
45
|
def connected?
|
39
|
-
|
46
|
+
ready_state == OPEN
|
40
47
|
end
|
41
48
|
|
42
|
-
def
|
49
|
+
def closed?
|
50
|
+
ready_state == CLOSED
|
51
|
+
end
|
52
|
+
|
53
|
+
def listen
|
43
54
|
establish_connection
|
44
55
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
process_stream
|
57
|
+
rescue IOError
|
58
|
+
# Closing the socket during read causes this exception and kills the actor
|
59
|
+
# We really don't wan to do anything if the socket is closed.
|
60
|
+
end
|
61
|
+
|
62
|
+
def close
|
63
|
+
@socket.close if @socket
|
64
|
+
@ready_state = CLOSED
|
65
|
+
end
|
66
|
+
|
67
|
+
def on(event_name, &action)
|
68
|
+
@on[event_name.to_sym] = action
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_open(&action)
|
72
|
+
@on[:open] = action
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_message(&action)
|
76
|
+
@on[:message] = action
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_error(&action)
|
80
|
+
@on[:error] = action
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def ssl?
|
86
|
+
url.scheme == 'https'
|
49
87
|
end
|
50
88
|
|
51
89
|
def establish_connection
|
90
|
+
@socket = Celluloid::IO::TCPSocket.new(@url.host, @url.port)
|
91
|
+
|
92
|
+
if ssl?
|
93
|
+
@socket = Celluloid::IO::SSLSocket.new(@socket)
|
94
|
+
@socket.connect
|
95
|
+
end
|
96
|
+
|
52
97
|
@socket.write(request_string)
|
53
98
|
|
54
99
|
until @parser.headers?
|
@@ -57,65 +102,86 @@ module Celluloid
|
|
57
102
|
|
58
103
|
if @parser.status_code != 200
|
59
104
|
close
|
60
|
-
@
|
105
|
+
@on[:error].call("Unable to establish connection. Response status #{@parser.status_code}")
|
61
106
|
end
|
62
107
|
|
63
108
|
handle_headers(@parser.headers)
|
64
109
|
end
|
65
110
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
111
|
+
def default_request_headers
|
112
|
+
{
|
113
|
+
'Accept' => 'text/event-stream',
|
114
|
+
'Cache-Control' => 'no-cache',
|
115
|
+
'Host' => url.host
|
116
|
+
}
|
69
117
|
end
|
70
118
|
|
71
|
-
def
|
72
|
-
|
119
|
+
def process_field(line)
|
120
|
+
case line
|
121
|
+
when /^data:(.+)$/
|
122
|
+
@data_buffer << $1.lstrip.concat("\n")
|
123
|
+
when /^id:(.+)$/
|
124
|
+
@last_event_id_buffer = $1.lstrip
|
125
|
+
when /^retry:(\d+)$/
|
126
|
+
@reconnect_timeout = $1.to_i
|
127
|
+
when /^event:(.+)$/
|
128
|
+
@event_type_buffer = $1.lstrip
|
129
|
+
end
|
73
130
|
end
|
74
131
|
|
75
|
-
|
76
|
-
@on_message = block
|
77
|
-
end
|
132
|
+
MessageEvent = Struct.new(:type, :data, :last_event_id)
|
78
133
|
|
79
|
-
def
|
80
|
-
@
|
134
|
+
def process_event
|
135
|
+
@last_event_id = @last_event_id_buffer
|
136
|
+
|
137
|
+
unless @data_buffer.empty?
|
138
|
+
@data_buffer = @data_buffer.chomp("\n") if @data_buffer.end_with?("\n")
|
139
|
+
event = MessageEvent.new(:message, @data_buffer, @last_event_id)
|
140
|
+
event.type = @event_type_buffer.to_sym unless @event_type_buffer.empty?
|
141
|
+
|
142
|
+
dispatch_event(event)
|
143
|
+
end
|
144
|
+
|
145
|
+
clear_buffers!
|
81
146
|
end
|
82
147
|
|
83
|
-
|
148
|
+
def clear_buffers!
|
149
|
+
@data_buffer = ""
|
150
|
+
@event_type_buffer = ""
|
151
|
+
end
|
84
152
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
stream.split("\n").each do |part|
|
89
|
-
case part
|
90
|
-
when /^data:(.+)$/
|
91
|
-
data = $1
|
92
|
-
when /^id:(.+)$/
|
93
|
-
@last_event_id = $1
|
94
|
-
when /^retry:(.+)$/
|
95
|
-
@reconnect_timeout = $1.to_i
|
96
|
-
when /^event:(.+)$/
|
97
|
-
# TODO
|
98
|
-
end
|
153
|
+
def dispatch_event(event)
|
154
|
+
unless closed?
|
155
|
+
@on[event.type] && @on[event.type].call(event)
|
99
156
|
end
|
157
|
+
end
|
100
158
|
|
101
|
-
|
102
|
-
|
159
|
+
def process_stream
|
160
|
+
until closed? || @socket.eof?
|
161
|
+
line = @socket.readline
|
103
162
|
|
104
|
-
|
163
|
+
if line.strip.empty?
|
164
|
+
process_event
|
165
|
+
else
|
166
|
+
process_field(line)
|
167
|
+
end
|
168
|
+
end
|
105
169
|
end
|
106
170
|
|
107
171
|
def handle_headers(headers)
|
108
172
|
if headers['Content-Type'].include?("text/event-stream")
|
109
173
|
@ready_state = OPEN
|
110
|
-
@
|
174
|
+
@on[:open].call
|
111
175
|
else
|
112
176
|
close
|
113
|
-
@
|
177
|
+
@on[:error].call("Invalid Content-Type #{headers['Content-Type']}. Expected text/event-stream")
|
114
178
|
end
|
115
179
|
end
|
116
180
|
|
117
181
|
def request_string
|
118
|
-
"
|
182
|
+
headers = @headers.map { |k, v| "#{k}: #{v}" }
|
183
|
+
|
184
|
+
["GET #{url.request_uri} HTTP/1.1", headers].flatten.join("\r\n").concat("\r\n\r\n")
|
119
185
|
end
|
120
186
|
|
121
187
|
end
|
@@ -26,7 +26,7 @@ module Celluloid
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def on_headers_complete(headers)
|
29
|
-
@headers = headers
|
29
|
+
@headers = canonical_headers(headers)
|
30
30
|
end
|
31
31
|
|
32
32
|
def on_body(chunk)
|
@@ -42,8 +42,20 @@ module Celluloid
|
|
42
42
|
chunk.to_s
|
43
43
|
end
|
44
44
|
|
45
|
+
private
|
46
|
+
|
47
|
+
def canonical_headers(headers)
|
48
|
+
headers.each_with_object({}) do |(key, value), canonicalized_headers|
|
49
|
+
name = canonicalize_header(key)
|
50
|
+
canonicalized_headers[name] = value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def canonicalize_header(name)
|
55
|
+
name.gsub('_', '-').split("-").map(&:capitalize).join("-")
|
56
|
+
end
|
45
57
|
end
|
46
58
|
|
47
59
|
end
|
48
60
|
|
49
|
-
end
|
61
|
+
end
|
data/log/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
@@ -1,14 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Celluloid::EventSource::ResponseParser do
|
3
|
+
RSpec.describe Celluloid::EventSource::ResponseParser do
|
4
4
|
|
5
5
|
let(:success_headers) {<<-eos
|
6
6
|
HTTP/1.1 200 OK
|
7
7
|
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
|
8
8
|
Content-Type: text/html; charset=UTF-8
|
9
9
|
Content-Length: 131
|
10
|
-
|
11
|
-
|
10
|
+
X_CASE_HEADER: foo
|
11
|
+
X_Mixed-Case: bar
|
12
|
+
eos
|
13
|
+
}
|
12
14
|
|
13
15
|
let(:error_headers) {<<-eos
|
14
16
|
HTTP/1.1 400 OK
|
@@ -36,7 +38,7 @@ eos
|
|
36
38
|
end
|
37
39
|
|
38
40
|
expect(parser.status).to eq(200)
|
39
|
-
expect(parser.headers?).to
|
41
|
+
expect(parser.headers?).to be_truthy
|
40
42
|
expect(parser.headers['Content-Type']).to eq('text/html; charset=UTF-8')
|
41
43
|
expect(parser.headers['Content-Length']).to eq("131")
|
42
44
|
end
|
@@ -47,7 +49,16 @@ eos
|
|
47
49
|
end
|
48
50
|
|
49
51
|
expect(parser.status).to eq(200)
|
50
|
-
expect(parser.headers?).to
|
52
|
+
expect(parser.headers?).to be_falsey
|
51
53
|
expect(parser.headers).to be_nil
|
52
54
|
end
|
53
|
-
|
55
|
+
|
56
|
+
it 'makes response headers canonicalized' do
|
57
|
+
streamed(response_string) { |line| parser << line }
|
58
|
+
expected_headers = {
|
59
|
+
'X-Mixed-Case' => 'bar', 'Content-Type' => 'text/html; charset=UTF-8', 'Content-Length' => "131", 'X-Case-Header' => 'foo'
|
60
|
+
}
|
61
|
+
expect(parser.headers).to include(expected_headers)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -1,13 +1,100 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
TIMEOUT = 0.0001
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
Celluloid::IO::TCPSocket.stub(:new).with("example.com", 80)
|
8
|
-
Celluloid::EventSource.any_instance.should_receive(:async).and_return(ces)
|
9
|
-
ces.should_receive(:run)
|
5
|
+
RSpec.describe Celluloid::EventSource do
|
6
|
+
let(:data) { "foo bar " }
|
10
7
|
|
11
|
-
|
8
|
+
def with_sse_server
|
9
|
+
server = ServerSentEvents.new
|
10
|
+
yield server
|
11
|
+
ensure
|
12
|
+
server.terminate if server && server.alive?
|
12
13
|
end
|
13
|
-
|
14
|
+
|
15
|
+
describe '#initialize' do
|
16
|
+
let(:url) { "example.com" }
|
17
|
+
|
18
|
+
it 'runs asynchronously' do
|
19
|
+
ces = double(Celluloid::EventSource)
|
20
|
+
expect_any_instance_of(Celluloid::EventSource).to receive_message_chain(:async, :listen).and_return(ces)
|
21
|
+
|
22
|
+
Celluloid::EventSource.new("http://#{url}")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'allows customizing headers' do
|
26
|
+
auth_header = { "Authorization" => "Basic aGVsbG86dzBybGQh" }
|
27
|
+
|
28
|
+
allow_any_instance_of(Celluloid::EventSource).to receive_message_chain(:async, :listen)
|
29
|
+
es = Celluloid::EventSource.new("http://#{url}", :headers => auth_header)
|
30
|
+
|
31
|
+
headers = es.instance_variable_get('@headers')
|
32
|
+
expect(headers['Authorization']).to eq(auth_header["Authorization"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "keeps track of last event id" do
|
37
|
+
with_sse_server do |server|
|
38
|
+
@last_event_id = ""
|
39
|
+
ces = Celluloid::EventSource.new("http://localhost:63310") do |conn|
|
40
|
+
conn.on_message { |event| @last_event_id = event.last_event_id }
|
41
|
+
end
|
42
|
+
|
43
|
+
sleep TIMEOUT until ces.connected?
|
44
|
+
|
45
|
+
expect { server.broadcast(nil, data); sleep TIMEOUT }.to change { @last_event_id }.to("1")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "ignores comment ':' lines" do
|
50
|
+
with_sse_server do |server|
|
51
|
+
expect { |event|
|
52
|
+
ces = Celluloid::EventSource.new("http://localhost:63310") do |conn|
|
53
|
+
conn.on_message(&event)
|
54
|
+
end
|
55
|
+
|
56
|
+
sleep TIMEOUT until ces.connected?
|
57
|
+
|
58
|
+
server.send_ping
|
59
|
+
|
60
|
+
sleep TIMEOUT
|
61
|
+
}.to_not yield_control
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'receives data through message event' do
|
66
|
+
with_sse_server do |server|
|
67
|
+
expect { |event|
|
68
|
+
ces = Celluloid::EventSource.new("http://localhost:63310") do |conn|
|
69
|
+
conn.on_message(&event)
|
70
|
+
end
|
71
|
+
|
72
|
+
sleep TIMEOUT until ces.connected?
|
73
|
+
|
74
|
+
server.broadcast(nil, data)
|
75
|
+
|
76
|
+
sleep TIMEOUT
|
77
|
+
}.to yield_with_args(Celluloid::EventSource::MessageEvent)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
it 'receives custom events through event handlers' do
|
83
|
+
with_sse_server do |server|
|
84
|
+
event_name = :custom_event
|
85
|
+
|
86
|
+
expect { |event|
|
87
|
+
ces = Celluloid::EventSource.new("http://localhost:63310") do |conn|
|
88
|
+
conn.on(event_name, &event)
|
89
|
+
end
|
90
|
+
|
91
|
+
sleep TIMEOUT until ces.connected?
|
92
|
+
|
93
|
+
server.broadcast(event_name, data)
|
94
|
+
|
95
|
+
sleep TIMEOUT
|
96
|
+
}.to yield_with_args(Celluloid::EventSource::MessageEvent)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,3 +3,91 @@ require 'bundler/setup'
|
|
3
3
|
|
4
4
|
require 'celluloid/eventsource'
|
5
5
|
require 'celluloid/rspec'
|
6
|
+
|
7
|
+
require 'reel'
|
8
|
+
|
9
|
+
logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a')
|
10
|
+
logfile.sync = true
|
11
|
+
|
12
|
+
Celluloid.logger = Logger.new(logfile)
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.expose_dsl_globally = false
|
16
|
+
end
|
17
|
+
|
18
|
+
class ServerSentEvents < Reel::Server::HTTP
|
19
|
+
include Celluloid::Logger
|
20
|
+
|
21
|
+
attr_reader :last_event_id, :connections
|
22
|
+
|
23
|
+
def initialize(ip = '127.0.0.1', port = 63310)
|
24
|
+
@connections = []
|
25
|
+
@history = []
|
26
|
+
@last_event_id = 0
|
27
|
+
super(ip, port, &method(:on_connection))
|
28
|
+
end
|
29
|
+
|
30
|
+
def broadcast(event, data)
|
31
|
+
if @history.size >= 10
|
32
|
+
@history.slice!(0, @history.size - 1000)
|
33
|
+
end
|
34
|
+
|
35
|
+
@last_event_id += 1
|
36
|
+
@history << { id: @last_event_id, event: event, data: data }
|
37
|
+
|
38
|
+
@connections.each do |socket|
|
39
|
+
async.send_sse(socket, data, event, @last_event_id)
|
40
|
+
end
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def send_ping
|
45
|
+
@connections.each do |socket|
|
46
|
+
begin
|
47
|
+
socket << ":\n"
|
48
|
+
rescue Reel::SocketError
|
49
|
+
@connections.delete(socket)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
# event and id are optional, Eventsource only needs data
|
56
|
+
def send_sse(socket, data, event = nil, id = nil)
|
57
|
+
begin
|
58
|
+
socket.id id if id
|
59
|
+
socket.event event if event
|
60
|
+
socket.data data
|
61
|
+
rescue Reel::SocketError, NoMethodError
|
62
|
+
@connections.delete(socket) if @connections.include?(socket)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_request(request)
|
67
|
+
event_stream = Reel::EventStream.new do |socket|
|
68
|
+
@connections << socket
|
69
|
+
socket.retry 5000
|
70
|
+
# after a Connection reset resend newer Messages to the Client, query['last_event_id'] is needed for https://github.com/Yaffle/EventSource
|
71
|
+
if @history.count > 0 && id = request.headers['Last-Event-ID']
|
72
|
+
begin
|
73
|
+
if history = @history.select {|h| h[:id] >= Integer(id)}.map {|a| "id: \nevent: %s%s\ndata: %s" % [a[:id], a[:event], a[:data]]}.join("\n\n")
|
74
|
+
socket << "%s\n\n" % [history]
|
75
|
+
end
|
76
|
+
rescue ArgumentError, Reel::SocketError
|
77
|
+
@connections.delete(socket)
|
78
|
+
request.close
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
request.respond Reel::StreamResponse.new(:ok, {
|
84
|
+
'Content-Type' => 'text/event-stream; charset=utf-8',
|
85
|
+
'Cache-Control' => 'no-cache'}, event_stream)
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_connection(connection)
|
89
|
+
connection.each_request do |request|
|
90
|
+
handle_request(request)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid-eventsource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leo Correa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: celluloid-io
|
@@ -16,98 +16,112 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.15.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.15.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: http_parser.rb
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.6.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.6.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: timers
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: reel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.5.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.5.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ~>
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 3.0.0
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - ~>
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: 3.0.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - ~>
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
89
|
+
version: 1.6.0
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - ~>
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: 1.6.0
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rake
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - ~>
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: 10.1.0
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - ~>
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: 10.1.0
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: pry
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - ~>
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
117
|
+
version: 0.9.0
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - ~>
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
124
|
+
version: 0.9.0
|
111
125
|
description: Celluloid::IO based library to consume Server-Sent Events.
|
112
126
|
email:
|
113
127
|
- lcorr005@gmail.com
|
@@ -117,6 +131,7 @@ extra_rdoc_files: []
|
|
117
131
|
files:
|
118
132
|
- .gitignore
|
119
133
|
- .rspec
|
134
|
+
- .travis.yml
|
120
135
|
- Gemfile
|
121
136
|
- LICENSE.md
|
122
137
|
- README.md
|
@@ -125,6 +140,7 @@ files:
|
|
125
140
|
- lib/celluloid/eventsource.rb
|
126
141
|
- lib/celluloid/eventsource/response_parser.rb
|
127
142
|
- lib/celluloid/eventsource/version.rb
|
143
|
+
- log/.gitignore
|
128
144
|
- spec/celluloid/eventsource/response_parser_spec.rb
|
129
145
|
- spec/celluloid/eventsource_spec.rb
|
130
146
|
- spec/spec_helper.rb
|
@@ -148,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
164
|
version: '0'
|
149
165
|
requirements: []
|
150
166
|
rubyforge_project:
|
151
|
-
rubygems_version: 2.
|
167
|
+
rubygems_version: 2.2.2
|
152
168
|
signing_key:
|
153
169
|
specification_version: 4
|
154
170
|
summary: celluloid-eventsource is a gem to consume SSE streaming API.
|
@@ -156,4 +172,3 @@ test_files:
|
|
156
172
|
- spec/celluloid/eventsource/response_parser_spec.rb
|
157
173
|
- spec/celluloid/eventsource_spec.rb
|
158
174
|
- spec/spec_helper.rb
|
159
|
-
has_rdoc:
|