celluloid-eventsource 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/celluloid-eventsource.png)](http://badge.fury.io/rb/celluloid-eventsource)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/Tonkpils/celluloid-eventsource.png)](https://codeclimate.com/github/Tonkpils/celluloid-eventsource)
|
5
|
+
[![Build Status](https://travis-ci.org/Tonkpils/celluloid-eventsource.svg?branch=master)](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:
|