ld-em-eventsource 0.2.2

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.
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: