reel 0.4.0 → 0.5.0.pre
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/.travis.yml +5 -2
- data/Gemfile +6 -0
- data/README.md +26 -16
- data/benchmarks/hello_reel.rb +1 -1
- data/benchmarks/reel_pool.rb +27 -0
- data/examples/hello_world.rb +2 -2
- data/examples/{ssl_hello_world.rb → https_hello_world.rb} +1 -1
- data/examples/roundtrip.rb +1 -1
- data/examples/server_sent_events.rb +22 -18
- data/examples/spy_hello_world.rb +22 -0
- data/examples/websockets.rb +3 -3
- data/lib/reel.rb +6 -9
- data/lib/reel/connection.rb +40 -42
- data/lib/reel/mixins.rb +3 -1
- data/lib/reel/request.rb +40 -9
- data/lib/reel/request/body.rb +65 -0
- data/lib/reel/request/info.rb +21 -0
- data/lib/reel/{request_parser.rb → request/parser.rb} +2 -2
- data/lib/reel/request/state_machine.rb +26 -0
- data/lib/reel/response.rb +4 -11
- data/lib/reel/response/writer.rb +59 -0
- data/lib/reel/server.rb +30 -6
- data/lib/reel/server/http.rb +20 -0
- data/lib/reel/server/https.rb +63 -0
- data/lib/reel/spy.rb +71 -0
- data/lib/reel/stream.rb +2 -2
- data/lib/reel/version.rb +2 -2
- data/lib/reel/websocket.rb +1 -1
- data/reel.gemspec +3 -3
- data/spec/fixtures/ca.crt +27 -0
- data/spec/fixtures/ca.key +27 -0
- data/spec/fixtures/client.crt +81 -20
- data/spec/fixtures/client.unsigned.crt +22 -0
- data/spec/fixtures/server.crt +80 -20
- data/spec/reel/connection_spec.rb +50 -11
- data/spec/reel/{server_spec.rb → http_server_spec.rb} +1 -1
- data/spec/reel/https_server_spec.rb +119 -0
- data/spec/reel/{response_writer_spec.rb → response/writer_spec.rb} +10 -2
- data/spec/reel/websocket_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -34
- data/spec/support/example_request.rb +34 -0
- metadata +56 -43
- data/examples/chunked.rb +0 -25
- data/lib/reel/request_body.rb +0 -56
- data/lib/reel/request_info.rb +0 -19
- data/lib/reel/response_writer.rb +0 -76
- data/lib/reel/ssl_server.rb +0 -41
- data/spec/reel/ssl_server_spec.rb +0 -54
data/examples/chunked.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'reel'
|
4
|
-
|
5
|
-
app = Rack::Builder.new do
|
6
|
-
map '/' do
|
7
|
-
run lambda { |env|
|
8
|
-
body = Reel::ChunkStream.new do |body|
|
9
|
-
# sending a payload to make sure browsers will render chunks as received
|
10
|
-
body << "<html>#{' '*1024}\n"
|
11
|
-
('A'..'Z').each do |l|
|
12
|
-
body << "<div>#{l}</div>\n"
|
13
|
-
sleep 0.5
|
14
|
-
end
|
15
|
-
body << "</html>\n"
|
16
|
-
body.finish
|
17
|
-
end
|
18
|
-
[200, {
|
19
|
-
'Content-Type' => 'text/html'
|
20
|
-
}, body]
|
21
|
-
}
|
22
|
-
end
|
23
|
-
end.to_app
|
24
|
-
|
25
|
-
Rack::Handler::Reel.run app, Port: 9292
|
data/lib/reel/request_body.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
module Reel
|
2
|
-
# Represents the bodies of Requests
|
3
|
-
class RequestBody
|
4
|
-
include Enumerable
|
5
|
-
|
6
|
-
def initialize(request)
|
7
|
-
@request = request
|
8
|
-
@streaming = nil
|
9
|
-
@contents = nil
|
10
|
-
end
|
11
|
-
|
12
|
-
# Read exactly the given amount of data
|
13
|
-
def read(length)
|
14
|
-
stream!
|
15
|
-
@request.read(length)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Read up to length bytes, but return any data that's available
|
19
|
-
def readpartial(length = nil)
|
20
|
-
stream!
|
21
|
-
@request.readpartial(length)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Iterate over the body, allowing it to be enumerable
|
25
|
-
def each
|
26
|
-
while chunk = readpartial
|
27
|
-
yield chunk
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Eagerly consume the entire body as a string
|
32
|
-
def to_s
|
33
|
-
return @contents if @contents
|
34
|
-
raise StateError, "body is being streamed" unless @streaming.nil?
|
35
|
-
|
36
|
-
begin
|
37
|
-
@streaming = false
|
38
|
-
@contents = ""
|
39
|
-
while chunk = @request.readpartial
|
40
|
-
@contents << chunk
|
41
|
-
end
|
42
|
-
rescue
|
43
|
-
@contents = nil
|
44
|
-
raise
|
45
|
-
end
|
46
|
-
|
47
|
-
@contents
|
48
|
-
end
|
49
|
-
|
50
|
-
# Assert that the body is actively being streamed
|
51
|
-
def stream!
|
52
|
-
raise StateError, "body has already been consumed" if @streaming == false
|
53
|
-
@streaming = true
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/lib/reel/request_info.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module Reel
|
2
|
-
class RequestInfo
|
3
|
-
attr_reader :http_method, :url, :http_version, :headers
|
4
|
-
|
5
|
-
def initialize(http_method, url, http_version, headers)
|
6
|
-
@http_method = http_method
|
7
|
-
@url = url
|
8
|
-
@http_version = http_version
|
9
|
-
@headers = headers
|
10
|
-
end
|
11
|
-
|
12
|
-
UPGRADE = 'Upgrade'.freeze
|
13
|
-
WEBSOCKET = 'websocket'.freeze
|
14
|
-
|
15
|
-
def websocket_request?
|
16
|
-
headers[UPGRADE] && headers[UPGRADE].downcase == WEBSOCKET
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/reel/response_writer.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
module Reel
|
2
|
-
class Response
|
3
|
-
class Writer
|
4
|
-
CRLF = "\r\n"
|
5
|
-
|
6
|
-
def initialize(socket)
|
7
|
-
@socket = socket
|
8
|
-
end
|
9
|
-
|
10
|
-
# Write body chunks directly to the connection
|
11
|
-
def write(chunk)
|
12
|
-
chunk_header = chunk.bytesize.to_s(16)
|
13
|
-
@socket << chunk_header + CRLF
|
14
|
-
@socket << chunk + CRLF
|
15
|
-
rescue IOError, Errno::EPIPE, Errno::ECONNRESET => ex
|
16
|
-
raise Reel::SocketError, ex.to_s
|
17
|
-
end
|
18
|
-
|
19
|
-
# Finish the response and reset the response state to header
|
20
|
-
def finish_response
|
21
|
-
@socket << "0#{CRLF * 2}"
|
22
|
-
end
|
23
|
-
|
24
|
-
# Takes a Reel::Response and renders it
|
25
|
-
# back over the socket.
|
26
|
-
def handle_response(response)
|
27
|
-
@socket << render_header(response)
|
28
|
-
if response.respond_to?(:render)
|
29
|
-
response.render(@socket)
|
30
|
-
else
|
31
|
-
case response.body
|
32
|
-
when String
|
33
|
-
@socket << response.body
|
34
|
-
when IO
|
35
|
-
begin
|
36
|
-
if defined?(JRUBY_VERSION) && JRUBY_VERSION <= "1.6.7"
|
37
|
-
# JRuby 1.6.7 doesn't support IO.copy_stream :(
|
38
|
-
while data = response.body.read(4096)
|
39
|
-
@socket << data
|
40
|
-
end
|
41
|
-
else
|
42
|
-
# Use OS sendfile via IO.copy_stream
|
43
|
-
# FIXME: should use Celluloid::IO.copy_stream and allow these
|
44
|
-
# calls to be multiplexed through Celluloid::IO's reactor
|
45
|
-
# Until then we need a thread for each of these responses
|
46
|
-
Celluloid.defer { IO.copy_stream(response.body, @socket.to_io) }
|
47
|
-
# @socket currently not being converted to appropriate IO object automatically.
|
48
|
-
# Convert the object in advance to still enjoy IO.copy_stream coverage.
|
49
|
-
end
|
50
|
-
ensure
|
51
|
-
response.body.close
|
52
|
-
end
|
53
|
-
when Enumerable
|
54
|
-
response.body.each do |chunk|
|
55
|
-
write(chunk)
|
56
|
-
end
|
57
|
-
|
58
|
-
finish_response
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Convert headers into a string
|
64
|
-
def render_header(response)
|
65
|
-
response_header = "#{response.version} #{response.status} #{response.reason}#{CRLF}"
|
66
|
-
unless response.headers.empty?
|
67
|
-
response_header << response.headers.map do |header, value|
|
68
|
-
"#{header}: #{value}"
|
69
|
-
end.join(CRLF) << CRLF
|
70
|
-
end
|
71
|
-
response_header << CRLF
|
72
|
-
end
|
73
|
-
private :render_header
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
data/lib/reel/ssl_server.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Reel
|
2
|
-
class SSLServer < Server
|
3
|
-
execute_block_on_receiver :initialize
|
4
|
-
|
5
|
-
def initialize(host, port, options = {}, &callback)
|
6
|
-
backlog = options.fetch(:backlog, DEFAULT_BACKLOG)
|
7
|
-
|
8
|
-
# Ideally we can encapsulate this rather than making Ruby OpenSSL a
|
9
|
-
# mandatory part of the Reel API. It would be nice to support
|
10
|
-
# alternatives (e.g. Puma's MiniSSL)
|
11
|
-
ssl_context = OpenSSL::SSL::SSLContext.new
|
12
|
-
ssl_context.cert = OpenSSL::X509::Certificate.new options.fetch(:cert)
|
13
|
-
ssl_context.key = OpenSSL::PKey::RSA.new options.fetch(:key)
|
14
|
-
|
15
|
-
# We don't presently support verifying client certificates
|
16
|
-
# TODO: support client certificates!
|
17
|
-
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
18
|
-
|
19
|
-
@tcpserver = Celluloid::IO::TCPServer.new(host, port)
|
20
|
-
@server = Celluloid::IO::SSLServer.new(@tcpserver, ssl_context)
|
21
|
-
|
22
|
-
@server.listen(backlog)
|
23
|
-
@callback = callback
|
24
|
-
|
25
|
-
async.run
|
26
|
-
end
|
27
|
-
|
28
|
-
def run
|
29
|
-
loop do
|
30
|
-
begin
|
31
|
-
socket = @server.accept
|
32
|
-
rescue OpenSSL::SSL::SSLError => ex
|
33
|
-
Logger.warn "Error accepting SSLSocket: #{ex.class}: #{ex.to_s}"
|
34
|
-
retry
|
35
|
-
end
|
36
|
-
|
37
|
-
async.handle_connection socket
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
describe Reel::SSLServer do
|
5
|
-
let(:example_ssl_port) { example_port + 1 }
|
6
|
-
let(:example_url) { "https://#{example_addr}:#{example_ssl_port}#{example_path}" }
|
7
|
-
let(:endpoint) { URI(example_url) }
|
8
|
-
let(:response_body) { "ohai thar" }
|
9
|
-
|
10
|
-
let(:server_cert) { fixture_dir.join("server.crt").read }
|
11
|
-
let(:server_key) { fixture_dir.join("server.key").read }
|
12
|
-
|
13
|
-
it "receives HTTP requests and sends responses" do
|
14
|
-
ex = nil
|
15
|
-
|
16
|
-
handler = proc do |connection|
|
17
|
-
begin
|
18
|
-
request = connection.request
|
19
|
-
request.method.should eq 'GET'
|
20
|
-
request.version.should eq "1.1"
|
21
|
-
request.url.should eq example_path
|
22
|
-
|
23
|
-
connection.respond :ok, response_body
|
24
|
-
rescue => ex
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
with_reel_sslserver(handler) do
|
29
|
-
http = Net::HTTP.new(endpoint.host, endpoint.port)
|
30
|
-
http.use_ssl = true
|
31
|
-
|
32
|
-
# FIXME: VERIFY_NONE is bad! Authenticate the server cert!
|
33
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
34
|
-
request = Net::HTTP::Get.new(endpoint.path)
|
35
|
-
response = http.request(request)
|
36
|
-
|
37
|
-
response.body.should eq response_body
|
38
|
-
end
|
39
|
-
|
40
|
-
raise ex if ex
|
41
|
-
end
|
42
|
-
|
43
|
-
def with_reel_sslserver(handler, context = OpenSSL::SSL::SSLContext.new)
|
44
|
-
options = {
|
45
|
-
:cert => server_cert,
|
46
|
-
:key => server_key
|
47
|
-
}
|
48
|
-
|
49
|
-
server = Reel::SSLServer.new(example_addr, example_ssl_port, options, &handler)
|
50
|
-
yield server
|
51
|
-
ensure
|
52
|
-
server.terminate if server && server.alive?
|
53
|
-
end
|
54
|
-
end
|