reel 0.4.0.pre → 0.4.0.pre2
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 +11 -3
- data/Gemfile +4 -0
- data/Guardfile +24 -0
- data/README.md +26 -16
- data/examples/ssl_hello_world.rb +27 -0
- data/examples/websocket_rack.sh +1 -1
- data/examples/websockets.rb +15 -5
- data/lib/rack/handler/reel.rb +1 -0
- data/lib/reel.rb +12 -1
- data/lib/reel/app.rb +1 -1
- data/lib/reel/connection.rb +59 -56
- data/lib/reel/mixins.rb +13 -9
- data/lib/reel/rack_worker.rb +3 -3
- data/lib/reel/request.rb +72 -34
- data/lib/reel/request_body.rb +56 -0
- data/lib/reel/request_info.rb +27 -0
- data/lib/reel/request_parser.rb +42 -30
- data/lib/reel/response.rb +5 -49
- data/lib/reel/response_writer.rb +69 -0
- data/lib/reel/server.rb +6 -6
- data/lib/reel/ssl_server.rb +39 -0
- data/lib/reel/stream.rb +5 -6
- data/lib/reel/version.rb +1 -1
- data/lib/reel/websocket.rb +6 -4
- data/reel.gemspec +4 -3
- data/spec/fixtures/client.crt +22 -0
- data/spec/fixtures/client.key +27 -0
- data/spec/fixtures/server.crt +22 -0
- data/spec/fixtures/server.key +27 -0
- data/spec/reel/connection_spec.rb +233 -7
- data/spec/reel/response_spec.rb +1 -1
- data/spec/reel/server_spec.rb +1 -1
- data/spec/reel/ssl_server_spec.rb +54 -0
- data/spec/reel/websocket_spec.rb +34 -2
- data/spec/spec_helper.rb +11 -5
- metadata +38 -8
@@ -0,0 +1,69 @@
|
|
1
|
+
module Reel
|
2
|
+
class Response
|
3
|
+
class Writer
|
4
|
+
|
5
|
+
CRLF = "\r\n"
|
6
|
+
def initialize(socket, connection)
|
7
|
+
@socket = socket
|
8
|
+
@connection = connection
|
9
|
+
end
|
10
|
+
|
11
|
+
# Write body chunks directly to the connection
|
12
|
+
def write(chunk)
|
13
|
+
chunk_header = chunk.bytesize.to_s(16)
|
14
|
+
@socket << chunk_header + CRLF
|
15
|
+
@socket << chunk + CRLF
|
16
|
+
end
|
17
|
+
|
18
|
+
# Finish the response and reset the response state to header
|
19
|
+
def finish_response
|
20
|
+
@socket << "0#{CRLF * 2}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Takes a Reel::Response and renders it
|
24
|
+
# back over the socket.
|
25
|
+
def handle_response(response)
|
26
|
+
@socket << render_header(response)
|
27
|
+
if response.respond_to?(:render)
|
28
|
+
response.render(@socket)
|
29
|
+
else
|
30
|
+
case response.body
|
31
|
+
when String
|
32
|
+
@socket << response.body
|
33
|
+
when IO
|
34
|
+
begin
|
35
|
+
if !defined?(JRUBY_VERSION)
|
36
|
+
IO.copy_stream(response.body, @socket)
|
37
|
+
else
|
38
|
+
# JRuby 1.6.7 doesn't support IO.copy_stream :(
|
39
|
+
while data = response.body.read(4096)
|
40
|
+
@socket << data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
response.body.close
|
45
|
+
end
|
46
|
+
when Enumerable
|
47
|
+
response.body.each do |chunk|
|
48
|
+
write(chunk)
|
49
|
+
end
|
50
|
+
|
51
|
+
finish_response
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Convert headers into a string
|
57
|
+
def render_header(response)
|
58
|
+
response_header = "#{response.version} #{response.status} #{response.reason}#{CRLF}"
|
59
|
+
unless response.headers.empty?
|
60
|
+
response_header << response.headers.map do |header, value|
|
61
|
+
"#{header}: #{value}"
|
62
|
+
end.join(CRLF) << CRLF
|
63
|
+
end
|
64
|
+
response_header << CRLF
|
65
|
+
end
|
66
|
+
private :render_header
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/reel/server.rb
CHANGED
@@ -5,20 +5,19 @@ module Reel
|
|
5
5
|
# How many connections to backlog in the TCP accept queue
|
6
6
|
DEFAULT_BACKLOG = 100
|
7
7
|
|
8
|
-
|
9
|
-
finalizer :
|
8
|
+
execute_block_on_receiver :initialize
|
9
|
+
finalizer :shutdown
|
10
10
|
|
11
11
|
def initialize(host, port, backlog = DEFAULT_BACKLOG, &callback)
|
12
12
|
# This is actually an evented Celluloid::IO::TCPServer
|
13
13
|
@server = TCPServer.new(host, port)
|
14
|
+
@server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
14
15
|
@server.listen(backlog)
|
15
16
|
@callback = callback
|
16
17
|
async.run
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
def finalize
|
20
|
+
def shutdown
|
22
21
|
@server.close if @server
|
23
22
|
end
|
24
23
|
|
@@ -28,8 +27,9 @@ module Reel
|
|
28
27
|
|
29
28
|
def handle_connection(socket)
|
30
29
|
connection = Connection.new(socket)
|
30
|
+
|
31
31
|
begin
|
32
|
-
@callback
|
32
|
+
@callback.call(connection)
|
33
33
|
ensure
|
34
34
|
if connection.attached?
|
35
35
|
connection.close rescue nil
|
@@ -0,0 +1,39 @@
|
|
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
|
+
# FIXME: VERY VERY VERY VERY BAD RELEASE BLOCKER BAD
|
16
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
17
|
+
|
18
|
+
@tcpserver = Celluloid::IO::TCPServer.new(host, port)
|
19
|
+
@server = Celluloid::IO::SSLServer.new(@tcpserver, ssl_context)
|
20
|
+
@server.listen(backlog)
|
21
|
+
@callback = callback
|
22
|
+
|
23
|
+
async.run
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
loop do
|
28
|
+
begin
|
29
|
+
socket = @server.accept
|
30
|
+
rescue OpenSSL::SSL::SSLError
|
31
|
+
# TODO: log this?
|
32
|
+
retry
|
33
|
+
end
|
34
|
+
|
35
|
+
async.handle_connection socket
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/reel/stream.rb
CHANGED
@@ -31,7 +31,7 @@ module Reel
|
|
31
31
|
@socket.closed?
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
class EventStream < Stream
|
36
36
|
|
37
37
|
# EventSource-related helpers
|
@@ -60,12 +60,12 @@ module Reel
|
|
60
60
|
end
|
61
61
|
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
class ChunkStream < Stream
|
65
65
|
def write(chunk)
|
66
66
|
chunk_header = chunk.bytesize.to_s(16)
|
67
|
-
write chunk_header + Response::CRLF
|
68
|
-
write chunk + Response::CRLF
|
67
|
+
write chunk_header + Response::Writer::CRLF
|
68
|
+
write chunk + Response::Writer::CRLF
|
69
69
|
self
|
70
70
|
end
|
71
71
|
alias :<< :write
|
@@ -73,7 +73,7 @@ module Reel
|
|
73
73
|
# finish does not actually close the socket,
|
74
74
|
# it only inform the browser there are no more messages
|
75
75
|
def finish
|
76
|
-
write "0#{Response::CRLF * 2}"
|
76
|
+
write "0#{Response::Writer::CRLF * 2}"
|
77
77
|
end
|
78
78
|
|
79
79
|
def close
|
@@ -107,7 +107,6 @@ module Reel
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def render(socket)
|
110
|
-
socket << render_header
|
111
110
|
@body.call socket
|
112
111
|
end
|
113
112
|
end
|
data/lib/reel/version.rb
CHANGED
data/lib/reel/websocket.rb
CHANGED
@@ -7,10 +7,12 @@ module Reel
|
|
7
7
|
include ConnectionMixin
|
8
8
|
include RequestMixin
|
9
9
|
|
10
|
+
attr_reader :socket
|
10
11
|
def_delegators :@socket, :addr, :peeraddr
|
11
12
|
|
12
|
-
def initialize(
|
13
|
-
@
|
13
|
+
def initialize(info, socket)
|
14
|
+
@request_info = info
|
15
|
+
@socket = socket
|
14
16
|
|
15
17
|
handshake = ::WebSocket::ClientHandshake.new(:get, url, headers)
|
16
18
|
|
@@ -36,8 +38,8 @@ module Reel
|
|
36
38
|
close
|
37
39
|
end
|
38
40
|
|
39
|
-
@parser.on_ping do
|
40
|
-
@socket << ::WebSocket::Message.pong.to_data
|
41
|
+
@parser.on_ping do |payload|
|
42
|
+
@socket << ::WebSocket::Message.pong(payload).to_data
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
data/reel.gemspec
CHANGED
@@ -15,10 +15,11 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Reel::VERSION
|
17
17
|
|
18
|
-
gem.add_runtime_dependency 'celluloid
|
18
|
+
gem.add_runtime_dependency 'celluloid', '>= 0.14.1'
|
19
|
+
gem.add_runtime_dependency 'celluloid-io', '>= 0.14.1'
|
19
20
|
gem.add_runtime_dependency 'http', '>= 0.2.0'
|
20
|
-
gem.add_runtime_dependency 'http_parser.rb', '>= 0.
|
21
|
-
gem.add_runtime_dependency 'websocket_parser', '>= 0.1.
|
21
|
+
gem.add_runtime_dependency 'http_parser.rb', '>= 0.6.0.beta.2'
|
22
|
+
gem.add_runtime_dependency 'websocket_parser', '>= 0.1.4'
|
22
23
|
gem.add_runtime_dependency 'rack', '>= 1.4.0'
|
23
24
|
|
24
25
|
gem.add_development_dependency 'rake'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDmjCCAoICCQDwZ8yE/0n4PjANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMC
|
3
|
+
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
|
4
|
+
EjAQBgNVBAoTCUNlbGx1bG9pZDEbMBkGA1UEAxMSY2xpZW50LmV4YW1wbGUuY29t
|
5
|
+
MSEwHwYJKoZIhvcNAQkBFhJjbGllbnRAZXhhbXBsZS5jb20wHhcNMTIxMTI1MTkx
|
6
|
+
NjI2WhcNMjIxMTIzMTkxNjI2WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
|
7
|
+
bGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUNlbGx1
|
8
|
+
bG9pZDEbMBkGA1UEAxMSY2xpZW50LmV4YW1wbGUuY29tMSEwHwYJKoZIhvcNAQkB
|
9
|
+
FhJjbGllbnRAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
10
|
+
AoIBAQDV6zzpkAIX4FijnytX84GgHb8hYdNJAn+9g57XGrqtAH6BlLoANFl4n/+y
|
11
|
+
nEQwBqlrNnfstPrf7sPezytntSVyufSE+LGUGBJA/jyjQCMcEe8+4bfOC2ZhCpvn
|
12
|
+
I2dKNKwsmM+DyWs/PVl7XEAZF2P4iQ8eGLVFjph+KA/D79cHkIeGt4FEA4xJWqKf
|
13
|
+
+Kjftxo1cBqLx2dUiucRL7tva3ingAqYSs/i82jKLGlj7fdRMytOx87Nhs35RWpu
|
14
|
+
66l7hvpetx3t2wU2obKOzKhS4ycaZ2AptEDNXKaBTQ5lejSRxFBCpYQtqmkd0bMG
|
15
|
+
/T5ZfXC45axj9a2rj8AKZct+mLCzAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAHzr
|
16
|
+
b4VTktAi+8baGRJCXupt0Ari8ffoWhsYerELFLQF7K2sluxOqCsGEEF21e99fZxP
|
17
|
+
lisLi0DIQ7cNlOGjRJ3xaydE74Fsry3xBNKoR8I7OMr9VFsrC54tc0x7NQ7bRHy6
|
18
|
+
kCjSwKN4I2KWJjQ8yf8mIalmUKOmb/hirzna8io4CiDeJGZ1XNAQ9dl1RHRW442G
|
19
|
+
GTu2ofAtU8TlzilZyclMY/lN7whw7sKP+pPr6fpAOJZsR64IzbBcWHHjJhx70XOx
|
20
|
+
jnd5FB1oXnuupgPqEKmatSCytrue8GTkanB8VZ6+Zd/4XgTkie3UtCZW8R+NL/Lo
|
21
|
+
us/+Ks3WIDyYdPSPnbE=
|
22
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEA1es86ZACF+BYo58rV/OBoB2/IWHTSQJ/vYOe1xq6rQB+gZS6
|
3
|
+
ADRZeJ//spxEMAapazZ37LT63+7D3s8rZ7Ulcrn0hPixlBgSQP48o0AjHBHvPuG3
|
4
|
+
zgtmYQqb5yNnSjSsLJjPg8lrPz1Ze1xAGRdj+IkPHhi1RY6YfigPw+/XB5CHhreB
|
5
|
+
RAOMSVqin/io37caNXAai8dnVIrnES+7b2t4p4AKmErP4vNoyixpY+33UTMrTsfO
|
6
|
+
zYbN+UVqbuupe4b6Xrcd7dsFNqGyjsyoUuMnGmdgKbRAzVymgU0OZXo0kcRQQqWE
|
7
|
+
LappHdGzBv0+WX1wuOWsY/Wtq4/ACmXLfpiwswIDAQABAoIBAQCFDDcpaUj0ArP+
|
8
|
+
qEuz+x6/MGEk6QwZV7WNcGSFkvlSCoGkJJV+9RBExvao5yo92JbcuNbj4Tg7uOwY
|
9
|
+
EzAC45a0AVZEscz4t/P6emXKf2SW28y6hnbkbxCxAIEwxENE0vfXEP/YDplmjsit
|
10
|
+
whWXxYWHGe/OHz33UhYkONR9YBmUeLrtloRNUV82XDSpn4d7toLKaZW2kOFl4nFR
|
11
|
+
SQ3pDPk1hleG8AZcfnF2LwaPx1XjPwBnXY9FK2jyNupVghfCw/Sv91dbbVkkIG14
|
12
|
+
A8WpZKAXjXXOcTroof5+NJSPXzYrRuvP8K6H2zGj7F/AsP4hiZqGkb4tel0yH5VM
|
13
|
+
oLCUTHqhAoGBAPysxeoT1ytajQ55UV1yjsnQ3jF9YcWsZwPEJgMI+bt+QzAfqqZs
|
14
|
+
Kuvg8Gi7ukbcc2pKwXv+ma9HLJq/pQbWlfxcMNulY0VCPY/ceaPen+EfCJTApVpY
|
15
|
+
YFS25i/JnIp9IudpQBuLHz9Yy4f1W2VoeG/mFqOmUxiTx4LM87G6rdtDAoGBANi7
|
16
|
+
5raiwDS+rD91l5FLq3kdvgSDgYk4hh7BBJNJt8vhJYInIev5eb/Z41X8PfqWa2ir
|
17
|
+
9aanpMYhWDJxbdSQDd3/op6jtOZM7InLceAm2O29VY2+HW5ePpc21AHsqoZpFYEZ
|
18
|
+
YP8xvbSjJzfkrYr4y+aAMXONVAi4afqG7x6GqCXRAoGBAPbzFWu1gHKKyZn/0Bn4
|
19
|
+
wL1WOhM8a7Z6zSPNLSmCODGbMadzC6Ijzb9D1TNHZsOi6dpUvc2mBCZe9aU48N1C
|
20
|
+
FMzUfZvuhJtIJkrYPLp/9tpbLlPUBMfL4Dprl4XVEf34V4i8QT+qNRwAeMukbXMr
|
21
|
+
K6qRwkanZEd9B107WmG2Bf1pAoGACpld1g6tgabVe6D/kY52y0yGD2hy/Ef0Xyqn
|
22
|
+
U6CmSWUwVWYehZDEwHoiYQEd8tRKWmsWb1kBeOMGkijz6xJEa1fmFwYAgca/RpnZ
|
23
|
+
btHXiADbXzwt6kjXnMOEqLdvO3WGJLMeCDzhfyT/dP9M8V/rcNFSGcmOk4KZRDQ3
|
24
|
+
G3IQZRECgYBqHqvxHeL087UHXE1tdAGFVfxOiYE1afksJbkV06VnO8XXr9sNSWwy
|
25
|
+
YjXVY9k6U1BFo7jHrWr6TkeMkB45wyn/fasHKU7qsPt0joRFkXMCzwl376hto3Tg
|
26
|
+
RGXQCA4RUQXkxaDctJ5TgcF7MhK7tAFd1aBVlxwGENLYUVL0ZPaMrw==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,22 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDmjCCAoICCQD+dJ16wNIKnzANBgkqhkiG9w0BAQUFADCBjjELMAkGA1UEBhMC
|
3
|
+
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
|
4
|
+
EjAQBgNVBAoTCUNlbGx1bG9pZDEbMBkGA1UEAxMSc2VydmVyLmV4YW1wbGUuY29t
|
5
|
+
MSEwHwYJKoZIhvcNAQkBFhJzZXJ2ZXJAZXhhbXBsZS5jb20wHhcNMTIxMTI1MTkx
|
6
|
+
NjAwWhcNMjIxMTIzMTkxNjAwWjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
|
7
|
+
bGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUNlbGx1
|
8
|
+
bG9pZDEbMBkGA1UEAxMSc2VydmVyLmV4YW1wbGUuY29tMSEwHwYJKoZIhvcNAQkB
|
9
|
+
FhJzZXJ2ZXJAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
10
|
+
AoIBAQCpLs3Xg00RtoG4+SKaaNfw6Dve9d0Kkg0CNfU8AsxOgTIU1Qu8s+bzKkqe
|
11
|
+
66NfCa6T8VPpk9VbIlF2ONdgY4o8muV1q+mS6j2HDAtWPiDjP+9YOwGf/DT3LhSb
|
12
|
+
g8k+alL2cqe7B1XNUsNFEvQ+yQLlj9MWKb7nbYDM8aqdv46KGoDj9v9rfm4QiKwI
|
13
|
+
B6u/KoQG22YF7sT4O44jU/u20xcm3rV1Elg0gC/UP/YqnuMPCZYcK/Z9vYGtDJ6G
|
14
|
+
8OYDcPZSZBdkqlffhYBssSj3R7sTCqoh4ms08ukcGycbvUO+cWrPKnmySsGaCYtG
|
15
|
+
kp7QsH1ec7QGA01hZL2yL8CuJMUbAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBABE4
|
16
|
+
gYVSdC87NhpA49k0vcLLU7v7mU3a3no/vu1CIqQxzx8/xh26Qi3aGb1s9MgHpF2Z
|
17
|
+
NiB1irXER2tyz/F3qCi8OCo7eNsndmDjj4GnkBjEPTtqRxH9imRWw4bJyqwqFHcu
|
18
|
+
1kczCZa+2VFQFEL4ErGycPFKM59ppTcJ0IxbK7PCGzO75TRQoAl52+3Aob+oejPP
|
19
|
+
qFbiqNlV1T3EKa5yLdvOC5sLrEcfm3iMxmOtNVzp9OBhjXfm8Q1zgYs4VyJXzLMK
|
20
|
+
wf956w2YEbpTAAzNc53zly/Jhr4MnFsa9Mn1oYp9Rfjzw/qJtXw+a3PtEKrO4XNe
|
21
|
+
TsKHsAkj8XvUrhliiNQ=
|
22
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEogIBAAKCAQEAqS7N14NNEbaBuPkimmjX8Og73vXdCpINAjX1PALMToEyFNUL
|
3
|
+
vLPm8ypKnuujXwmuk/FT6ZPVWyJRdjjXYGOKPJrldavpkuo9hwwLVj4g4z/vWDsB
|
4
|
+
n/w09y4Um4PJPmpS9nKnuwdVzVLDRRL0PskC5Y/TFim+522AzPGqnb+OihqA4/b/
|
5
|
+
a35uEIisCAervyqEBttmBe7E+DuOI1P7ttMXJt61dRJYNIAv1D/2Kp7jDwmWHCv2
|
6
|
+
fb2BrQyehvDmA3D2UmQXZKpX34WAbLEo90e7EwqqIeJrNPLpHBsnG71DvnFqzyp5
|
7
|
+
skrBmgmLRpKe0LB9XnO0BgNNYWS9si/AriTFGwIDAQABAoIBAGKRoV4p4rIqOiQy
|
8
|
+
CuYZpY53T8KUToeFFk0ucMXY/33RqgMXKTJ1Ql50SmuS8GlDs9IALZqOBiWFth6B
|
9
|
+
+YHwHK84s+2+DmUJUnWnH8fMhM7CBknKfyTeBWHqGBmPS6WwvstVe8HtASGSUbCh
|
10
|
+
3WnjJWvoQtzLz6z4UK2XM4ea/ooY+hlcw6DM+jZuTstzLFE/9BPoHueaW8UemjaH
|
11
|
+
ZUXMKm3+I3iIjHszUUWM59bS9pOBn/YvIJbVE5wMIVCP2YXDCgfpV2Z4nDiAHppn
|
12
|
+
fnha2TzHzlPMgwhBpz06r6G8X+A6gJl98TDSK41nIMyXqiZ2aoALL3UOssAMfUHr
|
13
|
+
2y9CGdECgYEA27F1IyUW3JgqCeqZ7eCeT4emhAvyohzM5pzWI7C8ZosF14zFRpek
|
14
|
+
TgmjdTGMQ1EZVVkyj85RyvMm3MkcKOHetc5g2jJg3EkxvAV+PMs7yjpqg3itEjC6
|
15
|
+
vIhXLoXdq+FuruA2h4O0hi6yuf1FCQYtpNLTe49qetjsaWzwwowHwlMCgYEAxSRo
|
16
|
+
k+AdpoNXblnIhd0EaKjGAsHFrC039o7JqQe/mKAiXaGiidIDk5Vt/ChT6Qa6fiLq
|
17
|
+
cdysCn+tSCt/DdRrELZohc0ipuy5/agQmJgWoW7oay8ldzxHP9VevWo4UuqVudW9
|
18
|
+
evhKe0a9uXCrSimvZ5PJk91lmBx92FBeP6Y+qRkCgYAXQsvPQ88O3kGdOSzBJgY9
|
19
|
+
D3TPCGDRT1FWnYaC0uSvysp8jxgYKFgqNxUKhIuAWSbghYg397VrUqFrwRNtNLUa
|
20
|
+
9NYGZE0jJdDRQpeiIjaba+H5N57DjUtISPtKHrxgxYatl2nOoWBM0Mb1sF5N3UyZ
|
21
|
+
5gSkUYQJq8wkQXegcakkpwKBgEdvvgV3vMbN6SyvlB4NzL8wCTCOjtapPBI4A5Mg
|
22
|
+
n6jqvgk3vPI8C9e62jP5WQ6jxYhXlqTT1fOn+F6ihFO6mWFg99ckUl4ygeMMt5bT
|
23
|
+
5b9xtP7CAs2GJjtXUhFJIEfLgZ3pedPJjRPGupEr5qXlHQ5nWzAdlebczC1KUhy2
|
24
|
+
XRZhAoGAGA3SAAF79PK3c3+bOOviXxDFuH5TCBmbtEpJ+/jCbwR6Z8fMRswZJ3Gc
|
25
|
+
l8eNMsB+Kfif+806xAgLxsyhYuyvF6rE/V34GKjW22T1gwk6gY/rOgFn42jo8lwl
|
26
|
+
HFbSB+IG0+Go0m+0QmyRh7SyPvDNtyYzBFFdl9U8JYYGM0Nfgd0=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -33,11 +33,11 @@ describe Reel::Connection do
|
|
33
33
|
request.url.should eq "/"
|
34
34
|
request.version.should eq "1.1"
|
35
35
|
request['Content-Length'].should eq body.length.to_s
|
36
|
-
request.body.should eq example_request.body
|
36
|
+
request.body.to_s.should eq example_request.body
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
it "
|
40
|
+
it "reads requests with large bodies" do
|
41
41
|
with_socket_pair do |client, connection|
|
42
42
|
client << ExampleRequest.new.to_s
|
43
43
|
request = connection.request
|
@@ -53,18 +53,34 @@ describe Reel::Connection do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
it "enumerates requests with #each_request" do
|
57
|
+
with_socket_pair do |client, connection|
|
58
|
+
client << ExampleRequest.new.to_s
|
59
|
+
|
60
|
+
request_count = 0
|
61
|
+
connection.each_request do |request|
|
62
|
+
request_count += 1
|
63
|
+
request.url.should eq "/"
|
64
|
+
request.respond :ok
|
65
|
+
client.close
|
66
|
+
end
|
67
|
+
|
68
|
+
request_count.should eq 1
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
56
72
|
it "streams responses when transfer-encoding is chunked" do
|
57
73
|
with_socket_pair do |client, connection|
|
58
74
|
client << ExampleRequest.new.to_s
|
59
75
|
request = connection.request
|
60
76
|
|
61
77
|
# Sending transfer_encoding chunked without a body enables streaming mode
|
62
|
-
|
78
|
+
request.respond :ok, :transfer_encoding => :chunked
|
63
79
|
|
64
80
|
# This will send individual chunks
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
request << "Hello"
|
82
|
+
request << "World"
|
83
|
+
request.finish_response # Write trailer and reset connection to header mode
|
68
84
|
connection.close
|
69
85
|
|
70
86
|
response = ""
|
@@ -95,7 +111,203 @@ describe Reel::Connection do
|
|
95
111
|
end
|
96
112
|
end
|
97
113
|
|
98
|
-
|
114
|
+
it "raises an error trying to read two pipelines without responding first" do
|
115
|
+
with_socket_pair do |client, connection|
|
116
|
+
2.times do
|
117
|
+
client << ExampleRequest.new.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
lambda{
|
121
|
+
2.times do
|
122
|
+
request = connection.request
|
123
|
+
end
|
124
|
+
}.should raise_error(Reel::Connection::StateError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "reads pipelined requests without bodies" do
|
129
|
+
with_socket_pair do |client, connection|
|
130
|
+
3.times do
|
131
|
+
client << ExampleRequest.new.to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
3.times do
|
135
|
+
request = connection.request
|
136
|
+
|
137
|
+
request.url.should eq "/"
|
138
|
+
request.version.should eq "1.1"
|
139
|
+
|
140
|
+
request['Host'].should eq "www.example.com"
|
141
|
+
request['Connection'].should eq "keep-alive"
|
142
|
+
request['User-Agent'].should eq "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.78 S"
|
143
|
+
request['Accept'].should eq "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
|
144
|
+
request['Accept-Encoding'].should eq "gzip,deflate,sdch"
|
145
|
+
request['Accept-Language'].should eq "en-US,en;q=0.8"
|
146
|
+
request['Accept-Charset'].should eq "ISO-8859-1,utf-8;q=0.7,*;q=0.3"
|
147
|
+
connection.respond :ok, {}, ""
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it "reads pipelined requests with bodies" do
|
153
|
+
with_socket_pair do |client, connection|
|
154
|
+
3.times do |i|
|
155
|
+
body = "Hello, world number #{i}!"
|
156
|
+
example_request = ExampleRequest.new
|
157
|
+
example_request.body = body
|
158
|
+
|
159
|
+
client << example_request.to_s
|
160
|
+
end
|
161
|
+
|
162
|
+
3.times do |i|
|
163
|
+
request = connection.request
|
164
|
+
|
165
|
+
expected_body = "Hello, world number #{i}!"
|
166
|
+
request.url.should eq "/"
|
167
|
+
request.version.should eq "1.1"
|
168
|
+
request['Content-Length'].should eq expected_body.length.to_s
|
169
|
+
request.body.to_s.should eq expected_body
|
170
|
+
|
171
|
+
connection.respond :ok, {}, ""
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
it "reads pipelined requests with streamed bodies" do
|
177
|
+
with_socket_pair(4) do |client, connection|
|
178
|
+
3.times do |i|
|
179
|
+
body = "Hello, world number #{i}!"
|
180
|
+
example_request = ExampleRequest.new
|
181
|
+
example_request.body = body
|
182
|
+
|
183
|
+
client << example_request.to_s
|
184
|
+
end
|
185
|
+
|
186
|
+
3.times do |i|
|
187
|
+
request = connection.request
|
188
|
+
|
189
|
+
expected_body = "Hello, world number #{i}!"
|
190
|
+
request.url.should eq "/"
|
191
|
+
request.version.should eq "1.1"
|
192
|
+
request['Content-Length'].should eq expected_body.length.to_s
|
193
|
+
request.should_not be_finished_reading
|
194
|
+
new_content = ""
|
195
|
+
while chunk = request.body.readpartial(1)
|
196
|
+
new_content << chunk
|
197
|
+
end
|
198
|
+
new_content.should == expected_body
|
199
|
+
request.should be_finished_reading
|
200
|
+
|
201
|
+
connection.respond :ok, {}, ""
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# This test will deadlock rspec waiting unless
|
207
|
+
# connection.request works properly
|
208
|
+
it "does not block waiting for body to read before handling request" do
|
209
|
+
with_socket_pair do |client, connection|
|
210
|
+
example_request = ExampleRequest.new
|
211
|
+
content = "Hi guys! Sorry I'm late to the party."
|
212
|
+
example_request['Content-Length'] = content.length
|
213
|
+
client << example_request.to_s
|
214
|
+
|
215
|
+
request = connection.request
|
216
|
+
request.should be_a(Reel::Request)
|
217
|
+
client << content
|
218
|
+
request.body.to_s.should == content
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it "blocks on read until written" do
|
223
|
+
with_socket_pair do |client, connection|
|
224
|
+
example_request = ExampleRequest.new
|
225
|
+
content = "Hi guys! Sorry I'm late to the party."
|
226
|
+
example_request['Content-Length'] = content.length
|
227
|
+
client << example_request.to_s
|
228
|
+
|
229
|
+
request = connection.request
|
230
|
+
timers = Timers.new
|
231
|
+
timers.after(0.2){
|
232
|
+
client << content
|
233
|
+
}
|
234
|
+
read_body = ""
|
235
|
+
timers.after(0.1){
|
236
|
+
timers.wait # continue timers, the next bit will block waiting for content
|
237
|
+
read_body = request.read(8)
|
238
|
+
}
|
239
|
+
timers.wait
|
240
|
+
|
241
|
+
request.should be_a(Reel::Request)
|
242
|
+
read_body.should == content[0..7]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
it "streams body properly with #read and buffered body" do
|
247
|
+
with_socket_pair do |client, connection|
|
248
|
+
example_request = ExampleRequest.new
|
249
|
+
content = "I'm data you can stream!"
|
250
|
+
example_request['Content-Length'] = content.length
|
251
|
+
client << example_request.to_s
|
252
|
+
|
253
|
+
request = connection.request
|
254
|
+
request.should be_a(Reel::Request)
|
255
|
+
request.should_not be_finished_reading
|
256
|
+
client << content
|
257
|
+
rebuilt = []
|
258
|
+
connection.readpartial(64) # Buffer some body
|
259
|
+
while chunk = request.read(8)
|
260
|
+
rebuilt << chunk
|
261
|
+
end
|
262
|
+
request.should be_finished_reading
|
263
|
+
rebuilt.should == ["I'm data", " you can", " stream!"]
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context "#readpartial" do
|
268
|
+
it "streams request bodies" do
|
269
|
+
with_socket_pair(8) do |client, connection|
|
270
|
+
example_request = ExampleRequest.new
|
271
|
+
content = "I'm data you can stream!"
|
272
|
+
example_request['Content-Length'] = content.length
|
273
|
+
client << example_request.to_s
|
274
|
+
|
275
|
+
request = connection.request
|
276
|
+
request.should be_a(Reel::Request)
|
277
|
+
request.should_not be_finished_reading
|
278
|
+
client << content
|
279
|
+
rebuilt = []
|
280
|
+
while chunk = request.body.readpartial(8)
|
281
|
+
rebuilt << chunk
|
282
|
+
end
|
283
|
+
request.should be_finished_reading
|
284
|
+
rebuilt.should == ["I'm data", " you can", " stream!"]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "#each" do
|
290
|
+
it "streams request bodies" do
|
291
|
+
with_socket_pair(8) do |client, connection|
|
292
|
+
example_request = ExampleRequest.new
|
293
|
+
content = "I'm data you can stream!"
|
294
|
+
example_request['Content-Length'] = content.length
|
295
|
+
client << example_request.to_s
|
296
|
+
|
297
|
+
request = connection.request
|
298
|
+
request.should be_a(Reel::Request)
|
299
|
+
request.should_not be_finished_reading
|
300
|
+
client << content
|
301
|
+
|
302
|
+
data = ""
|
303
|
+
request.body.each { |chunk| data << chunk }
|
304
|
+
request.should be_finished_reading
|
305
|
+
data.should == "I'm data you can stream!"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe "IO#read duck typing" do
|
99
311
|
it "raises an exception if length is a negative value" do
|
100
312
|
with_socket_pair do |client, connection|
|
101
313
|
example_request = ExampleRequest.new
|
@@ -118,6 +330,20 @@ describe Reel::Connection do
|
|
118
330
|
end
|
119
331
|
end
|
120
332
|
|
333
|
+
it "reads to EOF if length is nil, even small buffer" do
|
334
|
+
with_socket_pair(4) do |client, connection|
|
335
|
+
body = "Hello, world!"
|
336
|
+
example_request = ExampleRequest.new
|
337
|
+
example_request.body = body
|
338
|
+
connection.buffer_size.should == 4
|
339
|
+
|
340
|
+
client << example_request.to_s
|
341
|
+
request = connection.request
|
342
|
+
|
343
|
+
request.read.should eq "Hello, world!"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
121
347
|
it "reads to EOF if length is nil" do
|
122
348
|
with_socket_pair do |client, connection|
|
123
349
|
body = "Hello, world!"
|