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.

Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -2
  3. data/Gemfile +6 -0
  4. data/README.md +26 -16
  5. data/benchmarks/hello_reel.rb +1 -1
  6. data/benchmarks/reel_pool.rb +27 -0
  7. data/examples/hello_world.rb +2 -2
  8. data/examples/{ssl_hello_world.rb → https_hello_world.rb} +1 -1
  9. data/examples/roundtrip.rb +1 -1
  10. data/examples/server_sent_events.rb +22 -18
  11. data/examples/spy_hello_world.rb +22 -0
  12. data/examples/websockets.rb +3 -3
  13. data/lib/reel.rb +6 -9
  14. data/lib/reel/connection.rb +40 -42
  15. data/lib/reel/mixins.rb +3 -1
  16. data/lib/reel/request.rb +40 -9
  17. data/lib/reel/request/body.rb +65 -0
  18. data/lib/reel/request/info.rb +21 -0
  19. data/lib/reel/{request_parser.rb → request/parser.rb} +2 -2
  20. data/lib/reel/request/state_machine.rb +26 -0
  21. data/lib/reel/response.rb +4 -11
  22. data/lib/reel/response/writer.rb +59 -0
  23. data/lib/reel/server.rb +30 -6
  24. data/lib/reel/server/http.rb +20 -0
  25. data/lib/reel/server/https.rb +63 -0
  26. data/lib/reel/spy.rb +71 -0
  27. data/lib/reel/stream.rb +2 -2
  28. data/lib/reel/version.rb +2 -2
  29. data/lib/reel/websocket.rb +1 -1
  30. data/reel.gemspec +3 -3
  31. data/spec/fixtures/ca.crt +27 -0
  32. data/spec/fixtures/ca.key +27 -0
  33. data/spec/fixtures/client.crt +81 -20
  34. data/spec/fixtures/client.unsigned.crt +22 -0
  35. data/spec/fixtures/server.crt +80 -20
  36. data/spec/reel/connection_spec.rb +50 -11
  37. data/spec/reel/{server_spec.rb → http_server_spec.rb} +1 -1
  38. data/spec/reel/https_server_spec.rb +119 -0
  39. data/spec/reel/{response_writer_spec.rb → response/writer_spec.rb} +10 -2
  40. data/spec/reel/websocket_spec.rb +2 -2
  41. data/spec/spec_helper.rb +3 -34
  42. data/spec/support/example_request.rb +34 -0
  43. metadata +56 -43
  44. data/examples/chunked.rb +0 -25
  45. data/lib/reel/request_body.rb +0 -56
  46. data/lib/reel/request_info.rb +0 -19
  47. data/lib/reel/response_writer.rb +0 -76
  48. data/lib/reel/ssl_server.rb +0 -41
  49. 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
@@ -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
@@ -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
@@ -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
@@ -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