servent 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: afcf04d7de0fbed51a568743c7dad4af879042d5
4
- data.tar.gz: 95b1d000e7171f9df857952d6e4698b6073aca09
3
+ metadata.gz: 52b302c6ed718cbeaf7717c15db7028f171d12a5
4
+ data.tar.gz: 5dd81fbbcbd73b0e72b02c6e666feae00f6c3f09
5
5
  SHA512:
6
- metadata.gz: ca999d77e3c6f6236091167bee4e8f815c05af640ff16a990eee5d237187f31f33207811d72bae5b26c68e38e70d63b734fb3bf6386cd68a916e4e756cf90e2a
7
- data.tar.gz: 1d0b871339cb5f9f281de6522a5bf699f7f2489d33b2feb968c94db512de8b3ca43c45b08326c1c0f079921da7f63a6dfd2f821abe9831ad95e27c8cc09e8f36
6
+ metadata.gz: 0dfae2233984931fd793d1229a3a5cd39a3d41f58b81721822f6f3a4b8f49a8e3a8b29284639b0bca29cbfc2394cd82a7ae00b1cebb303345fe33ab10b584b01
7
+ data.tar.gz: 44d2e1e155d868e4e1af43db8b143fbd8e8ba7d6a585768e5597e32531a987318c0c8044f706a73148b84c9969175b2c2ab0988f3231362795d7a7a1d993d54b
@@ -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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- servent (0.0.1)
4
+ servent (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Servent
2
2
 
3
- [<img src="https://travis-ci.com/mistersourcerer/servent.svg?token=aMwiRm3UQ11zdWwMxGgZ&branch=master" />](https://travis-ci.com/mistersourcerer/servent)
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.body}"
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
- %(event: #{@type}
12
- id: #{@id}
13
- data: {
14
- data: "id": "#{@id}",
15
- data: "type": "#{@type}",
16
- data: "text": "#{@text}"
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 "/omg" do |_, res|
37
- r, w = IO.pipe
38
- clients << w
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
- res.content_type = "text/event-stream"
41
- res.body = r
42
- res.chunked = true
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 "/broadcast" do |_, _|
44
+ server.mount_proc "/enough" do |_, _|
46
45
  clients.each do |client|
47
- Thread.new do
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"
@@ -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
@@ -4,7 +4,9 @@ require "net/http"
4
4
 
5
5
  module Servent
6
6
  class EventSource
7
- attr_reader :ready_state
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
- headers.each { |header, value| get[header] = value }
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
- # FIXME: response CAN have more than one mime type
56
- unless response["Content-Type"] == "text/event-stream"
57
- @ready_state = Servent::CLOSED
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
- handle_response response
68
+ open_connection response
63
69
  end
64
70
  end
65
71
 
66
- def handle_response(response)
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
- @message_blocks.each { |block| block.call chunk }
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
@@ -1,3 +1,3 @@
1
1
  module Servent
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.1.0".freeze
3
3
  end
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.1
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-16 00:00:00.000000000 Z
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
- - hypotesis/consumer.rb
119
- - hypotesis/emitter.ru
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
@@ -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"