ld-em-eventsource 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/README.md +117 -0
  4. data/lib/ld-em-eventsource.rb +200 -0
  5. metadata +135 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e70e7f5b596b87a63a40c4015f57a19d0a45dd74
4
+ data.tar.gz: ab883c4378308c756df9df58c25d2db3ce1f9f3b
5
+ SHA512:
6
+ metadata.gz: 9eaff22a08d79c06719e432808a67cc9b81e3d502ecc6e301f33e25e84fff2b5defbcccbe7855d8a8d9ac8a459029a46901a89b5569e3e1bfa62f7aaa87f1e1c
7
+ data.tar.gz: 3675e0816e4d2658d89cf35ea1f754b16839d1015f8e15c5e8699dc50a11d9e096c1b8bffe56415bf5e06b06492e7ea7dfb3fd0e85d218280e8c13ce02daae7e
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # EventSource client for EventMachine
2
+
3
+ See the specification: http://dev.w3.org/html5/eventsource/
4
+
5
+ ## Install
6
+
7
+ Install with Rubygems:
8
+
9
+ gem install ld-em-eventsource
10
+
11
+ If you use bundler, add it to your Gemfile:
12
+
13
+ gem "ld-em-eventsource", "~> 0.2.2"
14
+
15
+ ## Usage
16
+
17
+ Basic usage:
18
+
19
+ ```ruby
20
+ require "ld-em-eventsource"
21
+ EM.run do
22
+ source = EventMachine::EventSource.new("http://example.com/streaming")
23
+ source.message do |message|
24
+ puts "new message #{message}"
25
+ end
26
+ source.start # Start listening
27
+ end
28
+ ```
29
+
30
+ Listening specific event name:
31
+
32
+ ```ruby
33
+ source.on "eventname" do |message|
34
+ puts "eventname #{message}"
35
+ end
36
+ ```
37
+
38
+ Handle error:
39
+
40
+ ```ruby
41
+ source.error do |error|
42
+ puts "error #{error}"
43
+ end
44
+ ```
45
+
46
+ Handle open stream:
47
+
48
+ ```ruby
49
+ source.open do
50
+ puts "opened"
51
+ end
52
+ ```
53
+
54
+ Close the stream:
55
+
56
+ ```ruby
57
+ source.close
58
+ ```
59
+
60
+ Current status of the connection:
61
+
62
+ ```ruby
63
+ # Can be:
64
+ # - EM::EventSource::CLOSED
65
+ # - EM::EventSource::CONNECTING
66
+ # - EM::EventSource::OPEN
67
+ source.ready_state
68
+ ```
69
+
70
+ Override the default retry value (if the connection is lost):
71
+
72
+ ```ruby
73
+ source.retry = 5 # in seconds (default 3)
74
+ ```
75
+
76
+ Get Last-Event-Id value:
77
+
78
+ ```ruby
79
+ source.last_event_id
80
+ ```
81
+
82
+ Attach middleware:
83
+
84
+ ```ruby
85
+ source.use EM::Middleware::JSONResponse
86
+ ```
87
+
88
+ Set the inactivity timeout. Set to 0 to disable the timeout.
89
+
90
+ ```ruby
91
+ source.inactivity_timeout = 120 # in seconds (default: 60).
92
+ ```
93
+
94
+ ## Licence
95
+
96
+ MIT License
97
+
98
+ Copyright (C) 2012 by François de Metz
99
+ Copyright (C) 2011 by af83
100
+
101
+ Permission is hereby granted, free of charge, to any person obtaining a copy
102
+ of this software and associated documentation files (the "Software"), to deal
103
+ in the Software without restriction, including without limitation the rights
104
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
105
+ copies of the Software, and to permit persons to whom the Software is
106
+ furnished to do so, subject to the following conditions:
107
+
108
+ The above copyright notice and this permission notice shall be included in
109
+ all copies or substantial portions of the Software.
110
+
111
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
112
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
113
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
114
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
115
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
116
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
117
+ THE SOFTWARE.
@@ -0,0 +1,200 @@
1
+ # encoding: utf-8
2
+
3
+ require "eventmachine"
4
+ require "em-http-request"
5
+
6
+ module EventMachine
7
+ # EventSource
8
+ # dev.w3.org/html5/eventsource/
9
+ class EventSource
10
+ # Get API url
11
+ attr_reader :url
12
+ # Get ready state
13
+ attr_reader :ready_state
14
+ # Get current retry value (in seconds)
15
+ attr_reader :retry
16
+ # Override retry value (in seconds)
17
+ attr_writer :retry
18
+ # Get value of last event id
19
+ attr_reader :last_event_id
20
+ # Get the inactivity timeout
21
+ attr_reader :inactivity_timeout
22
+ # Set the inactivity timeout
23
+ attr_writer :inactivity_timeout
24
+ # Ready state
25
+ # The connection has not yet been established, or it was closed and the user agent is reconnecting.
26
+ CONNECTING = 0
27
+ # The user agent has an open connection and is dispatching events as it receives them.
28
+ OPEN = 1
29
+ # The connection is not open, and the user agent is not trying to reconnect. Either there was a fatal error or the close() method was invoked.
30
+ CLOSED = 2
31
+
32
+ # Create a new stream
33
+ #
34
+ # url - the url as string
35
+ # query - the query string as hash
36
+ # headers - the headers for the request as hash
37
+ def initialize(url, query={}, headers={})
38
+ @url = url
39
+ @query = query
40
+ @headers = headers
41
+ @ready_state = CLOSED
42
+
43
+ @last_event_id = nil
44
+ @retry = 3 # seconds
45
+ @inactivity_timeout = 60 # seconds
46
+
47
+ @opens = []
48
+ @errors = []
49
+ @messages = []
50
+ @on = {}
51
+ @middlewares = []
52
+ end
53
+
54
+ # Add open event handler
55
+ #
56
+ # Returns nothing
57
+ def open(&block)
58
+ @opens << block
59
+ end
60
+
61
+ # Add a specific event handler
62
+ #
63
+ # name - name of event
64
+ #
65
+ # Returns nothing
66
+ def on(name, &block)
67
+ @on[name] ||= []
68
+ @on[name] << block
69
+ end
70
+
71
+ # Add message event handler
72
+ #
73
+ # Returns nothing
74
+ def message(&block)
75
+ @messages << block
76
+ end
77
+
78
+ # Add error event handler
79
+ #
80
+ # Returns nothing
81
+ def error(&block)
82
+ @errors << block
83
+ end
84
+
85
+ # Add a middleware
86
+ #
87
+ # *args - the middleware class
88
+ #
89
+ # Returns nothing
90
+ def use(*args, &block)
91
+ @middlewares << (args << block)
92
+ end
93
+
94
+ # Start subscription
95
+ #
96
+ # Returns nothing
97
+ def start
98
+ @ready_state = CONNECTING
99
+ listen
100
+ end
101
+
102
+ # Cancel subscription
103
+ #
104
+ # Returns nothing
105
+ def close
106
+ @ready_state = CLOSED
107
+ @conn.close('requested') if @conn
108
+ end
109
+
110
+ # Gracefully reconnect
111
+ def reconnect
112
+ close
113
+ EM.add_timer(@retry) do
114
+ start
115
+ end
116
+ end
117
+
118
+ protected
119
+
120
+ def listen
121
+ @conn, @req = prepare_request
122
+ @req.headers(&method(:handle_headers))
123
+ @req.errback(&method(:handle_reconnect))
124
+ @req.callback(&method(:handle_reconnect))
125
+ buffer = ""
126
+ @req.stream do |chunk|
127
+ buffer += chunk
128
+ while index = buffer.index(/\r\n\r\n|\n\n/)
129
+ stream = buffer.slice!(0..index)
130
+ handle_stream(stream)
131
+ end
132
+ end
133
+ end
134
+
135
+ def handle_reconnect(*args)
136
+ return if @ready_state == CLOSED
137
+ @ready_state = CONNECTING
138
+ @errors.each { |error| error.call("Connection lost. Reconnecting.") }
139
+ EM.add_timer(@retry) do
140
+ listen
141
+ end
142
+ end
143
+
144
+ def handle_headers(headers)
145
+ if headers.status != 200
146
+ reconnect
147
+ @errors.each { |error| error.call("Unexpected response status #{headers.status}") }
148
+ return
149
+ end
150
+ if /^text\/event-stream/.match headers['CONTENT_TYPE']
151
+ @ready_state = OPEN
152
+ @opens.each { |open| open.call }
153
+ else
154
+ close
155
+ @errors.each { |error| error.call("The content-type '#{headers['CONTENT_TYPE']}' is not text/event-stream") }
156
+ end
157
+ end
158
+
159
+ def handle_stream(stream)
160
+ data = ""
161
+ name = nil
162
+ stream.split(/\r?\n/).each do |part|
163
+ /^data:(.+)$/.match(part) do |m|
164
+ data += m[1].strip
165
+ data += "\n"
166
+ end
167
+ /^id:(.+)$/.match(part) do |m|
168
+ @last_event_id = m[1].strip
169
+ end
170
+ /^event:(.+)$/.match(part) do |m|
171
+ name = m[1].strip
172
+ end
173
+ /^retry:(.+)$/.match(part) do |m|
174
+ if m[1].strip! =~ /^[0-9]+$/
175
+ @retry = m[1].to_i
176
+ end
177
+ end
178
+ end
179
+ return if data.empty?
180
+ data.chomp!
181
+ if name.nil?
182
+ @messages.each { |message| message.call(data) }
183
+ else
184
+ @on[name].each { |message| message.call(data) } if not @on[name].nil?
185
+ end
186
+ end
187
+
188
+ def prepare_request
189
+ conn = EM::HttpRequest.new(@url, :inactivity_timeout => @inactivity_timeout)
190
+ @middlewares.each { |middleware|
191
+ block = middleware.pop
192
+ conn.use(*middleware, &block)
193
+ }
194
+ headers = @headers.merge({'Cache-Control' => 'no-cache', 'Accept' => 'text/event-stream'})
195
+ headers.merge!({'Last-Event-Id' => @last_event_id }) if not @last_event_id.nil?
196
+ [conn, conn.get({ :query => @query,
197
+ :head => headers})]
198
+ end
199
+ end
200
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ld-em-eventsource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - François de Metz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: eventmachine
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: em-http-request
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-spec-context
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |2
98
+ ld-em-eventsource is an eventmachine library to consume Server-Sent Events streaming API.
99
+ You can find the specification here: http://dev.w3.org/html5/eventsource/
100
+ This library was forked from https://github.com/AF83/em-eventsource
101
+ email: francois@2metz.fr
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files:
105
+ - README.md
106
+ files:
107
+ - Gemfile
108
+ - README.md
109
+ - lib/ld-em-eventsource.rb
110
+ homepage: http://github.com/launchdarkly/em-eventsource
111
+ licenses: []
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.2.2
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: ld-em-eventsource is an eventmachine library to consume Server-Sent Events
133
+ streaming API.
134
+ test_files: []
135
+ has_rdoc: