reel 0.4.0.pre2 → 0.4.0.pre3
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 +4 -1
- data/README.md +6 -18
- data/examples/hello_world.rb +6 -5
- data/examples/ssl_hello_world.rb +7 -6
- data/lib/reel.rb +0 -5
- data/lib/reel/request.rb +1 -1
- data/lib/reel/request_info.rb +0 -8
- data/lib/reel/version.rb +2 -1
- data/reel.gemspec +1 -3
- metadata +4 -47
- data/benchmarks/hello_rack.ru +0 -5
- data/bin/reel +0 -65
- data/examples/server-sent-events.rb +0 -65
- data/examples/stream.rb +0 -28
- data/examples/websocket-wall.rb +0 -66
- data/examples/websocket.ru +0 -93
- data/examples/websocket_rack.sh +0 -1
- data/lib/rack/handler/reel.rb +0 -93
- data/lib/reel/app.rb +0 -34
- data/lib/reel/rack_worker.rb +0 -132
- data/spec/reel/app_spec.rb +0 -35
- data/spec/reel/rack_worker_spec.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7fd599c0341fa3bf5d015b4425c5edebabdfa82
|
4
|
+
data.tar.gz: 180dc3111577c86b2f255afb2ad95f0596db318b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d53b5db5d246d04c2a22880d52816838485a7c71c9597a55724e69a265c1175308136800ad52e458d6969bf4794d8f9390e0754d5f1ecca11658bca6e34f724
|
7
|
+
data.tar.gz: 967895df5e03fdf13b70d9521f92dec8f3591c8681eeb692253592023c5216363a7d1baa6893dcbd0f23b8b3f44775c51006d220001f2fef067b83170c207989
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
0.4.0.
|
1
|
+
0.4.0.pre3
|
2
2
|
----------
|
3
|
+
* Rack adapter moved to the reel-rack project
|
3
4
|
* Pipelining support
|
4
5
|
* Reel::Connection#each_request for iterating through keep-alive requests
|
5
6
|
* Reel::Request#body now returns a Reel::RequestBody object instead of a String
|
@@ -9,6 +10,8 @@
|
|
9
10
|
* Allow Reel to stop cleanly
|
10
11
|
* Remove `on_error` callback system
|
11
12
|
* Increase buffer size
|
13
|
+
* Remove Reel::App (unmaintained, sorry)
|
14
|
+
* Reel::CODENAME added (presently "Garbo")
|
12
15
|
|
13
16
|
0.3.0
|
14
17
|
-----
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
[![Code Climate](https://codeclimate.com/github/celluloid/reel.png)](https://codeclimate.com/github/celluloid/reel)
|
7
7
|
[![Coverage Status](https://coveralls.io/repos/celluloid/reel/badge.png?branch=master)](https://coveralls.io/r/celluloid/reel)
|
8
8
|
|
9
|
-
> "A dizzying lifetime... reeling by on
|
9
|
+
> "A dizzying lifetime... reeling by on celluloid" _-- Rush / Between The Wheels_
|
10
10
|
|
11
11
|
Reel is a fast, non-blocking "evented" web server built on [http_parser.rb][parser],
|
12
12
|
[websocket_parser][websockets], [Celluloid::IO][celluloidio], and [nio4r][nio4r]. Thanks
|
@@ -65,8 +65,9 @@ API
|
|
65
65
|
|
66
66
|
*NOTE: these examples are for the Reel 0.4.0.pre2 API*
|
67
67
|
|
68
|
-
Reel
|
69
|
-
|
68
|
+
Reel aims to provide a "bare metal" API that other frameworks (such as Rack
|
69
|
+
and Webmachine) can leverage. This API can also be nice in performance critical
|
70
|
+
applications.
|
70
71
|
|
71
72
|
### Block Form
|
72
73
|
|
@@ -139,22 +140,9 @@ Framework Adapters
|
|
139
140
|
|
140
141
|
### Rack
|
141
142
|
|
142
|
-
|
143
|
-
application. Please be aware that Rack support is experimental and that there
|
144
|
-
are potential complications between using large numbers of rack middlewares
|
145
|
-
and the limited 4kB stack depth of Ruby Fibers, which are used extensively
|
146
|
-
by Celluloid. In addition, the Rack specification mandates that request bodies
|
147
|
-
are rewindable, which prevents streaming request bodies as the spec dictates
|
148
|
-
they must be written to disk.
|
143
|
+
A Rack adapter for Reel is available at:
|
149
144
|
|
150
|
-
|
151
|
-
|
152
|
-
```
|
153
|
-
rackup -p 1234 -s reel config.ru -Enone -O "workers=16"
|
154
|
-
```
|
155
|
-
|
156
|
-
To really leverage Reel's capabilities, you must use Reel via its own API,
|
157
|
-
or another Ruby library with direct Reel support.
|
145
|
+
https://github.com/celluloid/reel-rack
|
158
146
|
|
159
147
|
### Webmachine
|
160
148
|
|
data/examples/hello_world.rb
CHANGED
@@ -9,13 +9,14 @@ addr, port = '127.0.0.1', 1234
|
|
9
9
|
|
10
10
|
puts "*** Starting server on #{addr}:#{port}"
|
11
11
|
Reel::Server.run(addr, port) do |connection|
|
12
|
-
#
|
13
|
-
|
14
|
-
while request = connection.request
|
12
|
+
# For keep-alive support
|
13
|
+
connection.each_request do |request|
|
15
14
|
# Ordinarily we'd route the request here, e.g.
|
16
15
|
# route request.url
|
17
|
-
|
16
|
+
request.respond :ok, "hello, world!"
|
18
17
|
end
|
19
18
|
|
20
|
-
# Reel takes care of closing the connection
|
19
|
+
# Reel takes care of closing the connection for you
|
20
|
+
# If you would like to hand the connection off to another thread or actor,
|
21
|
+
# use, connection.detach and then manually call connection.close when done
|
21
22
|
end
|
data/examples/ssl_hello_world.rb
CHANGED
@@ -13,15 +13,16 @@ options = {
|
|
13
13
|
|
14
14
|
puts "*** Starting server on #{addr}:#{port}"
|
15
15
|
Reel::SSLServer.supervise(addr, port, options) do |connection|
|
16
|
-
#
|
17
|
-
|
18
|
-
while request = connection.request
|
16
|
+
# For keep-alive support
|
17
|
+
connection.each_request do |request|
|
19
18
|
# Ordinarily we'd route the request here, e.g.
|
20
19
|
# route request.url
|
21
|
-
|
20
|
+
request.respond :ok, "hello, world!"
|
22
21
|
end
|
23
22
|
|
24
|
-
# Reel takes care of closing the connection
|
23
|
+
# Reel takes care of closing the connection for you
|
24
|
+
# If you would like to hand the connection off to another thread or actor,
|
25
|
+
# use, connection.detach and then manually call connection.close when done
|
25
26
|
end
|
26
27
|
|
27
|
-
sleep
|
28
|
+
sleep
|
data/lib/reel.rb
CHANGED
data/lib/reel/request.rb
CHANGED
@@ -5,7 +5,7 @@ module Reel
|
|
5
5
|
extend Forwardable
|
6
6
|
include RequestMixin
|
7
7
|
|
8
|
-
def_delegators :@connection, :<<, :write, :respond, :finish_response
|
8
|
+
def_delegators :@connection, :<<, :write, :remote_addr, :respond, :finish_response
|
9
9
|
attr_reader :body
|
10
10
|
|
11
11
|
# request_info is a RequestInfo object including the headers and
|
data/lib/reel/request_info.rb
CHANGED
@@ -12,14 +12,6 @@ module Reel
|
|
12
12
|
UPGRADE = 'Upgrade'.freeze
|
13
13
|
WEBSOCKET = 'websocket'.freeze
|
14
14
|
|
15
|
-
# Array#include? seems slow compared to Hash lookup
|
16
|
-
request_methods = Http::METHODS.map { |m| m.to_s.upcase }
|
17
|
-
REQUEST_METHODS = Hash[request_methods.zip(request_methods)].freeze
|
18
|
-
|
19
|
-
def method
|
20
|
-
REQUEST_METHODS[http_method]
|
21
|
-
end
|
22
|
-
|
23
15
|
def websocket_request?
|
24
16
|
headers[UPGRADE] && headers[UPGRADE].downcase == WEBSOCKET
|
25
17
|
end
|
data/lib/reel/version.rb
CHANGED
data/reel.gemspec
CHANGED
@@ -17,12 +17,10 @@ Gem::Specification.new do |gem|
|
|
17
17
|
|
18
18
|
gem.add_runtime_dependency 'celluloid', '>= 0.14.1'
|
19
19
|
gem.add_runtime_dependency 'celluloid-io', '>= 0.14.1'
|
20
|
-
gem.add_runtime_dependency 'http', '>= 0.
|
20
|
+
gem.add_runtime_dependency 'http', '>= 0.5.0.pre'
|
21
21
|
gem.add_runtime_dependency 'http_parser.rb', '>= 0.6.0.beta.2'
|
22
22
|
gem.add_runtime_dependency 'websocket_parser', '>= 0.1.4'
|
23
|
-
gem.add_runtime_dependency 'rack', '>= 1.4.0'
|
24
23
|
|
25
24
|
gem.add_development_dependency 'rake'
|
26
25
|
gem.add_development_dependency 'rspec'
|
27
|
-
gem.add_development_dependency 'octarine'
|
28
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.pre3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - '>='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 0.
|
47
|
+
version: 0.5.0.pre
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.
|
54
|
+
version: 0.5.0.pre
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: http_parser.rb
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.1.4
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rack
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - '>='
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 1.4.0
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - '>='
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 1.4.0
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: rake
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,25 +108,10 @@ dependencies:
|
|
122
108
|
- - '>='
|
123
109
|
- !ruby/object:Gem::Version
|
124
110
|
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: octarine
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - '>='
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - '>='
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
139
111
|
description: A Celluloid::IO-powered HTTP server
|
140
112
|
email:
|
141
113
|
- tony.arcieri@gmail.com
|
142
|
-
executables:
|
143
|
-
- reel
|
114
|
+
executables: []
|
144
115
|
extensions: []
|
145
116
|
extra_rdoc_files: []
|
146
117
|
files:
|
@@ -156,26 +127,16 @@ files:
|
|
156
127
|
- Rakefile
|
157
128
|
- benchmarks/hello_goliath.rb
|
158
129
|
- benchmarks/hello_node.js
|
159
|
-
- benchmarks/hello_rack.ru
|
160
130
|
- benchmarks/hello_reel.rb
|
161
|
-
- bin/reel
|
162
131
|
- examples/chunked.rb
|
163
132
|
- examples/hello_world.rb
|
164
133
|
- examples/roundtrip.rb
|
165
|
-
- examples/server-sent-events.rb
|
166
134
|
- examples/ssl_hello_world.rb
|
167
|
-
- examples/stream.rb
|
168
|
-
- examples/websocket-wall.rb
|
169
|
-
- examples/websocket.ru
|
170
|
-
- examples/websocket_rack.sh
|
171
135
|
- examples/websockets.rb
|
172
|
-
- lib/rack/handler/reel.rb
|
173
136
|
- lib/reel.rb
|
174
|
-
- lib/reel/app.rb
|
175
137
|
- lib/reel/connection.rb
|
176
138
|
- lib/reel/logger.rb
|
177
139
|
- lib/reel/mixins.rb
|
178
|
-
- lib/reel/rack_worker.rb
|
179
140
|
- lib/reel/request.rb
|
180
141
|
- lib/reel/request_body.rb
|
181
142
|
- lib/reel/request_info.rb
|
@@ -195,9 +156,7 @@ files:
|
|
195
156
|
- spec/fixtures/example.txt
|
196
157
|
- spec/fixtures/server.crt
|
197
158
|
- spec/fixtures/server.key
|
198
|
-
- spec/reel/app_spec.rb
|
199
159
|
- spec/reel/connection_spec.rb
|
200
|
-
- spec/reel/rack_worker_spec.rb
|
201
160
|
- spec/reel/response_spec.rb
|
202
161
|
- spec/reel/server_spec.rb
|
203
162
|
- spec/reel/ssl_server_spec.rb
|
@@ -233,9 +192,7 @@ test_files:
|
|
233
192
|
- spec/fixtures/example.txt
|
234
193
|
- spec/fixtures/server.crt
|
235
194
|
- spec/fixtures/server.key
|
236
|
-
- spec/reel/app_spec.rb
|
237
195
|
- spec/reel/connection_spec.rb
|
238
|
-
- spec/reel/rack_worker_spec.rb
|
239
196
|
- spec/reel/response_spec.rb
|
240
197
|
- spec/reel/server_spec.rb
|
241
198
|
- spec/reel/ssl_server_spec.rb
|
data/benchmarks/hello_rack.ru
DELETED
data/bin/reel
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'reel'
|
4
|
-
require 'optparse'
|
5
|
-
|
6
|
-
options = {
|
7
|
-
rackup: "config.ru"
|
8
|
-
}
|
9
|
-
|
10
|
-
parser = OptionParser.new do |opts|
|
11
|
-
opts.on "-p", "--port PORT", Integer,
|
12
|
-
"Define what port TCP port to bind to (default: 3000)" do |arg|
|
13
|
-
options[:port] = arg
|
14
|
-
end
|
15
|
-
|
16
|
-
opts.on "-a", "--address HOST",
|
17
|
-
"bind to HOST address (default: 0.0.0.0)" do |arg|
|
18
|
-
options[:host] = arg
|
19
|
-
end
|
20
|
-
|
21
|
-
opts.on "-q", "--quiet", "Quiet down the output" do
|
22
|
-
options[:quiet] = true
|
23
|
-
end
|
24
|
-
|
25
|
-
opts.on "-e", "--environment ENVIRONMENT",
|
26
|
-
"The environment to run the Rack app on (default: development)" do |arg|
|
27
|
-
options[:environment] = arg
|
28
|
-
end
|
29
|
-
|
30
|
-
opts.on "-t", "--threads NUM", Integer,
|
31
|
-
"The number of worker threads (default: 10)" do |arg|
|
32
|
-
options[:workers] = arg
|
33
|
-
end
|
34
|
-
|
35
|
-
opts.on "-r", "--rackup FILE",
|
36
|
-
"Load Rack config from this file (default: config.ru)" do |arg|
|
37
|
-
options[:rackup] = arg
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
parser.banner = "reel <options> <rackup file>"
|
42
|
-
|
43
|
-
parser.on_tail "-h", "--help", "Show help" do
|
44
|
-
puts parser
|
45
|
-
exit 1
|
46
|
-
end
|
47
|
-
|
48
|
-
parser.parse!(ARGV)
|
49
|
-
|
50
|
-
if ARGV.last =~ /\.ru$/
|
51
|
-
options[:rackup] = ARGV.shift
|
52
|
-
end
|
53
|
-
|
54
|
-
unless File.exists?(options[:rackup])
|
55
|
-
abort "No rackup found at #{options[:rackup]}"
|
56
|
-
end
|
57
|
-
|
58
|
-
handler = Rack::Handler::Reel.new(options)
|
59
|
-
|
60
|
-
Reel::Logger.info "A Reel good HTTP server!"
|
61
|
-
Reel::Logger.info "Listening on #{handler[:host]}:#{handler[:port]}"
|
62
|
-
|
63
|
-
handler.start
|
64
|
-
|
65
|
-
sleep
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'reel'
|
4
|
-
require 'reel/app'
|
5
|
-
|
6
|
-
class ServerSideEvents
|
7
|
-
include Reel::App
|
8
|
-
|
9
|
-
def initialize(host, port)
|
10
|
-
super
|
11
|
-
@connections = []
|
12
|
-
@body = DATA.read
|
13
|
-
end
|
14
|
-
|
15
|
-
get '/' do
|
16
|
-
[200, {'Content-Type' => 'text/html'}, @body]
|
17
|
-
end
|
18
|
-
|
19
|
-
get '/subscribe' do
|
20
|
-
Celluloid.logger.info "subscribing a client"
|
21
|
-
body = Reel::EventStream.new do |socket|
|
22
|
-
@connections << socket
|
23
|
-
end
|
24
|
-
Celluloid.logger.info "subscribing a client"
|
25
|
-
[200, {'Content-Type' => 'text/event-stream'}, body]
|
26
|
-
end
|
27
|
-
|
28
|
-
get '/wall/:rest' do |request|
|
29
|
-
deliver request.path.rest
|
30
|
-
end
|
31
|
-
|
32
|
-
get '/wall' do
|
33
|
-
deliver Time.now.to_s
|
34
|
-
end
|
35
|
-
|
36
|
-
def deliver(msg)
|
37
|
-
Celluloid.logger.info "sending a message to clients: #{msg.inspect}"
|
38
|
-
@connections.each do |s|
|
39
|
-
begin
|
40
|
-
s.data(msg)
|
41
|
-
rescue SocketError
|
42
|
-
@connections.delete(s)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
[200, {'Content-Type' => 'text/html'}, "Sent \"#{msg}\" to #{@connections.size} clients"]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
ServerSideEvents.new("0.0.0.0", 9292)
|
51
|
-
sleep
|
52
|
-
|
53
|
-
__END__
|
54
|
-
<!doctype html>
|
55
|
-
<html lang="en">
|
56
|
-
<body>
|
57
|
-
<div id="content">Waiting for messages...</div>
|
58
|
-
</body>
|
59
|
-
<script type="text/javascript">
|
60
|
-
var evs = new EventSource('/subscribe');
|
61
|
-
evs.onmessage = function(e){
|
62
|
-
document.getElementById('content').innerHTML = e.data;
|
63
|
-
}
|
64
|
-
</script>
|
65
|
-
</html>
|
data/examples/stream.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'reel'
|
4
|
-
require 'reel/app'
|
5
|
-
|
6
|
-
class Streamer
|
7
|
-
include Reel::App
|
8
|
-
|
9
|
-
get '/' do
|
10
|
-
body = Reel::Stream.new do |body|
|
11
|
-
# sending a payload to make sure browsers will render chunks as received
|
12
|
-
body << "<html>#{' '*1024}\n"
|
13
|
-
('A'..'Z').each do |l|
|
14
|
-
body << "<div>#{l}</div>\n"
|
15
|
-
sleep 0.5
|
16
|
-
end
|
17
|
-
body << "</html>\n"
|
18
|
-
body.finish
|
19
|
-
end
|
20
|
-
[200, {
|
21
|
-
'Transfer-Encoding' => 'identity',
|
22
|
-
'Content-Type' => 'text/html'
|
23
|
-
}, body]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
Streamer.new("0.0.0.0", 9292)
|
28
|
-
sleep
|
data/examples/websocket-wall.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'reel'
|
4
|
-
|
5
|
-
Connections = []
|
6
|
-
Body = DATA.read
|
7
|
-
app = Rack::Builder.new do
|
8
|
-
map '/' do
|
9
|
-
run lambda { |env|
|
10
|
-
[200, {'Content-Type' => 'text/html'}, [Body]]
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
map '/subscribe' do
|
15
|
-
run lambda { |env|
|
16
|
-
if socket = env['rack.websocket']
|
17
|
-
|
18
|
-
socket.on_message do |m|
|
19
|
-
begin
|
20
|
-
socket << 'Server got "%s" message' % m
|
21
|
-
rescue => SocketError
|
22
|
-
Connections.delete(socket)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
Connections << socket
|
27
|
-
socket.read_every 1
|
28
|
-
end
|
29
|
-
[200, {}, []]
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
map '/wall' do
|
34
|
-
run lambda { |env|
|
35
|
-
msg = env['PATH_INFO'].gsub(/\/+/, '').strip
|
36
|
-
msg = Time.now if msg.empty?
|
37
|
-
|
38
|
-
Connections.each do |s|
|
39
|
-
begin
|
40
|
-
s << msg
|
41
|
-
rescue => SocketError
|
42
|
-
Connections.delete(s)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
[200, {'Content-Type' => 'text/html'}, ["Sent \"#{msg}\" to #{Connections.size} clients"]]
|
47
|
-
}
|
48
|
-
end
|
49
|
-
end.to_app
|
50
|
-
|
51
|
-
Rack::Handler::Reel.run app, Port: 9292
|
52
|
-
|
53
|
-
__END__
|
54
|
-
<!doctype html>
|
55
|
-
<html lang="en">
|
56
|
-
<body>
|
57
|
-
<input type="button" onClick="ws.send(Math.random());" value="Send a message to server">
|
58
|
-
<div id="content"></div>
|
59
|
-
</body>
|
60
|
-
<script type="text/javascript">
|
61
|
-
ws = new WebSocket('ws://' + window.location.host + '/subscribe');
|
62
|
-
ws.onmessage = function(e) {
|
63
|
-
document.getElementById('content').innerHTML += e.data + '<br>';
|
64
|
-
}
|
65
|
-
</script>
|
66
|
-
</html>
|
data/examples/websocket.ru
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'reel'
|
4
|
-
require 'celluloid/autostart'
|
5
|
-
|
6
|
-
class TimeServer
|
7
|
-
include Celluloid
|
8
|
-
include Celluloid::Notifications
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
run!
|
12
|
-
end
|
13
|
-
|
14
|
-
def run
|
15
|
-
now = Time.now.to_f
|
16
|
-
sleep now.ceil - now + 0.001
|
17
|
-
|
18
|
-
every(1) { publish 'time_change', Time.now }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class TimeClient
|
23
|
-
include Celluloid
|
24
|
-
include Celluloid::Notifications
|
25
|
-
include Celluloid::Logger
|
26
|
-
|
27
|
-
def initialize(websocket)
|
28
|
-
info "Streaming time changes to client"
|
29
|
-
@socket = websocket
|
30
|
-
subscribe('time_change', :notify_time_change)
|
31
|
-
end
|
32
|
-
|
33
|
-
def notify_time_change(topic, new_time)
|
34
|
-
@socket << new_time.inspect
|
35
|
-
rescue Reel::SocketError
|
36
|
-
info "Time client disconnected"
|
37
|
-
terminate
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class Web
|
42
|
-
include Celluloid::Logger
|
43
|
-
|
44
|
-
def render_index
|
45
|
-
info "200 OK: /"
|
46
|
-
<<-HTML
|
47
|
-
<!doctype html>
|
48
|
-
<html lang="en">
|
49
|
-
<head>
|
50
|
-
<meta charset="utf-8">
|
51
|
-
<title>Reel WebSockets time server example</title>
|
52
|
-
<style>
|
53
|
-
body {
|
54
|
-
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
55
|
-
font-weight: 300;
|
56
|
-
text-align: center;
|
57
|
-
}
|
58
|
-
|
59
|
-
#content {
|
60
|
-
width: 800px;
|
61
|
-
margin: 0 auto;
|
62
|
-
background: #EEEEEE;
|
63
|
-
padding: 1em;
|
64
|
-
}
|
65
|
-
</style>
|
66
|
-
</head>
|
67
|
-
<script>
|
68
|
-
var SocketKlass = "MozWebSocket" in window ? MozWebSocket : WebSocket;
|
69
|
-
var ws = new SocketKlass('ws://' + window.location.host + '/timeinfo');
|
70
|
-
ws.onmessage = function(msg){
|
71
|
-
document.getElementById('current-time').innerHTML = msg.data;
|
72
|
-
}
|
73
|
-
</script>
|
74
|
-
<body>
|
75
|
-
<div id="content">
|
76
|
-
<h1>Time Server Example</h1>
|
77
|
-
<div>The time is now: <span id="current-time">...</span></div>
|
78
|
-
</div>
|
79
|
-
</body>
|
80
|
-
</html>
|
81
|
-
HTML
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
TimeServer.supervise_as :time_server
|
86
|
-
|
87
|
-
run Rack::URLMap.new(
|
88
|
-
"/" => Proc.new{ [200, {"Content-Type" => "text/html"}, [Web.new.render_index]]},
|
89
|
-
"/timeinfo" => Proc.new{ |env|
|
90
|
-
TimeClient.new(env["websocket.rack"])
|
91
|
-
[200, {}, []] # Fake response for middleware.
|
92
|
-
}
|
93
|
-
)
|
data/examples/websocket_rack.sh
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rackup -s reel websocket.ru -Enone -O "workers=16"
|
data/lib/rack/handler/reel.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'reel'
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
module Handler
|
5
|
-
class Reel
|
6
|
-
attr_reader :options
|
7
|
-
|
8
|
-
# Don't mess with Rack::File
|
9
|
-
File = ::File
|
10
|
-
|
11
|
-
DEFAULT_OPTIONS = {
|
12
|
-
:host => "0.0.0.0",
|
13
|
-
:port => 3000,
|
14
|
-
:quiet => false,
|
15
|
-
:workers => 10,
|
16
|
-
:rackup => "config.ru"
|
17
|
-
}
|
18
|
-
|
19
|
-
def self.run(app, options = {})
|
20
|
-
|
21
|
-
@handler = Reel.new(options.merge :app => app)
|
22
|
-
|
23
|
-
::Reel::Logger.info "A Reel good HTTP server!"
|
24
|
-
::Reel::Logger.info "Listening on #{@handler[:host]}:#{@handler[:port]}"
|
25
|
-
|
26
|
-
yield @handler if block_given?
|
27
|
-
@handler.start
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(opts = {})
|
31
|
-
opts = normalize_options(opts)
|
32
|
-
|
33
|
-
@options = DEFAULT_OPTIONS.merge(opts)
|
34
|
-
|
35
|
-
if @options[:environment]
|
36
|
-
ENV['RACK_ENV'] = @options[:environment].to_s
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def start
|
41
|
-
Celluloid::Actor[:reel_rack_pool] = ::Reel::RackWorker.pool(size: options[:workers], args: [self])
|
42
|
-
|
43
|
-
::Reel::Server.supervise_as(:reel_server, options[:host], options[:port]) do |connection|
|
44
|
-
Celluloid::Actor[:reel_rack_pool].handle(connection.detach)
|
45
|
-
end
|
46
|
-
|
47
|
-
sleep
|
48
|
-
end
|
49
|
-
|
50
|
-
def stop
|
51
|
-
Celluloid::Actor[:reel_server].terminate!
|
52
|
-
Celluloid::Actor[:reel_rack_pool].terminate!
|
53
|
-
exit
|
54
|
-
end
|
55
|
-
|
56
|
-
def [](option)
|
57
|
-
@options[option]
|
58
|
-
end
|
59
|
-
|
60
|
-
def rack_app
|
61
|
-
return @options[:app] if @options[:app]
|
62
|
-
|
63
|
-
path = @options[:rackup]
|
64
|
-
|
65
|
-
unless File.exists?(path)
|
66
|
-
raise "Missing rackup file '#{path}'"
|
67
|
-
end
|
68
|
-
|
69
|
-
@options[:app], options = Rack::Builder.parse_file path
|
70
|
-
@options.merge! options
|
71
|
-
|
72
|
-
unless @options[:quiet]
|
73
|
-
@options[:app] = Rack::CommonLogger.new(@options[:app], STDOUT)
|
74
|
-
end
|
75
|
-
|
76
|
-
@options[:app]
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
# Transform the options that rails s reel passes
|
82
|
-
def normalize_options(options)
|
83
|
-
options = options.inject({}) { |h, (k,v)| h[k.downcase] = v ; h }
|
84
|
-
options[:rackup] = options[:config] if options[:config]
|
85
|
-
options[:port] = options[:port].to_i if options[:port]
|
86
|
-
options[:workers] = options[:workers].to_i if options[:workers]
|
87
|
-
options
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
register :reel, Reel
|
92
|
-
end
|
93
|
-
end
|
data/lib/reel/app.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'reel'
|
2
|
-
require 'octarine'
|
3
|
-
|
4
|
-
module Reel
|
5
|
-
# Define Reel endpoints using a sinatra-like dsl (provided by octarine)
|
6
|
-
module App
|
7
|
-
def self.included(base)
|
8
|
-
base.class_eval do
|
9
|
-
include Octarine::App
|
10
|
-
|
11
|
-
attr_accessor :server
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(host, port)
|
16
|
-
super()
|
17
|
-
@server = Reel::Server.supervise(host, port) do |connection|
|
18
|
-
while request = connection.request
|
19
|
-
status, headers, body = call Rack::MockRequest.env_for(request.url, :method => request.method, :input => request.body.to_s)
|
20
|
-
response_klass = body.is_a?(Stream) ? StreamResponse : Response
|
21
|
-
connection.respond(response_klass.new(status_symbol(status), headers, body))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def status_symbol(status)
|
27
|
-
status.is_a?(Fixnum) ? Http::Response::STATUS_CODES[status].downcase.gsub(/\s|-/, '_').to_sym : status.to_sym
|
28
|
-
end
|
29
|
-
|
30
|
-
def terminate
|
31
|
-
@server.terminate if @server
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/reel/rack_worker.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
module Reel
|
2
|
-
class RackWorker
|
3
|
-
include Celluloid
|
4
|
-
include Celluloid::Logger
|
5
|
-
|
6
|
-
INITIAL_BODY = ''
|
7
|
-
|
8
|
-
# Freeze some HTTP header names & values
|
9
|
-
CONTENT_TYPE_ORIG = 'Content-Type'.freeze
|
10
|
-
CONTENT_LENGTH_ORIG = 'Content-Length'.freeze
|
11
|
-
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
12
|
-
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
13
|
-
|
14
|
-
SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
|
15
|
-
SERVER_NAME = 'SERVER_NAME'.freeze
|
16
|
-
SERVER_PORT = 'SERVER_PORT'.freeze
|
17
|
-
SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
|
18
|
-
GATEWAY_INTERFACE = "GATEWAY_INTERFACE".freeze
|
19
|
-
LOCALHOST = 'localhost'.freeze
|
20
|
-
HTTP_VERSION = 'HTTP_VERSION'.freeze
|
21
|
-
CGI_1_1 = 'CGI/1.1'.freeze
|
22
|
-
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
23
|
-
CONNECTION = 'HTTP_CONNECTION'.freeze
|
24
|
-
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
25
|
-
PATH_INFO = 'PATH_INFO'.freeze
|
26
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
27
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
28
|
-
HTTP_1_0 = 'HTTP/1.0'.freeze
|
29
|
-
HTTP_1_1 = 'HTTP/1.1'.freeze
|
30
|
-
HTTP_ = 'HTTP_'.freeze
|
31
|
-
HOST = 'Host'.freeze
|
32
|
-
|
33
|
-
# Freeze some Rack header names
|
34
|
-
RACK_INPUT = 'rack.input'.freeze
|
35
|
-
RACK_LOGGER = 'rack.logger'.freeze
|
36
|
-
RACK_VERSION = 'rack.version'.freeze
|
37
|
-
RACK_ERRORS = 'rack.errors'.freeze
|
38
|
-
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
39
|
-
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
40
|
-
RACK_RUN_ONCE = 'rack.run_once'.freeze
|
41
|
-
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
42
|
-
RACK_WEBSOCKET = 'rack.websocket'.freeze
|
43
|
-
|
44
|
-
PROTO_RACK_ENV = {
|
45
|
-
RACK_VERSION => ::Rack::VERSION,
|
46
|
-
RACK_ERRORS => STDERR,
|
47
|
-
RACK_MULTITHREAD => true,
|
48
|
-
RACK_MULTIPROCESS => false,
|
49
|
-
RACK_RUN_ONCE => false,
|
50
|
-
RACK_URL_SCHEME => "http".freeze,
|
51
|
-
SCRIPT_NAME => ENV[SCRIPT_NAME] || "",
|
52
|
-
SERVER_PROTOCOL => HTTP_1_1,
|
53
|
-
SERVER_SOFTWARE => "Reel/#{Reel::VERSION}".freeze,
|
54
|
-
GATEWAY_INTERFACE => CGI_1_1
|
55
|
-
}.freeze
|
56
|
-
|
57
|
-
def initialize(handler)
|
58
|
-
@handler, @app = handler, handler.rack_app
|
59
|
-
end
|
60
|
-
|
61
|
-
def handle(connection)
|
62
|
-
while request = connection.request
|
63
|
-
case request
|
64
|
-
when Request
|
65
|
-
handle_request(request, connection)
|
66
|
-
when WebSocket
|
67
|
-
handle_websocket(request, connection)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def handle_request(request, connection)
|
73
|
-
status, headers, body_parts = @app.call(request_env(request, connection))
|
74
|
-
body, is_stream = response_body(body_parts)
|
75
|
-
connection.respond (is_stream ? StreamResponse : Response).new(status, headers, body)
|
76
|
-
end
|
77
|
-
|
78
|
-
def handle_websocket(request, connection)
|
79
|
-
status, *rest = @app.call(websocket_env(request))
|
80
|
-
request.close unless status < 300
|
81
|
-
end
|
82
|
-
|
83
|
-
def request_env request, connection
|
84
|
-
env = env(request)
|
85
|
-
env[REMOTE_ADDR] = connection.remote_ip
|
86
|
-
env
|
87
|
-
end
|
88
|
-
|
89
|
-
def websocket_env request
|
90
|
-
env = env(request)
|
91
|
-
env[RACK_WEBSOCKET] = request.websocket
|
92
|
-
env[REMOTE_ADDR] = request.websocket.remote_ip
|
93
|
-
env
|
94
|
-
end
|
95
|
-
|
96
|
-
def response_body(body_parts)
|
97
|
-
if body_parts.respond_to?(:to_path)
|
98
|
-
::File.new(body_parts.to_path)
|
99
|
-
else
|
100
|
-
body = ''
|
101
|
-
body_parts.each do |c|
|
102
|
-
return [c, true] if c.is_a?(Reel::Stream)
|
103
|
-
body << c
|
104
|
-
end
|
105
|
-
body_parts.close if body_parts.respond_to?(:close)
|
106
|
-
body
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
private
|
111
|
-
def env request
|
112
|
-
env = Hash[PROTO_RACK_ENV]
|
113
|
-
|
114
|
-
env[RACK_INPUT] = StringIO.new(request.body.to_s || INITIAL_BODY)
|
115
|
-
env[RACK_INPUT].set_encoding(Encoding::BINARY) if env[RACK_INPUT].respond_to?(:set_encoding)
|
116
|
-
env[SERVER_NAME], env[SERVER_PORT] = (request[HOST]||'').split(':', 2)
|
117
|
-
env[SERVER_PORT] ||= @handler[:port].to_s
|
118
|
-
env[HTTP_VERSION] = request.version || env[SERVER_PROTOCOL]
|
119
|
-
env[REQUEST_METHOD] = request.method
|
120
|
-
env[PATH_INFO] = request.path
|
121
|
-
env[QUERY_STRING] = request.query_string || ''
|
122
|
-
|
123
|
-
(_ = request.headers.delete CONTENT_TYPE_ORIG) && (env[CONTENT_TYPE] = _)
|
124
|
-
(_ = request.headers.delete CONTENT_LENGTH_ORIG) && (env[CONTENT_LENGTH] = _)
|
125
|
-
|
126
|
-
request.headers.each_pair do |key, val|
|
127
|
-
env[HTTP_ + key.gsub('-', '_').upcase] = val
|
128
|
-
end
|
129
|
-
env
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
data/spec/reel/app_spec.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'reel/app'
|
3
|
-
|
4
|
-
describe Reel::App do
|
5
|
-
let(:app) {
|
6
|
-
Class.new do
|
7
|
-
include Reel::App
|
8
|
-
|
9
|
-
get example_path do
|
10
|
-
[200, {}, "hello foo"]
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
}
|
15
|
-
|
16
|
-
before(:each) do
|
17
|
-
@app = app.new(example_addr, example_port)
|
18
|
-
end
|
19
|
-
|
20
|
-
after(:each) do
|
21
|
-
@app.server.terminate if @app && @app.server.alive?
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'responds to get requests' do
|
25
|
-
res = Http.with_response(:object).get example_url
|
26
|
-
res.status.should == 200
|
27
|
-
res.headers.should == {"Content-Length" => res.body.length.to_s}
|
28
|
-
res.body.should == "hello foo"
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'terminates the server' do
|
32
|
-
@app.terminate
|
33
|
-
@app.server.should_not be_alive
|
34
|
-
end
|
35
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Reel::RackWorker do
|
4
|
-
let(:endpoint) { URI(example_url) }
|
5
|
-
|
6
|
-
RackApp = Proc.new do |env|
|
7
|
-
[200, {'Content-Type' => 'text/plain'}, ['Hello rack world!']]
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:worker) do
|
11
|
-
handler = Rack::Handler::Reel.new
|
12
|
-
handler.options[:app] = RackApp
|
13
|
-
|
14
|
-
Reel::RackWorker.new(handler)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "creates a rack env from a request" do
|
18
|
-
with_socket_pair do |client, connection|
|
19
|
-
client << ExampleRequest.new(:get, '/test?hello=true').to_s
|
20
|
-
request = connection.request
|
21
|
-
env = worker.request_env(request, connection)
|
22
|
-
|
23
|
-
Reel::RackWorker::PROTO_RACK_ENV.each do |k, v|
|
24
|
-
env[k].should == v
|
25
|
-
end
|
26
|
-
|
27
|
-
env["SERVER_NAME"].should == 'www.example.com'
|
28
|
-
env["SERVER_PORT"].should == "3000"
|
29
|
-
env["REMOTE_ADDR"].should == "127.0.0.1"
|
30
|
-
env["PATH_INFO"].should == "/test"
|
31
|
-
env["REQUEST_METHOD"].should == "GET"
|
32
|
-
env["QUERY_STRING"].should == "hello=true"
|
33
|
-
env["HTTP_HOST"].should == 'www.example.com'
|
34
|
-
env["HTTP_ACCEPT_LANGUAGE"].should == "en-US,en;q=0.8"
|
35
|
-
|
36
|
-
env["rack.input"].should be_kind_of(StringIO)
|
37
|
-
env["rack.input"].string.should == ''
|
38
|
-
|
39
|
-
validator = ::Rack::Lint.new(RackApp)
|
40
|
-
status, *rest = validator.call(env)
|
41
|
-
status.should == 200
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context "WebSocket" do
|
46
|
-
include WebSocketHelpers
|
47
|
-
|
48
|
-
it "places websocket into rack env" do
|
49
|
-
with_socket_pair do |client, connection|
|
50
|
-
client << handshake.to_data
|
51
|
-
request = connection.request
|
52
|
-
env = worker.websocket_env(request)
|
53
|
-
|
54
|
-
env["REMOTE_ADDR"].should == "127.0.0.1"
|
55
|
-
env["rack.websocket"].should be_a Reel::WebSocket
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
it "delegates web requests to the rack app" do
|
61
|
-
ex = nil
|
62
|
-
|
63
|
-
handler = proc do |connection|
|
64
|
-
begin
|
65
|
-
worker.async.handle(connection.detach)
|
66
|
-
rescue => ex
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
with_reel(handler) do
|
71
|
-
http = Net::HTTP.new(endpoint.host, endpoint.port)
|
72
|
-
request = Net::HTTP::Get.new(endpoint.request_uri)
|
73
|
-
response = http.request(request)
|
74
|
-
response.should be_a Net::HTTPOK
|
75
|
-
response.body.should == 'Hello rack world!'
|
76
|
-
end
|
77
|
-
|
78
|
-
raise ex if ex
|
79
|
-
end
|
80
|
-
end
|