hatetepe 0.6.0.pre → 0.6.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|