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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c139c2fa0dcd5e9456cb8867fbe296eddc1c9f6
4
- data.tar.gz: 0ba97001f92ac212ba4184ba5059580e0e124245
3
+ metadata.gz: a7fd599c0341fa3bf5d015b4425c5edebabdfa82
4
+ data.tar.gz: 180dc3111577c86b2f255afb2ad95f0596db318b
5
5
  SHA512:
6
- metadata.gz: 02cfe9e2b456d5f6abb5c7810df4d6133ef5a1acfb49917de65f245d6bfadfe48c525b527ed060348ff8b306c071ac6714fa274c47aef693e08d79330aa639d9
7
- data.tar.gz: a9ebba27cce7485bae2ae2236311d5fe43e59fc8e2d03c2d3b5a2f2ca4b728b6cccf430b6962d6abad639b614da846b1367c0880095cae2e420cbdf91bbefedf
6
+ metadata.gz: 8d53b5db5d246d04c2a22880d52816838485a7c71c9597a55724e69a265c1175308136800ad52e458d6969bf4794d8f9390e0754d5f1ecca11658bca6e34f724
7
+ data.tar.gz: 967895df5e03fdf13b70d9521f92dec8f3591c8681eeb692253592023c5216363a7d1baa6893dcbd0f23b8b3f44775c51006d220001f2fef067b83170c207989
data/CHANGES.md CHANGED
@@ -1,5 +1,6 @@
1
- 0.4.0.pre2
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 Celluloid" _-- Rush / Between The Wheels_
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 also provides a "bare metal" API which was used in the benchmarks above.
69
- Here are some examples:
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
- Reel can be used as a standard Rack server via the "reel" command line
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
- To run `.ru` file using Reel w/ 16 workers
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
 
@@ -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
- # To use keep-alive with Reel, use a while loop that repeatedly calls
13
- # connection.request and consumes connection objects
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
- connection.respond :ok, "hello, world!"
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
@@ -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
- # To use keep-alive with Reel, use a while loop that repeatedly calls
17
- # connection.request and consumes connection objects
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
- connection.respond :ok, "hello, world!"
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
@@ -22,11 +22,6 @@ require 'reel/ssl_server'
22
22
  require 'reel/websocket'
23
23
  require 'reel/stream'
24
24
 
25
- require 'rack'
26
- require 'rack/handler'
27
- require 'rack/handler/reel'
28
- require 'reel/rack_worker'
29
-
30
25
  # A Reel good HTTP server
31
26
  module Reel
32
27
 
@@ -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
@@ -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
@@ -1,3 +1,4 @@
1
1
  module Reel
2
- VERSION = "0.4.0.pre2"
2
+ VERSION = "0.4.0.pre3"
3
+ CODENAME = "Garbo"
3
4
  end
@@ -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.2.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.pre2
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.2.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.2.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
@@ -1,5 +0,0 @@
1
- # Run with: thin -e production -R hello_rack.ru start
2
- require 'rack'
3
-
4
- body = "Hello World"
5
- run proc { |env| [200, {}, body] }
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>
@@ -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
@@ -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>
@@ -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
- )
@@ -1 +0,0 @@
1
- rackup -s reel websocket.ru -Enone -O "workers=16"
@@ -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
@@ -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
@@ -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
@@ -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