hatetepe 0.6.0.pre → 0.6.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -4
- data/bin/hatetepe +4 -8
- data/config/flay.yml +2 -2
- data/config/reek.yml +15 -11
- data/lib/hatetepe.rb +1 -1
- data/lib/hatetepe/body.rb +62 -0
- data/lib/hatetepe/client.rb +34 -34
- data/lib/hatetepe/client/keep_alive.rb +4 -49
- data/lib/hatetepe/client/timeouts.rb +3 -3
- data/lib/hatetepe/connection/eventmachine.rb +7 -8
- data/lib/hatetepe/promise.rb +7 -2
- data/lib/hatetepe/request.rb +8 -10
- data/lib/hatetepe/response.rb +5 -7
- data/lib/hatetepe/serializer.rb +2 -3
- data/lib/hatetepe/serializer/encoding.rb +8 -16
- data/lib/hatetepe/server.rb +15 -10
- data/lib/hatetepe/server/keep_alive.rb +26 -31
- data/lib/hatetepe/server/timeouts.rb +3 -3
- data/lib/hatetepe/support/message.rb +25 -3
- data/lib/hatetepe/version.rb +1 -1
- data/spec/integration/keep_alive_spec.rb +18 -24
- data/spec/integration/smoke_spec.rb +2 -2
- data/spec/integration/streaming_spec.rb +9 -13
- data/spec/support/handler.rb +12 -16
- data/spec/support/helper.rb +8 -8
- data/spec/unit/client_spec.rb +25 -24
- data/spec/unit/connection/eventmachine_spec.rb +9 -8
- data/spec/unit/request_spec.rb +6 -6
- data/spec/unit/response_spec.rb +3 -3
- data/spec/unit/server/keep_alive_spec.rb +67 -0
- data/spec/unit/server_spec.rb +13 -9
- data/spec/unit/support/message_spec.rb +5 -2
- metadata +5 -5
- data/lib/hatetepe/support/keep_alive.rb +0 -14
- data/spec/unit/support/keep_alive_spec.rb +0 -52
@@ -11,11 +11,11 @@ describe 'Smoke test' do
|
|
11
11
|
it 'exchanges request and response' do
|
12
12
|
expect(response.status).to be(200)
|
13
13
|
expect(response.headers).to include('Content-Length' => '1')
|
14
|
-
expect(response.body).to
|
14
|
+
expect(response.body.to_s).to eq('/')
|
15
15
|
|
16
16
|
expect(request.http_method).to be(:get)
|
17
17
|
expect(request.uri).to eq('/')
|
18
18
|
expect(request.headers).to include('Content-Length' => '0')
|
19
|
-
expect(request.body).to
|
19
|
+
expect(request.body.to_s).to eq('')
|
20
20
|
end
|
21
21
|
end
|
@@ -5,25 +5,26 @@ require 'spec_helper'
|
|
5
5
|
describe 'Request body streaming' do
|
6
6
|
include_context :server_client_pair
|
7
7
|
|
8
|
-
let(:bodies) { [] }
|
9
8
|
let(:chunks) { [] }
|
10
9
|
|
11
10
|
before do
|
12
11
|
client.perform(request)
|
13
12
|
tick(2)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
Fiber.new do
|
14
|
+
intercepted_requests[0].body.each do |chunk|
|
15
|
+
chunks << chunk
|
16
|
+
end
|
17
|
+
end.resume
|
18
18
|
end
|
19
19
|
|
20
|
-
let(:request) {
|
20
|
+
let(:request) { open_request }
|
21
21
|
|
22
22
|
subject! do
|
23
23
|
%w[foo bar baz].each do |chunk|
|
24
|
-
request.
|
24
|
+
request.body.closed.progress(chunk)
|
25
25
|
tick
|
26
26
|
end
|
27
|
+
request.close
|
27
28
|
tick
|
28
29
|
end
|
29
30
|
|
@@ -31,7 +32,6 @@ describe 'Request body streaming' do
|
|
31
32
|
expect(intercepted_requests[0].headers)
|
32
33
|
.to include('Transfer-Encoding' => 'chunked')
|
33
34
|
|
34
|
-
expect(bodies).to eq(%w[foo foobar foobarbaz])
|
35
35
|
expect(chunks).to eq(%w[foo bar baz])
|
36
36
|
end
|
37
37
|
end
|
@@ -39,7 +39,6 @@ end
|
|
39
39
|
describe 'Response body streaming' do
|
40
40
|
include_context :server_client_pair
|
41
41
|
|
42
|
-
let(:bodies) { [] }
|
43
42
|
let(:chunks) { [] }
|
44
43
|
|
45
44
|
let(:response) { client.request(:get, '/streaming') }
|
@@ -47,15 +46,12 @@ describe 'Response body streaming' do
|
|
47
46
|
subject! { response }
|
48
47
|
|
49
48
|
before do
|
50
|
-
response.
|
51
|
-
bodies << response.body.dup
|
49
|
+
response.body.each do |chunk|
|
52
50
|
chunks << chunk
|
53
51
|
end
|
54
|
-
response.finished.sync
|
55
52
|
end
|
56
53
|
|
57
54
|
it 'progressively receives the body' do
|
58
|
-
expect(bodies).to eq(%w[foo foobar foobarbaz])
|
59
55
|
expect(chunks).to eq(%w[foo bar baz])
|
60
56
|
end
|
61
57
|
end
|
data/spec/support/handler.rb
CHANGED
@@ -4,28 +4,24 @@ class SpecHandler
|
|
4
4
|
def initialize(config, server, connection)
|
5
5
|
end
|
6
6
|
|
7
|
-
def serve(request)
|
8
|
-
|
9
|
-
request.served.fulfill(response)
|
7
|
+
def serve(request, served)
|
8
|
+
served.fulfill(response_for(request))
|
10
9
|
end
|
11
10
|
|
12
11
|
private
|
13
12
|
|
14
13
|
def response_for(request)
|
15
14
|
case request.uri
|
16
|
-
when '/'
|
17
|
-
when '/
|
18
|
-
when '/close' then close_response.tap(&fulfill)
|
15
|
+
when '/sleep' then sleep_response
|
16
|
+
when '/close' then close_response
|
19
17
|
when '/streaming' then streaming_response
|
18
|
+
else echo_response(request)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def root_response
|
28
|
-
Hatetepe::Response.new(200, { 'Content-Type' => 'text/plain' }, '/')
|
22
|
+
def echo_response(request)
|
23
|
+
Hatetepe::Response.new(200, { 'Content-Type' => 'text/plain' },
|
24
|
+
request.uri)
|
29
25
|
end
|
30
26
|
|
31
27
|
def sleep_response
|
@@ -41,14 +37,14 @@ class SpecHandler
|
|
41
37
|
end
|
42
38
|
|
43
39
|
def streaming_response
|
44
|
-
response = Hatetepe::Response.new(200, 'Content-Type' => 'text/plain'
|
40
|
+
response = Hatetepe::Response.new(200, { 'Content-Type' => 'text/plain' },
|
41
|
+
Hatetepe::Body.new)
|
45
42
|
SpecHelper.defer do
|
46
43
|
%w[foo bar baz].each do |chunk|
|
47
44
|
SpecHelper.tick(2)
|
48
|
-
response.body
|
49
|
-
response.finished.progress(chunk)
|
45
|
+
response.body.closed.progress(chunk)
|
50
46
|
end
|
51
|
-
response.
|
47
|
+
response.close
|
52
48
|
end
|
53
49
|
response
|
54
50
|
end
|
data/spec/support/helper.rb
CHANGED
@@ -24,20 +24,20 @@ module SpecHelper
|
|
24
24
|
end
|
25
25
|
module_function :defer
|
26
26
|
|
27
|
-
def
|
28
|
-
Hatetepe::Request.new(http_method, uri, headers)
|
27
|
+
def open_request(http_method = :get, uri = '/', headers = {})
|
28
|
+
Hatetepe::Request.new(http_method, uri, headers, Hatetepe::Body.new)
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
31
|
+
def closed_request(*args)
|
32
|
+
open_request(*args).tap(&:close)
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
36
|
-
Hatetepe::Response.new(status, headers)
|
35
|
+
def open_response(status = 200, headers = {})
|
36
|
+
Hatetepe::Response.new(status, headers, Hatetepe::Body.new)
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
40
|
-
|
39
|
+
def closed_response(*args)
|
40
|
+
open_response(*args).tap(&:close)
|
41
41
|
end
|
42
42
|
|
43
43
|
def localhost
|
data/spec/unit/client_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe Hatetepe::Client do
|
|
15
15
|
close: nil)
|
16
16
|
end
|
17
17
|
let(:handler_class) { double('handler_class', new: handler) }
|
18
|
-
let(:handler) { double('handler',
|
18
|
+
let(:handler) { double('handler', setup: nil, receive: nil) }
|
19
19
|
|
20
20
|
before do
|
21
21
|
allow(EM).to receive(:connect) { connection }
|
@@ -39,28 +39,27 @@ describe Hatetepe::Client do
|
|
39
39
|
specify do
|
40
40
|
expect(handler_class).to have_received(:new)
|
41
41
|
.with(config, subject, connection)
|
42
|
-
expect(handler).to have_received(:
|
42
|
+
expect(handler).to have_received(:setup)
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
describe '#request' do
|
47
47
|
let(:request) do
|
48
|
-
double('request',
|
49
|
-
served: double('served', sync: response))
|
48
|
+
double('request', body: double(closed: double('closed', fulfill: nil)))
|
50
49
|
end
|
51
50
|
let(:response) { double('response') }
|
51
|
+
let(:served) { double('served', sync: response) }
|
52
52
|
|
53
53
|
before do
|
54
|
-
allow(Hatetepe::Request).to receive(:new)
|
55
|
-
allow(client).to
|
54
|
+
allow(Hatetepe::Request).to receive(:new) { request }
|
55
|
+
allow(client).to receive(:perform) { served }
|
56
56
|
end
|
57
57
|
|
58
58
|
subject! { client.request(:head, '/wat') }
|
59
59
|
|
60
60
|
specify do
|
61
|
-
expect(Hatetepe::Request).to
|
62
|
-
expect(
|
63
|
-
expect(client).to have_received(:perform).with(request)
|
61
|
+
expect(Hatetepe::Request).to have_received(:new).with(:head, '/wat')
|
62
|
+
expect(client).to have_received(:perform).with(request)
|
64
63
|
|
65
64
|
expect(subject).to be(response)
|
66
65
|
end
|
@@ -95,17 +94,17 @@ describe Hatetepe::Client do
|
|
95
94
|
end
|
96
95
|
|
97
96
|
describe '#receive' do
|
98
|
-
let(:request) {
|
99
|
-
let(:response) {
|
97
|
+
let(:request) { open_request }
|
98
|
+
let(:response) { open_response }
|
100
99
|
|
101
100
|
describe 'with outstanding request' do
|
102
|
-
|
101
|
+
let!(:served) { client.perform(request) }
|
103
102
|
|
104
103
|
subject! { client.receive(response) }
|
105
104
|
|
106
105
|
it 'correlates response with request, and notifies handlers' do
|
107
|
-
expect(
|
108
|
-
expect(
|
106
|
+
expect(served).to be_fulfilled
|
107
|
+
expect(served.value).to be(response)
|
109
108
|
|
110
109
|
expect(handler).to have_received(:receive).with(request, response)
|
111
110
|
end
|
@@ -121,8 +120,8 @@ describe Hatetepe::Client do
|
|
121
120
|
|
122
121
|
[:fulfill, :reject].each do |action|
|
123
122
|
describe "after #{action}ed response" do
|
124
|
-
let(:request) { WeakRef.new(
|
125
|
-
let(:response) { WeakRef.new(
|
123
|
+
let(:request) { WeakRef.new(closed_request) }
|
124
|
+
let(:response) { WeakRef.new(open_response) }
|
126
125
|
|
127
126
|
before do
|
128
127
|
client.perform(request.__getobj__)
|
@@ -148,23 +147,25 @@ describe Hatetepe::Client do
|
|
148
147
|
end
|
149
148
|
|
150
149
|
describe '#teardown' do
|
151
|
-
let(:requests) { [
|
152
|
-
let(:responses) { [
|
150
|
+
let(:requests) { [closed_request, closed_request] }
|
151
|
+
let(:responses) { [open_response] }
|
153
152
|
let(:reason) { double }
|
154
153
|
|
154
|
+
let!(:served) do
|
155
|
+
requests.map { |request| client.perform(request) }
|
156
|
+
end
|
157
|
+
|
155
158
|
before do
|
156
|
-
client.perform(requests[0])
|
157
|
-
client.perform(requests[1])
|
158
159
|
client.receive(responses[0])
|
159
160
|
end
|
160
161
|
|
161
162
|
subject! { client.teardown(reason) }
|
162
163
|
|
163
164
|
it 'rejects outstanding requests and responses' do
|
164
|
-
expect(responses[0].
|
165
|
-
expect(responses[0].
|
166
|
-
expect(
|
167
|
-
expect(
|
165
|
+
expect(responses[0].body.closed).to be_rejected
|
166
|
+
expect(responses[0].body.closed.reason).to be(reason)
|
167
|
+
expect(served[1]).to be_rejected
|
168
|
+
expect(served[1].reason).to be(reason)
|
168
169
|
end
|
169
170
|
end
|
170
171
|
end
|
@@ -7,7 +7,7 @@ describe Hatetepe::Connection::EventMachine do
|
|
7
7
|
let(:callback) { double('callback', call: nil) }
|
8
8
|
let(:parser) { double('parser', :<< => nil) }
|
9
9
|
let(:serializer) { double('serializer', serialize: nil) }
|
10
|
-
let(:message) {
|
10
|
+
let(:message) { open_request }
|
11
11
|
|
12
12
|
before do
|
13
13
|
allow(connection).to receive(:close_connection_after_writing)
|
@@ -17,8 +17,8 @@ describe Hatetepe::Connection::EventMachine do
|
|
17
17
|
|
18
18
|
connection.instance_variable_set(:@signature, 123)
|
19
19
|
|
20
|
-
# initialize separately, EM::Connection overloads .new
|
21
|
-
#
|
20
|
+
# XXX: initialize separately, EM::Connection overloads .new
|
21
|
+
# this smell tells us that we should decouple from EM::Connection
|
22
22
|
connection.send(:initialize, callback)
|
23
23
|
end
|
24
24
|
|
@@ -120,14 +120,15 @@ describe Hatetepe::Connection::EventMachine do
|
|
120
120
|
end
|
121
121
|
|
122
122
|
subject! do
|
123
|
-
message.
|
123
|
+
message.body.closed.on_progress { |chunk| progress << chunk }
|
124
124
|
connection.on_body(chunks[0])
|
125
125
|
connection.on_body(chunks[1])
|
126
|
+
message.close
|
126
127
|
end
|
127
128
|
|
128
129
|
it 'passes chunk to message body' do
|
129
|
-
expect(message.body).to eq(chunks.join)
|
130
|
-
expect(progress).to
|
130
|
+
expect(message.body.to_s).to eq(chunks.join)
|
131
|
+
expect(progress).to eq(chunks)
|
131
132
|
end
|
132
133
|
end
|
133
134
|
|
@@ -139,8 +140,8 @@ describe Hatetepe::Connection::EventMachine do
|
|
139
140
|
|
140
141
|
subject! { connection.on_message_complete }
|
141
142
|
|
142
|
-
it '
|
143
|
-
expect(message.
|
143
|
+
it 'closes the body' do
|
144
|
+
expect(message.body.closed).to be_fulfilled
|
144
145
|
end
|
145
146
|
end
|
146
147
|
end
|
data/spec/unit/request_spec.rb
CHANGED
@@ -5,23 +5,23 @@ require 'spec_helper'
|
|
5
5
|
describe Hatetepe::Request do
|
6
6
|
describe '#initialize' do
|
7
7
|
subject do
|
8
|
-
Hatetepe::Request.new(:get, '/',
|
8
|
+
Hatetepe::Request.new(:get, '/a?b=c',
|
9
|
+
{ 'Key' => 'value' }, 'hello').freeze
|
9
10
|
end
|
10
11
|
|
11
12
|
its(:http_method) { should be(:get) }
|
12
|
-
its(:uri) { should eq('/') }
|
13
|
+
its(:uri) { should eq('/a?b=c') }
|
13
14
|
its(:headers) { should eq('Key' => 'value') }
|
14
|
-
its(:body) { should eq('hello') }
|
15
15
|
|
16
|
-
|
17
|
-
its(:served) { should be_a(Hatetepe::Promise) }
|
16
|
+
specify { expect(subject.body.read).to eq('hello') }
|
18
17
|
|
19
18
|
describe 'defaults' do
|
20
19
|
subject { Hatetepe::Request.new(:get, '/') }
|
21
20
|
|
22
21
|
its(:headers) { should eq({}) }
|
23
|
-
its(:body) { should eq('') }
|
24
22
|
its(:http_version) { should eq(1.1) }
|
23
|
+
|
24
|
+
specify { expect(subject.body.read).to be_empty }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
data/spec/unit/response_spec.rb
CHANGED
@@ -10,9 +10,8 @@ describe Hatetepe::Response do
|
|
10
10
|
|
11
11
|
its(:status) { should be(200) }
|
12
12
|
its(:headers) { should eq('Key' => 'value') }
|
13
|
-
its(:body) { should eq('hello') }
|
14
13
|
|
15
|
-
|
14
|
+
specify { expect(subject.body.read).to eq('hello') }
|
16
15
|
|
17
16
|
its(:status_name) { should eq('OK') }
|
18
17
|
|
@@ -20,8 +19,9 @@ describe Hatetepe::Response do
|
|
20
19
|
subject { Hatetepe::Response.new(200) }
|
21
20
|
|
22
21
|
its(:headers) { should eq({}) }
|
23
|
-
its(:body) { should eq('') }
|
24
22
|
its(:http_version) { should eq(1.1) }
|
23
|
+
|
24
|
+
specify { expect(subject.body.read).to be_empty }
|
25
25
|
end
|
26
26
|
|
27
27
|
describe 'unknown status' do
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Hatetepe::Server::KeepAlive do
|
6
|
+
let(:config) { double('config') }
|
7
|
+
let(:server) { double('server') }
|
8
|
+
let(:connection) { double('connection') }
|
9
|
+
|
10
|
+
let(:object) { described_class.new(config, server, connection) }
|
11
|
+
let(:request) do
|
12
|
+
double('request', http_version: http_version,
|
13
|
+
headers: { 'Connection' => header })
|
14
|
+
end
|
15
|
+
let(:served) { double('served', value: response) }
|
16
|
+
let(:response) { double('response', headers: { 'Connection' => header }) }
|
17
|
+
|
18
|
+
describe '#close_connection?' do
|
19
|
+
before { object.serve(request, served) }
|
20
|
+
|
21
|
+
let(:subject) { object.send(:close_connection?, request) }
|
22
|
+
|
23
|
+
describe 'with HTTP/1.0 request' do
|
24
|
+
let(:http_version) { 1.0 }
|
25
|
+
|
26
|
+
describe 'and no header' do
|
27
|
+
let(:header) { nil }
|
28
|
+
|
29
|
+
it { should be(true) }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'and Connection: close' do
|
33
|
+
let(:header) { 'close' }
|
34
|
+
|
35
|
+
it { should be(true) }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'and Connection: keep-alive' do
|
39
|
+
let(:header) { 'keep-alive' }
|
40
|
+
|
41
|
+
it { should be(false) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'with HTTP/1.1 request' do
|
46
|
+
let(:http_version) { 1.1 }
|
47
|
+
|
48
|
+
describe 'and no header' do
|
49
|
+
let(:header) { nil }
|
50
|
+
|
51
|
+
it { should be(false) }
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'and Connection: close' do
|
55
|
+
let(:header) { 'close' }
|
56
|
+
|
57
|
+
it { should be(true) }
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'and Connection: keep-alive' do
|
61
|
+
let(:header) { 'keep-alive' }
|
62
|
+
|
63
|
+
it { should be(false) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|