servent 0.0.1 → 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/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +58 -9
- data/{hypotesis/emitter.ru → examples/config.ru} +16 -19
- data/examples/consumer.rb +17 -0
- data/lib/servent.rb +9 -0
- data/lib/servent/event_source.rb +54 -17
- data/lib/servent/version.rb +1 -1
- metadata +5 -4
- data/hypotesis/consumer.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52b302c6ed718cbeaf7717c15db7028f171d12a5
|
4
|
+
data.tar.gz: 5dd81fbbcbd73b0e72b02c6e666feae00f6c3f09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0dfae2233984931fd793d1229a3a5cd39a3d41f58b81721822f6f3a4b8f49a8e3a8b29284639b0bca29cbfc2394cd82a7ae00b1cebb303345fe33ab10b584b01
|
7
|
+
data.tar.gz: 44d2e1e155d868e4e1af43db8b143fbd8e8ba7d6a585768e5597e32531a987318c0c8044f706a73148b84c9969175b2c2ab0988f3231362795d7a7a1d993d54b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project
|
4
|
+
will be documented in this file.
|
5
|
+
|
6
|
+
The format is based
|
7
|
+
on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
8
|
+
and this project
|
9
|
+
adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
10
|
+
|
11
|
+
## Unreleased
|
12
|
+
|
13
|
+
## [0.1.0] - 2017-11-20
|
14
|
+
### Added
|
15
|
+
- Consumer for a server-sent events endpoint.
|
16
|
+
- First fully functional example.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Servent
|
2
2
|
|
3
|
-
[<img src="https://travis-ci.
|
3
|
+
[<img src="https://travis-ci.org/mistersourcerer/servent.svg?branch=master" />](https://travis-ci.org/mistersourcerer/servent)
|
4
4
|
|
5
5
|
Ruby _Server-Sent Events_ client.
|
6
6
|
A _EventSource_ Ruby implementation based on the [W3C specification](https://www.w3.org/TR/eventsource).
|
@@ -29,17 +29,10 @@ gem 'servent'
|
|
29
29
|
# id: 42
|
30
30
|
# data: Omg! Hello World.
|
31
31
|
|
32
|
-
events = Queue.new
|
33
|
-
|
34
32
|
event_source = Servent::EventSource.new("http://example.org/event-source")
|
35
33
|
event_source.on_message do |message|
|
36
|
-
events.push message
|
37
|
-
end
|
38
|
-
event_source.start
|
39
|
-
|
40
|
-
while (event = events.pop)
|
41
34
|
puts "Event type: #{event.type}"
|
42
|
-
puts "Event body: #{event.
|
35
|
+
puts "Event body: #{event.data}"
|
43
36
|
|
44
37
|
# Will print:
|
45
38
|
#
|
@@ -49,8 +42,64 @@ while (event = events.pop)
|
|
49
42
|
# ```
|
50
43
|
# And wait for the next event to arrive.
|
51
44
|
end
|
45
|
+
|
46
|
+
# join the internal event source thread
|
47
|
+
# so we can receive event until it terminates:
|
48
|
+
event_source.listen
|
52
49
|
```
|
53
50
|
|
51
|
+
## More examples
|
52
|
+
|
53
|
+
There is directory `examples` in this project
|
54
|
+
with a _WEBrick_ server
|
55
|
+
and also a `EventSource` consumer.
|
56
|
+
|
57
|
+
### How to run the example
|
58
|
+
|
59
|
+
#### TL;DR
|
60
|
+
|
61
|
+
# on one terminal:
|
62
|
+
$ rackup
|
63
|
+
|
64
|
+
# on a second one:
|
65
|
+
$ ruby consumer.rb
|
66
|
+
|
67
|
+
# on yeat another one
|
68
|
+
$ curl http://localhost:9292/broadcast
|
69
|
+
|
70
|
+
# and to make the consumer close itself:
|
71
|
+
$ curl http://localhost:9292/enough
|
72
|
+
|
73
|
+
#### More detailed version
|
74
|
+
|
75
|
+
if you are inside the directory
|
76
|
+
(or copied the files in the example dir to your own)
|
77
|
+
you can run a _rackup_:
|
78
|
+
|
79
|
+
$ rackup
|
80
|
+
|
81
|
+
The server will run on port _9292_
|
82
|
+
and it has 3 endpoints:
|
83
|
+
|
84
|
+
/
|
85
|
+
/broadcast
|
86
|
+
/enough
|
87
|
+
|
88
|
+
The root (`/`) is intended to consumers
|
89
|
+
and the one in the example
|
90
|
+
starts listening to that endpoint like this:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
event_source = Servent::EventSource.new("http://localhost:9292/")
|
94
|
+
# ...
|
95
|
+
event_source.listen
|
96
|
+
```
|
97
|
+
|
98
|
+
If you want to test multiple messages arriving
|
99
|
+
you can use the `repeat` parameters in the request:
|
100
|
+
|
101
|
+
$ curl http://localhost/broadcast?repeat=3
|
102
|
+
|
54
103
|
## Development
|
55
104
|
|
56
105
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -8,15 +8,12 @@ class SSEEvent
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def event
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
data:
|
15
|
-
|
16
|
-
|
17
|
-
data: }
|
18
|
-
|
19
|
-
)
|
11
|
+
<<~EVENT
|
12
|
+
event: #{@type}
|
13
|
+
id: #{@id}
|
14
|
+
data: #{@text}
|
15
|
+
|
16
|
+
EVENT
|
20
17
|
end
|
21
18
|
end
|
22
19
|
|
@@ -33,20 +30,20 @@ server.mount_proc "/" do |_, res|
|
|
33
30
|
res.chunked = true
|
34
31
|
end
|
35
32
|
|
36
|
-
server.mount_proc "/
|
37
|
-
|
38
|
-
|
33
|
+
server.mount_proc "/broadcast" do |req, _|
|
34
|
+
repeat = req.query["repeat"].to_i
|
35
|
+
repeat = 1 if repeat <= 0 || repeat.nil?
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
clients.each do |client|
|
38
|
+
repeat.times do |counter|
|
39
|
+
client << SSEEvent.new("streaming #{counter}!").event
|
40
|
+
end
|
41
|
+
end
|
43
42
|
end
|
44
43
|
|
45
|
-
server.mount_proc "/
|
44
|
+
server.mount_proc "/enough" do |_, _|
|
46
45
|
clients.each do |client|
|
47
|
-
|
48
|
-
client << SSEEvent.new("streaming!").event
|
49
|
-
end
|
46
|
+
client << SSEEvent.new("close").event
|
50
47
|
end
|
51
48
|
end
|
52
49
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "net/http"
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
3
|
+
require "servent"
|
4
|
+
require "pp"
|
5
|
+
|
6
|
+
event_source = Servent::EventSource.new("http://localhost:9292/")
|
7
|
+
event_source.on_message do |event|
|
8
|
+
if event.data == "close"
|
9
|
+
puts "going to close this client"
|
10
|
+
return event_source.close
|
11
|
+
end
|
12
|
+
|
13
|
+
puts "received: "+ event.data
|
14
|
+
end
|
15
|
+
event_source.listen
|
16
|
+
|
17
|
+
puts "bye"
|
data/lib/servent.rb
CHANGED
@@ -8,4 +8,13 @@ module Servent
|
|
8
8
|
CONNECTING = 0
|
9
9
|
OPEN = 1
|
10
10
|
CLOSED = 2
|
11
|
+
|
12
|
+
REDIRECT_STATUSES = [301, 302, 303, 307]
|
13
|
+
RECONNECTION_STATUSES = [500, 502, 503, 504]
|
14
|
+
AUTHORIZATION_STATUSES = [305, 401, 407]
|
15
|
+
|
16
|
+
KNOWN_STATUSES = [200] +
|
17
|
+
REDIRECT_STATUSES +
|
18
|
+
RECONNECTION_STATUSES +
|
19
|
+
AUTHORIZATION_STATUSES
|
11
20
|
end
|
data/lib/servent/event_source.rb
CHANGED
@@ -4,7 +4,9 @@ require "net/http"
|
|
4
4
|
|
5
5
|
module Servent
|
6
6
|
class EventSource
|
7
|
-
|
7
|
+
DEFAULT_HEADERS = { "Accept" => "text/event-stream" }
|
8
|
+
|
9
|
+
attr_reader :ready_state, :uri
|
8
10
|
|
9
11
|
def initialize(url, net_http_options: { read_timeout: 600 })
|
10
12
|
@uri = URI(url)
|
@@ -20,18 +22,29 @@ module Servent
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def start(http_starter = Net::HTTP)
|
25
|
+
@http_starter ||= http_starter
|
23
26
|
params = HTTPStartParams.new(@uri, @proxy_config, @net_http_options)
|
24
27
|
|
25
|
-
Thread.new {
|
26
|
-
http_starter.start(*params.parameterize) do |http|
|
28
|
+
@thread = Thread.new {
|
29
|
+
@http_starter.start(*params.parameterize) do |http|
|
27
30
|
get = Net::HTTP::Get.new @uri
|
28
|
-
|
31
|
+
DEFAULT_HEADERS.each { |header, value| get[header] = value }
|
29
32
|
yield http, get if block_given?
|
33
|
+
|
30
34
|
perform_request http, get
|
31
35
|
end
|
32
36
|
}
|
33
37
|
end
|
34
38
|
|
39
|
+
def listen(http_starter = Net::HTTP)
|
40
|
+
start(http_starter).join
|
41
|
+
end
|
42
|
+
|
43
|
+
def close
|
44
|
+
@ready_state = Servent::CLOSED
|
45
|
+
@thread.kill unless @thread.nil?
|
46
|
+
end
|
47
|
+
|
35
48
|
def on_open(&open_block)
|
36
49
|
@open_blocks << open_block
|
37
50
|
end
|
@@ -46,30 +59,54 @@ module Servent
|
|
46
59
|
|
47
60
|
private
|
48
61
|
|
49
|
-
def headers
|
50
|
-
{ "Accept" => "text/event-stream" }
|
51
|
-
end
|
52
|
-
|
53
62
|
def perform_request(http, type)
|
54
63
|
http.request type do |response|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@error_blocks.each { |block| block.call response, :wrong_mime_type }
|
59
|
-
return
|
60
|
-
end
|
64
|
+
return fail_connection response if should_fail? response
|
65
|
+
return schedule_reconnection if should_reconnect? response
|
66
|
+
store_new_parmanent_url response
|
61
67
|
|
62
|
-
|
68
|
+
open_connection response
|
63
69
|
end
|
64
70
|
end
|
65
71
|
|
66
|
-
def
|
72
|
+
def open_connection(response)
|
67
73
|
@ready_state = Servent::OPEN
|
68
74
|
@open_blocks.each { |block| block.call response }
|
69
75
|
response.read_body do |chunk|
|
70
|
-
|
76
|
+
# FIXME: use the same stream object to parse
|
77
|
+
# different chunks.
|
78
|
+
stream = Stream.new chunk
|
79
|
+
events = stream.parse
|
80
|
+
events.each do |event|
|
81
|
+
@message_blocks.each { |block| block.call event }
|
82
|
+
end
|
71
83
|
end
|
72
84
|
end
|
85
|
+
|
86
|
+
def should_fail?(response)
|
87
|
+
return false if Servent::REDIRECT_STATUSES.include?(response.code.to_i)
|
88
|
+
(response["Content-Type"] != "text/event-stream") ||
|
89
|
+
!Servent::KNOWN_STATUSES.include?(response.code.to_i)
|
90
|
+
end
|
91
|
+
|
92
|
+
def fail_connection(response)
|
93
|
+
@ready_state = Servent::CLOSED
|
94
|
+
@error_blocks.each { |block| block.call response, :wrong_mime_type }
|
95
|
+
end
|
96
|
+
|
97
|
+
def should_reconnect?(response)
|
98
|
+
Servent::RECONNECTION_STATUSES.include? response.code.to_i
|
99
|
+
end
|
100
|
+
|
101
|
+
def schedule_reconnection
|
102
|
+
start
|
103
|
+
end
|
104
|
+
|
105
|
+
def store_new_parmanent_url(response)
|
106
|
+
return unless response.code.to_i == 301
|
107
|
+
@original_uri = @uri
|
108
|
+
@uri = URI(response["Location"])
|
109
|
+
end
|
73
110
|
end
|
74
111
|
|
75
112
|
class ProxyConfig
|
data/lib/servent/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: servent
|
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
|
- Ricardo Valeriano
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-11-
|
11
|
+
date: 2017-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -107,6 +107,7 @@ files:
|
|
107
107
|
- ".rubocop.yml"
|
108
108
|
- ".ruby-version"
|
109
109
|
- ".travis.yml"
|
110
|
+
- CHANGELOG.md
|
110
111
|
- CODE_OF_CONDUCT.md
|
111
112
|
- Gemfile
|
112
113
|
- Gemfile.lock
|
@@ -115,8 +116,8 @@ files:
|
|
115
116
|
- Rakefile
|
116
117
|
- bin/console
|
117
118
|
- bin/setup
|
118
|
-
-
|
119
|
-
-
|
119
|
+
- examples/config.ru
|
120
|
+
- examples/consumer.rb
|
120
121
|
- lib/servent.rb
|
121
122
|
- lib/servent/event.rb
|
122
123
|
- lib/servent/event_source.rb
|
data/hypotesis/consumer.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require "net/http"
|
2
|
-
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
3
|
-
require "servent"
|
4
|
-
require "pp"
|
5
|
-
|
6
|
-
#q = Queue.new
|
7
|
-
#
|
8
|
-
#trap :INT do
|
9
|
-
# q << nil
|
10
|
-
#end
|
11
|
-
|
12
|
-
#Thread.new do
|
13
|
-
# uri = URI("http://localhost:9292/omg")
|
14
|
-
#
|
15
|
-
# Net::HTTP.start(uri.host, uri.port, read_timeout: 600) do |http|
|
16
|
-
# get = Net::HTTP::Get.new uri
|
17
|
-
# get["Accept"] = "text/event-stream"
|
18
|
-
# http.request(get) do |response|
|
19
|
-
# response.read_body do |chunk|
|
20
|
-
# q.push chunk
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
# q.push nil
|
24
|
-
# end
|
25
|
-
#end
|
26
|
-
|
27
|
-
event_source = Servent::EventSource.new("http://localhost:9292/omg")
|
28
|
-
event_source.on_message do |message|
|
29
|
-
#q.push message
|
30
|
-
pp message
|
31
|
-
end
|
32
|
-
event_source.start.join
|
33
|
-
|
34
|
-
#while (chunk = q.pop)
|
35
|
-
# puts chunk
|
36
|
-
#end
|
37
|
-
|
38
|
-
puts "bye"
|