reel 0.4.0.pre5 → 0.4.0.pre6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of reel might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +1 -1
- data/Gemfile +3 -2
- data/README.md +52 -52
- data/examples/server_sent_events.rb +115 -0
- data/lib/reel/response.rb +5 -4
- data/lib/reel/version.rb +1 -1
- data/spec/reel/response_spec.rb +13 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62ce6ab7ff51c2b2765a1f13a373135fe8f80f1d
|
4
|
+
data.tar.gz: 685cd6aaa68ba0abf31525113bcaa9dc1633e30e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 826bd08b859f751762f444d0ce4192c5efced88d41c1982b4f9488377fc4f051cabc79dadfe7a6859b120313c4e796713bb67b82aa06a85218e2bd7ab4490362
|
7
|
+
data.tar.gz: 47fe6ce99b5571deeffeb3909a42bb2b715ddf63e9bfb3b70a487efe4613f059257a1295202d1d4f04869053723712e00d8c1308358023b103ab6084e003eb76
|
data/CHANGES.md
CHANGED
data/Gemfile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'celluloid', github: 'celluloid/celluloid'
|
4
|
-
gem 'celluloid-io', github: 'celluloid/celluloid-io'
|
3
|
+
gem 'celluloid', github: 'celluloid/celluloid'
|
4
|
+
gem 'celluloid-io', github: 'celluloid/celluloid-io'
|
5
|
+
gem 'http', github: 'tarcieri/http'
|
5
6
|
|
6
7
|
gem 'jruby-openssl' if defined? JRUBY_VERSION
|
7
8
|
gem 'coveralls', require: false
|
data/README.md
CHANGED
@@ -59,8 +59,58 @@ Node.js (0.6.5) 11735 reqs/s (0.1 ms/req)
|
|
59
59
|
All Ruby benchmarks done on Ruby 1.9.3. Latencies given are average-per-request
|
60
60
|
and are not amortized across all concurrent requests.
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
Framework Adapters
|
63
|
+
------------------
|
64
|
+
|
65
|
+
### Rack
|
66
|
+
|
67
|
+
A Rack adapter for Reel is available at:
|
68
|
+
|
69
|
+
https://github.com/celluloid/reel-rack
|
70
|
+
|
71
|
+
### Webmachine
|
72
|
+
|
73
|
+
The most notable library with native Reel support is
|
74
|
+
[webmachine-ruby](https://github.com/seancribbs/webmachine-ruby),
|
75
|
+
an advanced HTTP framework for Ruby with a complete state machine for proper
|
76
|
+
processing of HTTP/1.1 requests. Together with Reel, Webmachine provides
|
77
|
+
full streaming support for both requests and responses.
|
78
|
+
|
79
|
+
To use Reel with Webmachine, add the following to your Gemfile:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
gem 'webmachine', git: 'git://github.com/seancribbs/webmachine-ruby.git'
|
83
|
+
```
|
84
|
+
|
85
|
+
Then use `config.adapter = :Reel` when configuring a Webmachine app, e.g:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
MyApp = Webmachine::Application.new do |app|
|
89
|
+
app.routes do
|
90
|
+
add ['*'], MyHome
|
91
|
+
end
|
92
|
+
|
93
|
+
app.configure do |config|
|
94
|
+
config.ip = MYAPP_IP
|
95
|
+
config.port = MYAPP_PORT
|
96
|
+
config.adapter = :Reel
|
97
|
+
|
98
|
+
# Optional: handler for incoming websockets
|
99
|
+
config.adapter_options[:websocket_handler] = proc do |websocket|
|
100
|
+
# socket is a Reel::WebSocket
|
101
|
+
socket << "hello, world"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
MyApp.run
|
107
|
+
```
|
108
|
+
|
109
|
+
See the [Webmachine documentation](http://rubydoc.info/gems/webmachine/frames/file/README.md)
|
110
|
+
for further information
|
111
|
+
|
112
|
+
Ruby API
|
113
|
+
--------
|
64
114
|
|
65
115
|
*NOTE: these examples are for the Reel 0.4.0.pre2 API*
|
66
116
|
|
@@ -134,56 +184,6 @@ end
|
|
134
184
|
MyServer.run
|
135
185
|
```
|
136
186
|
|
137
|
-
Framework Adapters
|
138
|
-
------------------
|
139
|
-
|
140
|
-
### Rack
|
141
|
-
|
142
|
-
A Rack adapter for Reel is available at:
|
143
|
-
|
144
|
-
https://github.com/celluloid/reel-rack
|
145
|
-
|
146
|
-
### Webmachine
|
147
|
-
|
148
|
-
The most notable library with native Reel support is
|
149
|
-
[webmachine-ruby](https://github.com/seancribbs/webmachine-ruby),
|
150
|
-
an advanced HTTP framework for Ruby with a complete state machine for proper
|
151
|
-
processing of HTTP/1.1 requests. Together with Reel, Webmachine provides
|
152
|
-
full streaming support for both requests and responses.
|
153
|
-
|
154
|
-
To use Reel with Webmachine, add the following to your Gemfile:
|
155
|
-
|
156
|
-
```ruby
|
157
|
-
gem 'webmachine', git: 'git://github.com/seancribbs/webmachine-ruby.git'
|
158
|
-
```
|
159
|
-
|
160
|
-
Then use `config.adapter = :Reel` when configuring a Webmachine app, e.g:
|
161
|
-
|
162
|
-
```ruby
|
163
|
-
MyApp = Webmachine::Application.new do |app|
|
164
|
-
app.routes do
|
165
|
-
add ['*'], MyHome
|
166
|
-
end
|
167
|
-
|
168
|
-
app.configure do |config|
|
169
|
-
config.ip = MYAPP_IP
|
170
|
-
config.port = MYAPP_PORT
|
171
|
-
config.adapter = :Reel
|
172
|
-
|
173
|
-
# Optional: handler for incoming websockets
|
174
|
-
config.adapter_options[:websocket_handler] = proc do |websocket|
|
175
|
-
# socket is a Reel::WebSocket
|
176
|
-
socket << "hello, world"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
MyApp.run
|
182
|
-
```
|
183
|
-
|
184
|
-
See the [Webmachine documentation](http://rubydoc.info/gems/webmachine/frames/file/README.md)
|
185
|
-
for further information
|
186
|
-
|
187
187
|
Contributing
|
188
188
|
------------
|
189
189
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Run with: bundle exec examples/server_sent_events.rb
|
3
|
+
# Test with: curl -vNH 'Accept: text/event-stream' -H 'Last-Event-ID: 1' -H 'Cache-Control: no-cache' http://localhost:63310
|
4
|
+
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'time'
|
7
|
+
require 'reel'
|
8
|
+
|
9
|
+
class ServerSentEvents < Reel::Server
|
10
|
+
include Celluloid::Logger
|
11
|
+
|
12
|
+
def initialize(ip = '127.0.0.1', port = 63310)
|
13
|
+
@connections = []
|
14
|
+
@history = []
|
15
|
+
@lastMessageId = 0
|
16
|
+
async.ping
|
17
|
+
async.ring
|
18
|
+
super(ip, port, &method(:on_connection))
|
19
|
+
end
|
20
|
+
|
21
|
+
def broadcast(event, data)
|
22
|
+
#only keep the last 5000 Events
|
23
|
+
if @history.size >= 6000
|
24
|
+
@history.slice!(0, @history.size - 1000)
|
25
|
+
end
|
26
|
+
@lastMessageId += 1
|
27
|
+
@history << {id: @lastMessageId, event: event, data: data}
|
28
|
+
info "Sending Event: #{event} Data: #{data} to #{@connections.count} Clients"
|
29
|
+
@connections.each do |socket|
|
30
|
+
async.send_sse(socket, data, event, @lastMessageId)
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
#event and id are optional
|
37
|
+
def send_sse(socket, data, event = nil, id = nil)
|
38
|
+
begin
|
39
|
+
socket.id id if id
|
40
|
+
socket.event event if event
|
41
|
+
socket.data data
|
42
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
|
43
|
+
@connections.delete(socket)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_ping
|
48
|
+
@connections.each do |socket|
|
49
|
+
begin
|
50
|
+
#Lines that start with a Colon are Comments and will be ignored
|
51
|
+
socket << ":\n"
|
52
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
|
53
|
+
@connections.delete(socket)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def ping
|
59
|
+
#apache 2.2 closes connections after five seconds when nothing is send
|
60
|
+
every(5) do
|
61
|
+
send_ping
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def ring
|
66
|
+
every(2) do
|
67
|
+
broadcast(:time, Time.now.httpdate)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_request(request)
|
72
|
+
query = {}
|
73
|
+
(request.query_string || '').split('&').each do |kv|
|
74
|
+
key, value = kv.split('=')
|
75
|
+
if key && value
|
76
|
+
key, value = CGI.unescape(key), CGI.unescape(value)
|
77
|
+
query[key] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
body = Reel::EventStream.new do |socket|
|
81
|
+
@connections << socket
|
82
|
+
socket.retry 5000
|
83
|
+
#after a Connection reset resend newer Messages to the Client
|
84
|
+
if @history.count > 0 && id = (request.headers['Last-Event-ID'] || query['lastEventId'])
|
85
|
+
begin
|
86
|
+
id = Integer(id)
|
87
|
+
if history = @history.select {|h| h[:id] >= id}.map {|a| "id: %d\nevent: %s\ndata: %s" % [a[:id], a[:event], a[:data]]}.join("\n\n")
|
88
|
+
socket << "%s\n\n" % [history]
|
89
|
+
else
|
90
|
+
socket << "id\n\n"
|
91
|
+
end
|
92
|
+
rescue ArgumentError, IOError, Errno::ECONNRESET, Errno::EPIPE
|
93
|
+
@connections.delete(socket)
|
94
|
+
request.close
|
95
|
+
end
|
96
|
+
else
|
97
|
+
socket << "id\n\n"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
#X-Accel-Buffering is nginx(?) specific. Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
|
101
|
+
request.respond Reel::StreamResponse.new(:ok, {
|
102
|
+
'Content-Type' => 'text/event-stream; charset=utf-8',
|
103
|
+
'Cache-Control' => 'no-cache',
|
104
|
+
'X-Accel-Buffering' => 'no',
|
105
|
+
'Access-Control-Allow-Origin' => '*'}, body)
|
106
|
+
end
|
107
|
+
|
108
|
+
def on_connection(connection)
|
109
|
+
connection.each_request do |request|
|
110
|
+
handle_request(request)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
ServerSentEvents.run
|
data/lib/reel/response.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
module Reel
|
2
2
|
class Response
|
3
|
+
include HTTP::Header
|
3
4
|
|
4
5
|
CONTENT_LENGTH = 'Content-Length'.freeze
|
5
6
|
TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
|
6
7
|
CHUNKED = 'chunked'.freeze
|
7
8
|
|
8
|
-
# Use status code tables from the
|
9
|
-
STATUS_CODES =
|
10
|
-
SYMBOL_TO_STATUS_CODE =
|
9
|
+
# Use status code tables from the HTTP gem
|
10
|
+
STATUS_CODES = HTTP::Response::STATUS_CODES
|
11
|
+
SYMBOL_TO_STATUS_CODE = HTTP::Response::SYMBOL_TO_STATUS_CODE
|
11
12
|
|
12
13
|
attr_reader :status # Status has a special setter to coerce symbol names
|
13
14
|
attr_accessor :reason # Reason can be set explicitly if desired
|
@@ -62,7 +63,7 @@ module Reel
|
|
62
63
|
|
63
64
|
def canonicalize_headers(headers)
|
64
65
|
headers.inject({}) do |headers, (header, value)|
|
65
|
-
headers.merge
|
66
|
+
headers.merge canonicalize_header(header) => value.to_s
|
66
67
|
end.freeze
|
67
68
|
end
|
68
69
|
private :canonicalize_headers
|
data/lib/reel/version.rb
CHANGED
data/spec/reel/response_spec.rb
CHANGED
@@ -15,4 +15,17 @@ describe Reel::Response do
|
|
15
15
|
response[(response.length - fixture.length)..-1].should eq fixture
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
it "canonicalizes response headers" do
|
20
|
+
with_socket_pair do |client, connection|
|
21
|
+
client << ExampleRequest.new.to_s
|
22
|
+
request = connection.request
|
23
|
+
|
24
|
+
connection.respond Reel::Response.new(:ok, {"content-type" => "application/json"}, "['mmmkay']")
|
25
|
+
connection.close
|
26
|
+
|
27
|
+
response = client.read(4096)
|
28
|
+
expect(response["Content-Type: application/json"]).to_not be_nil
|
29
|
+
end
|
30
|
+
end
|
18
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.0.
|
4
|
+
version: 0.4.0.pre6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: celluloid
|
@@ -131,6 +131,7 @@ files:
|
|
131
131
|
- examples/chunked.rb
|
132
132
|
- examples/hello_world.rb
|
133
133
|
- examples/roundtrip.rb
|
134
|
+
- examples/server_sent_events.rb
|
134
135
|
- examples/ssl_hello_world.rb
|
135
136
|
- examples/websockets.rb
|
136
137
|
- lib/reel.rb
|
@@ -198,3 +199,4 @@ test_files:
|
|
198
199
|
- spec/reel/ssl_server_spec.rb
|
199
200
|
- spec/reel/websocket_spec.rb
|
200
201
|
- spec/spec_helper.rb
|
202
|
+
has_rdoc:
|