reel 0.5.0 → 0.6.0.pre1
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/.rspec +0 -1
- data/.travis.yml +3 -4
- data/CHANGES.md +25 -5
- data/Gemfile +3 -3
- data/Guardfile +13 -23
- data/examples/roundtrip.rb +15 -5
- data/lib/reel.rb +1 -0
- data/lib/reel/connection.rb +4 -2
- data/lib/reel/request.rb +2 -2
- data/lib/reel/request/body.rb +4 -0
- data/lib/reel/request/info.rb +7 -2
- data/lib/reel/request/parser.rb +5 -1
- data/lib/reel/response.rb +2 -2
- data/lib/reel/response/writer.rb +1 -1
- data/lib/reel/server.rb +21 -10
- data/lib/reel/server/http.rb +1 -1
- data/lib/reel/server/https.rb +6 -18
- data/lib/reel/server/unix.rb +18 -0
- data/lib/reel/spy.rb +2 -1
- data/lib/reel/stream.rb +2 -2
- data/lib/reel/version.rb +2 -2
- data/lib/reel/websocket.rb +77 -44
- data/reel.gemspec +4 -1
- data/spec/reel/connection_spec.rb +85 -66
- data/spec/reel/http_server_spec.rb +8 -11
- data/spec/reel/https_server_spec.rb +22 -33
- data/spec/reel/response/writer_spec.rb +1 -1
- data/spec/reel/response_spec.rb +2 -2
- data/spec/reel/unix_server_spec.rb +41 -0
- data/spec/reel/websocket_spec.rb +95 -20
- data/spec/spec_helper.rb +26 -4
- data/spec/support/create_certs.rb +73 -0
- metadata +65 -43
- data/spec/fixtures/ca.crt +0 -27
- data/spec/fixtures/ca.key +0 -27
- data/spec/fixtures/client.crt +0 -83
- data/spec/fixtures/client.key +0 -27
- data/spec/fixtures/client.unsigned.crt +0 -22
- data/spec/fixtures/server.crt +0 -82
- data/spec/fixtures/server.key +0 -27
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'net/http'
|
3
3
|
|
4
|
-
describe Reel::Server::HTTP do
|
4
|
+
RSpec.describe Reel::Server::HTTP do
|
5
5
|
let(:endpoint) { URI(example_url) }
|
6
6
|
let(:response_body) { "ohai thar" }
|
7
7
|
|
@@ -11,18 +11,16 @@ describe Reel::Server::HTTP do
|
|
11
11
|
handler = proc do |connection|
|
12
12
|
begin
|
13
13
|
request = connection.request
|
14
|
-
request.method.
|
15
|
-
request.version.
|
16
|
-
request.url.
|
17
|
-
|
14
|
+
expect(request.method).to eq 'GET'
|
15
|
+
expect(request.version).to eq "1.1"
|
16
|
+
expect(request.url).to eq example_path
|
18
17
|
connection.respond :ok, response_body
|
19
|
-
rescue => ex
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
23
21
|
with_reel(handler) do
|
24
22
|
response = Net::HTTP.get endpoint
|
25
|
-
response.
|
23
|
+
expect(response).to eq response_body
|
26
24
|
end
|
27
25
|
|
28
26
|
raise ex if ex
|
@@ -34,9 +32,8 @@ describe Reel::Server::HTTP do
|
|
34
32
|
handler = proc do |connection|
|
35
33
|
begin
|
36
34
|
request = connection.request
|
37
|
-
request.method.
|
35
|
+
expect(request.method).to eq 'POST'
|
38
36
|
connection.respond :ok, request.body.to_s
|
39
|
-
rescue => ex
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
@@ -46,8 +43,8 @@ describe Reel::Server::HTTP do
|
|
46
43
|
request['connection'] = 'close'
|
47
44
|
request.body = response_body
|
48
45
|
response = http.request(request)
|
49
|
-
response.
|
50
|
-
response.body.
|
46
|
+
expect(response).to be_a Net::HTTPOK
|
47
|
+
expect(response.body).to eq(response_body)
|
51
48
|
end
|
52
49
|
|
53
50
|
raise ex if ex
|
@@ -1,19 +1,20 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'net/http'
|
3
3
|
|
4
|
-
describe Reel::Server::HTTPS do
|
4
|
+
RSpec.describe Reel::Server::HTTPS do
|
5
|
+
|
5
6
|
let(:example_https_port) { example_port + 1 }
|
6
7
|
let(:example_url) { "https://#{example_addr}:#{example_https_port}#{example_path}" }
|
7
8
|
let(:endpoint) { URI(example_url) }
|
8
9
|
let(:response_body) { "ohai thar" }
|
9
10
|
|
10
|
-
let(:ca_file)
|
11
|
+
let(:ca_file) { certs_dir.join('ca.crt').to_s }
|
11
12
|
|
12
|
-
let(:server_cert) {
|
13
|
-
let(:server_key) {
|
14
|
-
let(:client_cert) {
|
15
|
-
let(:client_cert_unsigned) {
|
16
|
-
let(:client_key) {
|
13
|
+
let(:server_cert) { certs_dir.join("server.crt") .read }
|
14
|
+
let(:server_key) { certs_dir.join("server.key") .read }
|
15
|
+
let(:client_cert) { certs_dir.join("client.crt") .read }
|
16
|
+
let(:client_cert_unsigned) { certs_dir.join("client.unsigned.crt").read }
|
17
|
+
let(:client_key) { certs_dir.join("client.key") .read }
|
17
18
|
|
18
19
|
it "receives HTTP requests and sends responses" do
|
19
20
|
ex = nil
|
@@ -21,12 +22,10 @@ describe Reel::Server::HTTPS do
|
|
21
22
|
handler = proc do |connection|
|
22
23
|
begin
|
23
24
|
request = connection.request
|
24
|
-
request.method.
|
25
|
-
request.version.
|
26
|
-
request.url.
|
27
|
-
|
25
|
+
expect(request.method).to eq 'GET'
|
26
|
+
expect(request.version).to eq "1.1"
|
27
|
+
expect(request.url).to eq example_path
|
28
28
|
connection.respond :ok, response_body
|
29
|
-
rescue => ex
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
@@ -34,28 +33,24 @@ describe Reel::Server::HTTPS do
|
|
34
33
|
http = Net::HTTP.new(endpoint.host, endpoint.port)
|
35
34
|
http.use_ssl = true
|
36
35
|
http.ca_file = self.ca_file
|
37
|
-
|
38
36
|
request = Net::HTTP::Get.new(endpoint.path)
|
39
37
|
response = http.request(request)
|
40
|
-
|
41
|
-
response.body.should eq response_body
|
38
|
+
expect(response.body).to eq response_body
|
42
39
|
end
|
43
40
|
|
44
41
|
raise ex if ex
|
45
42
|
end
|
46
43
|
|
47
|
-
it
|
44
|
+
it "verifies client SSL certs when provided with a CA" do
|
48
45
|
ex = nil
|
49
46
|
|
50
47
|
handler = proc do |connection|
|
51
48
|
begin
|
52
49
|
request = connection.request
|
53
|
-
request.method.
|
54
|
-
request.version.
|
55
|
-
request.url.
|
56
|
-
|
50
|
+
expect(request.method).to eq 'GET'
|
51
|
+
expect(request.version).to eq '1.1'
|
52
|
+
expect(request.url).to eq example_path
|
57
53
|
connection.respond :ok, response_body
|
58
|
-
rescue => ex
|
59
54
|
end
|
60
55
|
end
|
61
56
|
|
@@ -65,28 +60,24 @@ describe Reel::Server::HTTPS do
|
|
65
60
|
http.ca_file = self.ca_file
|
66
61
|
http.cert = OpenSSL::X509::Certificate.new self.client_cert
|
67
62
|
http.key = OpenSSL::PKey::RSA.new self.client_key
|
68
|
-
|
69
63
|
request = Net::HTTP::Get.new(endpoint.path)
|
70
64
|
response = http.request(request)
|
71
|
-
|
72
|
-
response.body.should eq response_body
|
65
|
+
expect(response.body).to eq response_body
|
73
66
|
end
|
74
67
|
|
75
68
|
raise ex if ex
|
76
69
|
end
|
77
70
|
|
78
|
-
it
|
71
|
+
it "fails to verify client certificates that aren't signed" do
|
79
72
|
ex = nil
|
80
73
|
|
81
74
|
handler = proc do |connection|
|
82
75
|
begin
|
83
76
|
request = connection.request
|
84
|
-
request.method.
|
85
|
-
request.version.
|
86
|
-
request.url.
|
87
|
-
|
77
|
+
expect(request.method).to eq 'GET'
|
78
|
+
expect(request.version).to eq '1.1'
|
79
|
+
expect(request.url).to eq example_path
|
88
80
|
connection.respond :ok, response_body
|
89
|
-
rescue => ex
|
90
81
|
end
|
91
82
|
end
|
92
83
|
|
@@ -96,10 +87,8 @@ describe Reel::Server::HTTPS do
|
|
96
87
|
http.ca_file = self.ca_file
|
97
88
|
http.cert = OpenSSL::X509::Certificate.new self.client_cert_unsigned
|
98
89
|
http.key = OpenSSL::PKey::RSA.new self.client_key
|
99
|
-
|
100
90
|
request = Net::HTTP::Get.new(endpoint.path)
|
101
|
-
|
102
|
-
proc { http.request(request) }.should raise_error(OpenSSL::SSL::SSLError)
|
91
|
+
expect { http.request(request) }.to raise_error(OpenSSL::SSL::SSLError)
|
103
92
|
end
|
104
93
|
|
105
94
|
raise ex if ex
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Reel::Response::Writer do
|
3
|
+
RSpec.describe Reel::Response::Writer do
|
4
4
|
let(:fixture_path) { File.expand_path("../../../fixtures/example.txt", __FILE__) }
|
5
5
|
let(:expected_response) { "HTTP/1.1 200 OK\r\nContent-Length: 56\r\n\r\n#{File.read(fixture_path)}" }
|
6
6
|
|
data/spec/reel/response_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Reel::Response do
|
3
|
+
RSpec.describe Reel::Response do
|
4
4
|
it "streams enumerables" do
|
5
5
|
with_socket_pair do |client, peer|
|
6
6
|
connection = Reel::Connection.new(peer)
|
@@ -13,7 +13,7 @@ describe Reel::Response do
|
|
13
13
|
response = client.read(4096)
|
14
14
|
crlf = Reel::Response::Writer::CRLF
|
15
15
|
fixture = "5#{crlf}Hello#{crlf}5#{crlf}World#{crlf}0#{crlf*2}"
|
16
|
-
response[(response.length - fixture.length)..-1].
|
16
|
+
expect(response[(response.length - fixture.length)..-1]).to eq fixture
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
unless defined?(JRUBY_VERSION)
|
5
|
+
|
6
|
+
RSpec.describe Reel::Server::UNIX do
|
7
|
+
let(:endpoint) { URI(example_url) }
|
8
|
+
let(:response_body) { "ohai thar" }
|
9
|
+
|
10
|
+
it 'allows connections over UNIX sockets' do
|
11
|
+
ex = nil
|
12
|
+
|
13
|
+
handler = proc do |connection|
|
14
|
+
begin
|
15
|
+
request = connection.request
|
16
|
+
expect( request.method ).to eq 'GET'
|
17
|
+
connection.respond :ok, self.response_body
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Dir::Tmpname.create('reel-sock') do |path|
|
22
|
+
begin
|
23
|
+
server = Reel::Server::UNIX.new(path, &handler)
|
24
|
+
sock = Net::BufferedIO.new Celluloid::IO::UNIXSocket.new(path)
|
25
|
+
request = Net::HTTP::Get.new('/')
|
26
|
+
|
27
|
+
request.exec(sock, '1.1', path)
|
28
|
+
response = Net::HTTPResponse.read_new(sock)
|
29
|
+
response.reading_body(sock, request.response_body_permitted?) { }
|
30
|
+
|
31
|
+
expect(response.body).to eq(self.response_body)
|
32
|
+
ensure
|
33
|
+
server.terminate if server && server.alive?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise ex if ex
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/spec/reel/websocket_spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'websocket_parser'
|
2
3
|
|
3
|
-
describe Reel::WebSocket do
|
4
|
+
RSpec.describe Reel::WebSocket do
|
4
5
|
include WebSocketHelpers
|
5
6
|
|
6
7
|
let(:example_message) { "Hello, World!" }
|
@@ -12,12 +13,12 @@ describe Reel::WebSocket do
|
|
12
13
|
client << handshake.to_data
|
13
14
|
|
14
15
|
request = connection.request
|
15
|
-
request.
|
16
|
+
expect(request).to be_websocket
|
16
17
|
|
17
18
|
websocket = request.websocket
|
18
|
-
websocket.
|
19
|
+
expect(websocket).to be_a Reel::WebSocket
|
19
20
|
|
20
|
-
handshake.errors.
|
21
|
+
expect(handshake.errors).to be_empty
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -27,30 +28,80 @@ describe Reel::WebSocket do
|
|
27
28
|
client << handshake.to_data
|
28
29
|
|
29
30
|
websocket = connection.request.websocket
|
30
|
-
websocket.
|
31
|
+
expect(websocket).to be_a Reel::WebSocket
|
31
32
|
expect { connection.close }.to raise_error(Reel::StateError)
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
36
|
it "knows its URL" do
|
36
37
|
with_websocket_pair do |_, websocket|
|
37
|
-
websocket.url.
|
38
|
+
expect(websocket.url).to eq(example_path)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
it "knows its headers" do
|
42
43
|
with_websocket_pair do |_, websocket|
|
43
|
-
websocket['Host'].
|
44
|
+
expect(websocket['Host']).to eq(example_host)
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
47
48
|
it "reads frames" do
|
48
49
|
with_websocket_pair do |client, websocket|
|
49
|
-
|
50
|
-
|
50
|
+
message = WebSocket::Message.new(example_message)
|
51
|
+
message.mask!
|
52
|
+
next_message = WebSocket::Message.new(another_message)
|
53
|
+
next_message.mask!
|
54
|
+
client << message.to_data
|
55
|
+
client << next_message.to_data
|
56
|
+
|
57
|
+
expect(websocket.read).to eq(example_message)
|
58
|
+
expect(websocket.read).to eq(another_message)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "WebSocket#next_message" do
|
63
|
+
it "triggers on the next sent message" do
|
64
|
+
with_websocket_pair do |client, websocket|
|
65
|
+
f = Celluloid::Future.new
|
66
|
+
websocket.on_message do |message|
|
67
|
+
f << Celluloid::SuccessResponse.new(:on_message, message)
|
68
|
+
end
|
69
|
+
|
70
|
+
message = WebSocket::Message.new(example_message)
|
71
|
+
message.mask!
|
72
|
+
client << message.to_data
|
73
|
+
websocket.read
|
74
|
+
|
75
|
+
message = f.value
|
76
|
+
expect(message).to eq(example_message)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
51
80
|
|
52
|
-
|
53
|
-
|
81
|
+
describe "WebSocket#read_every" do
|
82
|
+
it "automatically executes read" do
|
83
|
+
with_websocket_pair do |client, websocket|
|
84
|
+
class MyActor
|
85
|
+
include Celluloid
|
86
|
+
|
87
|
+
def initialize(websocket)
|
88
|
+
websocket.read_every 0.1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
f = Celluloid::Future.new
|
93
|
+
websocket.on_message do |message|
|
94
|
+
f << Celluloid::SuccessResponse.new(:on_message, message)
|
95
|
+
end
|
96
|
+
|
97
|
+
message = WebSocket::Message.new(example_message)
|
98
|
+
message.mask!
|
99
|
+
client << message.to_data
|
100
|
+
MyActor.new(websocket)
|
101
|
+
|
102
|
+
message = f.value
|
103
|
+
expect(message).to eq(example_message)
|
104
|
+
end
|
54
105
|
end
|
55
106
|
end
|
56
107
|
|
@@ -62,18 +113,27 @@ describe Reel::WebSocket do
|
|
62
113
|
parser = WebSocket::Parser.new
|
63
114
|
|
64
115
|
parser.append client.readpartial(4096) until first_message = parser.next_message
|
65
|
-
first_message.
|
116
|
+
expect(first_message).to eq(example_message)
|
66
117
|
|
67
118
|
parser.append client.readpartial(4096) until next_message = parser.next_message
|
68
|
-
next_message.
|
119
|
+
expect(next_message).to eq(another_message)
|
69
120
|
end
|
70
121
|
end
|
71
122
|
|
72
123
|
it "closes" do
|
73
124
|
with_websocket_pair do |_, websocket|
|
74
|
-
websocket.
|
125
|
+
expect(websocket).not_to be_closed
|
75
126
|
websocket.close
|
76
|
-
websocket.
|
127
|
+
expect(websocket).to be_closed
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
it "exposes addr and peeraddr" do
|
132
|
+
with_websocket_pair do |client, websocket|
|
133
|
+
expect(websocket).to respond_to(:peeraddr)
|
134
|
+
expect(websocket.peeraddr.first).to eq "AF_INET"
|
135
|
+
expect(websocket).to respond_to(:addr)
|
136
|
+
expect(websocket.addr.first).to eq "AF_INET"
|
77
137
|
end
|
78
138
|
end
|
79
139
|
|
@@ -85,12 +145,27 @@ describe Reel::WebSocket do
|
|
85
145
|
remote_host = connection.remote_host
|
86
146
|
|
87
147
|
request = connection.request
|
88
|
-
request.
|
148
|
+
expect(request).to be_websocket
|
89
149
|
websocket = request.websocket
|
90
|
-
websocket.
|
150
|
+
expect(websocket).to be_a Reel::WebSocket
|
91
151
|
|
92
152
|
expect { connection.remote_host }.to raise_error(Reel::StateError)
|
93
|
-
websocket.remote_host.
|
153
|
+
expect(websocket.remote_host).to eq(remote_host)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it "performs websocket handshakes with header key case-insensitivity" do
|
158
|
+
with_socket_pair do |client, peer|
|
159
|
+
connection = Reel::Connection.new(peer)
|
160
|
+
client << case_handshake.to_data
|
161
|
+
|
162
|
+
request = connection.request
|
163
|
+
expect(request).to be_websocket
|
164
|
+
|
165
|
+
websocket = request.websocket
|
166
|
+
expect(websocket).to be_a Reel::WebSocket
|
167
|
+
|
168
|
+
expect(case_handshake.errors).to be_empty
|
94
169
|
end
|
95
170
|
end
|
96
171
|
|
@@ -100,9 +175,9 @@ describe Reel::WebSocket do
|
|
100
175
|
client << handshake.to_data
|
101
176
|
request = connection.request
|
102
177
|
|
103
|
-
request.
|
178
|
+
expect(request).to be_websocket
|
104
179
|
websocket = request.websocket
|
105
|
-
websocket.
|
180
|
+
expect(websocket).to be_a Reel::WebSocket
|
106
181
|
|
107
182
|
# Discard handshake
|
108
183
|
client.readpartial(4096)
|
data/spec/spec_helper.rb
CHANGED
@@ -5,15 +5,22 @@ require 'bundler/setup'
|
|
5
5
|
require 'reel'
|
6
6
|
require 'pry'
|
7
7
|
|
8
|
+
def fixture_dir
|
9
|
+
Pathname.new File.expand_path("../fixtures", __FILE__)
|
10
|
+
end
|
11
|
+
|
12
|
+
def certs_dir
|
13
|
+
Pathname.new File.expand_path('../../tmp/certs', __FILE__)
|
14
|
+
end
|
15
|
+
|
8
16
|
require 'support/example_request'
|
17
|
+
require 'support/create_certs'
|
18
|
+
|
19
|
+
RSpec.configure(&:disable_monkey_patching!)
|
9
20
|
|
10
21
|
logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a')
|
11
22
|
Celluloid.logger = Logger.new(logfile)
|
12
23
|
|
13
|
-
def fixture_dir
|
14
|
-
Pathname.new File.expand_path("../fixtures", __FILE__)
|
15
|
-
end
|
16
|
-
|
17
24
|
def example_addr; '127.0.0.1'; end
|
18
25
|
def example_port; 1234; end
|
19
26
|
def example_path; "/example"; end
|
@@ -61,7 +68,22 @@ module WebSocketHelpers
|
|
61
68
|
}
|
62
69
|
end
|
63
70
|
|
71
|
+
let :case_handshake_headers do
|
72
|
+
{
|
73
|
+
"HoSt" => example_host,
|
74
|
+
"UpgRAde" => "websocket",
|
75
|
+
"ConnECTion" => "Upgrade",
|
76
|
+
"Sec-WebsOCket-Key" => "dGhlIHNhbXBsZSBub25jZQ==",
|
77
|
+
"Origin" => "http://example.com",
|
78
|
+
"Sec-WEBsOCKET-pROTOCol" => "chat, superchat",
|
79
|
+
"Sec-WEBsOCKET-vERsion" => "13"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
64
83
|
let(:handshake) { WebSocket::ClientHandshake.new(:get, example_url, handshake_headers) }
|
84
|
+
let(:case_handshake) do
|
85
|
+
WebSocket::ClientHandshake.new(:get, example_url, case_handshake_headers)
|
86
|
+
end
|
65
87
|
end
|
66
88
|
end
|
67
89
|
end
|