hatetepe 0.3.1 → 0.4.0
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 +21 -16
- data/hatetepe.gemspec +1 -2
- data/lib/hatetepe/body.rb +5 -3
- data/lib/hatetepe/builder.rb +6 -3
- data/lib/hatetepe/cli.rb +27 -5
- data/lib/hatetepe/client.rb +157 -82
- data/lib/hatetepe/client/keep_alive.rb +58 -0
- data/lib/hatetepe/client/pipeline.rb +19 -0
- data/lib/hatetepe/connection.rb +42 -0
- data/lib/hatetepe/deferred_status_fix.rb +11 -0
- data/lib/hatetepe/message.rb +4 -4
- data/lib/hatetepe/parser.rb +3 -4
- data/lib/hatetepe/request.rb +19 -6
- data/lib/hatetepe/response.rb +11 -3
- data/lib/hatetepe/server.rb +115 -85
- data/lib/hatetepe/{app.rb → server/app.rb} +7 -2
- data/lib/hatetepe/server/keep_alive.rb +61 -0
- data/lib/hatetepe/server/pipeline.rb +24 -0
- data/lib/hatetepe/{proxy.rb → server/proxy.rb} +5 -11
- data/lib/hatetepe/version.rb +1 -1
- data/lib/rack/handler/hatetepe.rb +1 -4
- data/spec/integration/cli/start_spec.rb +75 -123
- data/spec/integration/client/keep_alive_spec.rb +74 -0
- data/spec/integration/server/keep_alive_spec.rb +99 -0
- data/spec/spec_helper.rb +41 -16
- data/spec/unit/app_spec.rb +16 -5
- data/spec/unit/builder_spec.rb +4 -4
- data/spec/unit/client/pipeline_spec.rb +40 -0
- data/spec/unit/client_spec.rb +355 -199
- data/spec/unit/connection_spec.rb +64 -0
- data/spec/unit/parser_spec.rb +3 -2
- data/spec/unit/proxy_spec.rb +9 -18
- data/spec/unit/rack_handler_spec.rb +2 -12
- data/spec/unit/server_spec.rb +154 -60
- metadata +31 -36
- data/.rspec +0 -1
- data/.travis.yml +0 -3
- data/.yardopts +0 -1
- data/lib/hatetepe/pipeline.rb +0 -27
@@ -0,0 +1,64 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hatetepe/connection"
|
3
|
+
|
4
|
+
describe Hatetepe::Connection do
|
5
|
+
let(:conn) { Hatetepe::Connection.allocate }
|
6
|
+
|
7
|
+
let(:peername) { "\x02\x00\x86\xF6\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" }
|
8
|
+
let(:address) { "127.0.0.1" }
|
9
|
+
let(:port) { 34550 }
|
10
|
+
|
11
|
+
before { conn.stub :get_peername => peername }
|
12
|
+
|
13
|
+
it "inherits from EM::Connection" do
|
14
|
+
conn.should be_an(EM::Connection)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#remote_address" do
|
18
|
+
it "returns the remote peer's address" do
|
19
|
+
conn.remote_address.should == address
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#remote_port" do
|
24
|
+
it "returns the remote peer's port" do
|
25
|
+
conn.remote_port.should == port
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#close_connection" do
|
30
|
+
before { EM::Connection.any_instance.stub :close_connection }
|
31
|
+
|
32
|
+
it "sets the closed-by-self flag" do
|
33
|
+
pending "How to test a call to super?"
|
34
|
+
|
35
|
+
conn.close_connection
|
36
|
+
conn.should be_closed
|
37
|
+
conn.should be_closed_by_self
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:arg) { stub "arg" }
|
41
|
+
|
42
|
+
it "calls EM::Connection.close_connection" do
|
43
|
+
pending "How to test a call to super?"
|
44
|
+
|
45
|
+
EM::Connection.any_instance.should_receive(:close_connection).with arg
|
46
|
+
conn.close_connection arg
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#unbind" do
|
51
|
+
it "sets the closed-by-remote flag" do
|
52
|
+
conn.unbind
|
53
|
+
conn.should be_closed
|
54
|
+
conn.should be_closed_by_remote
|
55
|
+
end
|
56
|
+
|
57
|
+
it "doesn't overwrite an existing closed-by flag" do
|
58
|
+
conn.stub :closed? => true
|
59
|
+
conn.unbind
|
60
|
+
conn.should be_closed
|
61
|
+
conn.should_not be_closed_by_remote
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/unit/parser_spec.rb
CHANGED
@@ -90,14 +90,15 @@ describe Hatetepe::Parser do
|
|
90
90
|
}
|
91
91
|
|
92
92
|
context "#on_request {|request| ... }" do
|
93
|
-
it "evals the block when
|
93
|
+
it "evals the block when the request headers have arrived" do
|
94
94
|
block.should_receive(:call) {|request|
|
95
95
|
request.should equal(parser.message)
|
96
96
|
|
97
97
|
request.verb.should == "POST"
|
98
98
|
request.uri.should == "/"
|
99
99
|
request.http_version.should == "1.1"
|
100
|
-
request.headers.should
|
100
|
+
request.headers.should == {"Transfer-Encoding" => "chunked",
|
101
|
+
"Bar" => "baz"}
|
101
102
|
}
|
102
103
|
|
103
104
|
parser.on_request &block
|
data/spec/unit/proxy_spec.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require "spec_helper"
|
2
|
-
require "hatetepe/
|
2
|
+
require "hatetepe/server"
|
3
3
|
|
4
|
-
describe Hatetepe::Proxy do
|
4
|
+
describe Hatetepe::Server::Proxy do
|
5
5
|
let(:app) { stub "app" }
|
6
6
|
|
7
7
|
describe "#initialize(app)" do
|
8
8
|
it "sets the app" do
|
9
|
-
Hatetepe::Proxy.new(app).app.should equal(app)
|
9
|
+
Hatetepe::Server::Proxy.new(app).app.should equal(app)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:proxy) { Hatetepe::Proxy.new app }
|
13
|
+
let(:proxy) { Hatetepe::Server::Proxy.new app }
|
14
14
|
let(:target) { stub "target" }
|
15
15
|
let(:env) { {} }
|
16
16
|
let(:client) { stub "client", :<< => nil }
|
@@ -133,22 +133,13 @@ describe Hatetepe::Proxy do
|
|
133
133
|
proxy.build_request(env, target).uri.should == "/bar/foo"
|
134
134
|
end
|
135
135
|
|
136
|
-
it "sets X-Forwarded-For header" do
|
137
|
-
xff = proxy.build_request(env, target).headers["X-Forwarded-For"]
|
138
|
-
env["REMOTE_ADDR"].should == xff
|
139
|
-
end
|
140
|
-
|
141
|
-
it "adds the target to Host header" do
|
142
|
-
host = "localhost:3000"
|
143
|
-
proxy.build_request(env, target).headers["Host"].should == host
|
144
|
-
|
145
|
-
base_request.headers["Host"] = host
|
146
|
-
host = "localhost:3000, localhost:3000"
|
147
|
-
proxy.build_request(env, target).headers["Host"].should == host
|
148
|
-
end
|
149
|
-
|
150
136
|
it "builds a new request" do
|
151
137
|
proxy.build_request(env, target).should_not equal(base_request)
|
152
138
|
end
|
139
|
+
|
140
|
+
it "sets version to HTTP/1.1" do
|
141
|
+
base_request.http_version = "1.0"
|
142
|
+
proxy.build_request(env, target).http_version.should == "1.1"
|
143
|
+
end
|
153
144
|
end
|
154
145
|
end
|
@@ -31,17 +31,6 @@ describe Rack::Handler::Hatetepe do
|
|
31
31
|
Rack::Handler::Hatetepe.run app, options
|
32
32
|
end
|
33
33
|
|
34
|
-
it "yields the server" do
|
35
|
-
Hatetepe::Server.stub :start => server
|
36
|
-
|
37
|
-
srvr = nil
|
38
|
-
Rack::Handler::Hatetepe.run(app) {|s|
|
39
|
-
srvr = s
|
40
|
-
EM.stop
|
41
|
-
}
|
42
|
-
srvr.should equal(server)
|
43
|
-
end
|
44
|
-
|
45
34
|
it "can be stopped by sending SIGTERM or SIGINT" do
|
46
35
|
EM.should_receive(:synchrony) {|&block| block.call }
|
47
36
|
|
@@ -63,8 +52,9 @@ describe Rack::Handler::Hatetepe do
|
|
63
52
|
Hatetepe::Server.should_receive(:start) {|opts|
|
64
53
|
opts[:host].should == "0.0.0.0"
|
65
54
|
opts[:port].should == 8080
|
55
|
+
EM.stop
|
66
56
|
}
|
67
|
-
Rack::Handler::Hatetepe.run
|
57
|
+
Rack::Handler::Hatetepe.run app
|
68
58
|
end
|
69
59
|
end
|
70
60
|
end
|
data/spec/unit/server_spec.rb
CHANGED
@@ -5,16 +5,13 @@ describe Hatetepe::Server do
|
|
5
5
|
let(:server) {
|
6
6
|
Hatetepe::Server.allocate.tap {|s|
|
7
7
|
s.send :initialize, config
|
8
|
+
s.stub :comm_inactivity_timeout=
|
8
9
|
s.post_init
|
9
10
|
s.requests << request
|
10
11
|
}
|
11
12
|
}
|
12
|
-
let(:request) {
|
13
|
-
let(:env) {
|
14
|
-
{
|
15
|
-
"rack.input" => Hatetepe::Body.new
|
16
|
-
}
|
17
|
-
}
|
13
|
+
let(:request) { Hatetepe::Request.new :get, "/" }
|
14
|
+
let(:env) { request.to_h }
|
18
15
|
|
19
16
|
let(:app) { stub "app" }
|
20
17
|
let(:host) { "127.0.4.1" }
|
@@ -25,10 +22,24 @@ describe Hatetepe::Server do
|
|
25
22
|
:app => app,
|
26
23
|
:host => host,
|
27
24
|
:port => port,
|
28
|
-
:errors => errors
|
25
|
+
:errors => errors,
|
26
|
+
:timeout => 0.0123
|
29
27
|
}
|
30
28
|
}
|
31
29
|
|
30
|
+
before do
|
31
|
+
server.stub :sockaddr => [42424, "127.0.42.1"]
|
32
|
+
@old_env, ENV["RACK_ENV"] = ENV["RACK_ENV"], "testing"
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
ENV["RACK_ENV"] = @old_env
|
37
|
+
end
|
38
|
+
|
39
|
+
it "inherits from Hatetepe::Connection" do
|
40
|
+
server.should be_a(Hatetepe::Connection)
|
41
|
+
end
|
42
|
+
|
32
43
|
context ".start(config)" do
|
33
44
|
it "starts an EventMachine server" do
|
34
45
|
args = [host, port, Hatetepe::Server, config]
|
@@ -40,23 +51,11 @@ describe Hatetepe::Server do
|
|
40
51
|
|
41
52
|
context "#initialize(config)" do
|
42
53
|
let(:server) { Hatetepe::Server.allocate }
|
43
|
-
let(:builder) { stub "app builder", :to_app => to_app }
|
44
|
-
let(:to_app) { stub "to_app" }
|
45
|
-
|
46
|
-
it "builds the app" do
|
47
|
-
Rack::Builder.stub :new => builder
|
48
|
-
builder.should_receive(:use).with Hatetepe::Pipeline
|
49
|
-
builder.should_receive(:use).with Hatetepe::App
|
50
|
-
builder.should_receive(:use).with Hatetepe::Proxy
|
51
|
-
builder.should_receive(:run).with app
|
52
|
-
|
53
|
-
server.send :initialize, config
|
54
|
-
server.app.should equal(to_app)
|
55
|
-
end
|
56
54
|
|
57
55
|
it "sets up the error stream" do
|
58
56
|
server.send :initialize, config
|
59
57
|
server.errors.should equal(errors)
|
58
|
+
server.config[:errors].should be_nil
|
60
59
|
end
|
61
60
|
|
62
61
|
it "uses stderr as default error stream" do
|
@@ -64,30 +63,63 @@ describe Hatetepe::Server do
|
|
64
63
|
server.send :initialize, config
|
65
64
|
server.errors.should equal($stderr)
|
66
65
|
end
|
66
|
+
|
67
|
+
it "assumes a default connection inactivity timeout of 1 seconds" do
|
68
|
+
server.send :initialize, {}
|
69
|
+
server.config[:timeout].should equal(1)
|
70
|
+
end
|
67
71
|
end
|
68
72
|
|
69
73
|
context "#post_init" do
|
70
|
-
let
|
71
|
-
Hatetepe::Server.allocate.tap
|
72
|
-
|
74
|
+
let :server do
|
75
|
+
Hatetepe::Server.allocate.tap do |s|
|
76
|
+
s.send :initialize, config
|
77
|
+
s.stub :comm_inactivity_timeout=
|
78
|
+
end
|
79
|
+
end
|
73
80
|
|
74
81
|
it "sets up the request queue" do
|
82
|
+
server.post_init
|
75
83
|
server.requests.should be_an(Array)
|
76
84
|
server.requests.should be_empty
|
77
85
|
end
|
78
86
|
|
79
87
|
it "sets up the parser" do
|
88
|
+
server.post_init
|
80
89
|
server.parser.should respond_to(:<<)
|
81
90
|
server.parser.on_request[0].should == server.requests.method(:<<)
|
82
91
|
end
|
83
92
|
|
84
93
|
it "sets up the builder" do
|
94
|
+
server.post_init
|
85
95
|
server.builder.on_write[0].should == server.method(:send_data)
|
86
96
|
end
|
97
|
+
|
98
|
+
it "builds the app" do
|
99
|
+
server.post_init
|
100
|
+
server.app.should be_a(Hatetepe::Server::Pipeline)
|
101
|
+
server.app.app.should be_a(Hatetepe::Server::App)
|
102
|
+
server.app.app.app.should be_a(Hatetepe::Server::KeepAlive)
|
103
|
+
server.app.app.app.app.should be_a(Hatetepe::Server::Proxy)
|
104
|
+
server.app.app.app.app.app.should equal(app)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "starts the connection inactivity tracking" do
|
108
|
+
server.should_receive(:comm_inactivity_timeout=).with 0.0123
|
109
|
+
server.post_init
|
110
|
+
end
|
111
|
+
|
112
|
+
it "enables request processing" do
|
113
|
+
server.post_init
|
114
|
+
server.processing_enabled.should be_true
|
115
|
+
end
|
87
116
|
end
|
88
117
|
|
89
118
|
context "#receive_data(data)" do
|
90
|
-
before
|
119
|
+
before do
|
120
|
+
ENV.delete "RACK_ENV"
|
121
|
+
server.stub :close_connection
|
122
|
+
end
|
91
123
|
|
92
124
|
it "feeds data into the parser" do
|
93
125
|
data = stub("data")
|
@@ -102,13 +134,29 @@ describe Hatetepe::Server do
|
|
102
134
|
server.receive_data "irrelevant data"
|
103
135
|
end
|
104
136
|
|
137
|
+
it "re-raises parsing errors if RACK_ENV is testing" do
|
138
|
+
ENV["RACK_ENV"] = "testing"
|
139
|
+
server.parser.should_receive(:<<).and_raise Hatetepe::ParserError
|
140
|
+
|
141
|
+
expect {
|
142
|
+
server.receive_data "irrelevant data"
|
143
|
+
}.to raise_error(Hatetepe::ParserError)
|
144
|
+
end
|
145
|
+
|
105
146
|
it "closes the connection when catching an exception" do
|
106
|
-
server.parser.should_receive(:<<).and_raise
|
147
|
+
server.parser.should_receive(:<<).and_raise Exception
|
107
148
|
server.should_receive :close_connection_after_writing
|
108
149
|
|
109
150
|
server.receive_data ""
|
110
151
|
end
|
111
152
|
|
153
|
+
it "re-raises caught exceptions" do
|
154
|
+
ENV["RACK_ENV"] = "testing"
|
155
|
+
server.parser.should_receive(:<<).and_raise Exception
|
156
|
+
|
157
|
+
expect { server.receive_data "" }.to raise_error(Exception)
|
158
|
+
end
|
159
|
+
|
112
160
|
it "logs caught exceptions" do
|
113
161
|
server.parser.should_receive(:<<).and_raise "error message"
|
114
162
|
errors.should_receive(:<<) {|str|
|
@@ -121,6 +169,11 @@ describe Hatetepe::Server do
|
|
121
169
|
end
|
122
170
|
|
123
171
|
context "#process" do
|
172
|
+
before do
|
173
|
+
request.stub :to_h => env
|
174
|
+
app.stub :call => [-1]
|
175
|
+
end
|
176
|
+
|
124
177
|
it "puts useful stuff into env[]" do
|
125
178
|
app.should_receive(:call) {|e|
|
126
179
|
e.should equal(env)
|
@@ -135,6 +188,9 @@ describe Hatetepe::Server do
|
|
135
188
|
e["SERVER_NAME"].should == host
|
136
189
|
e["SERVER_NAME"].should_not equal(host)
|
137
190
|
e["SERVER_PORT"].should == String(port)
|
191
|
+
e["REMOTE_ADDR"].should == server.remote_address
|
192
|
+
e["REMOTE_ADDR"].should_not equal(server.remote_address)
|
193
|
+
e["REMOTE_PORT"].should == String(server.remote_port)
|
138
194
|
e["HTTP_HOST"].should == "#{host}:#{port}"
|
139
195
|
|
140
196
|
[-1]
|
@@ -150,6 +206,32 @@ describe Hatetepe::Server do
|
|
150
206
|
}
|
151
207
|
server.process
|
152
208
|
end
|
209
|
+
|
210
|
+
it "is a no-op if processing is disabled" do
|
211
|
+
server.processing_enabled = false
|
212
|
+
app.should_not_receive :call
|
213
|
+
server.process
|
214
|
+
end
|
215
|
+
|
216
|
+
let(:another_request) { Hatetepe::Request.new :get, "/asdf" }
|
217
|
+
|
218
|
+
it "disables the connection timeout until the last request is finished" do
|
219
|
+
server.requests << another_request
|
220
|
+
|
221
|
+
server.should_receive(:comm_inactivity_timeout=).with 0
|
222
|
+
server.process
|
223
|
+
|
224
|
+
server.should_not_receive(:comm_inactivity_timeout=).with config[:timeout]
|
225
|
+
server.requests.delete request
|
226
|
+
request.succeed
|
227
|
+
|
228
|
+
server.rspec_verify
|
229
|
+
server.rspec_reset
|
230
|
+
|
231
|
+
server.should_receive(:comm_inactivity_timeout=).with config[:timeout]
|
232
|
+
server.requests.delete another_request
|
233
|
+
another_request.succeed
|
234
|
+
end
|
153
235
|
end
|
154
236
|
|
155
237
|
context "env[stream.start].call(response)" do
|
@@ -176,22 +258,11 @@ describe Hatetepe::Server do
|
|
176
258
|
server.process
|
177
259
|
end
|
178
260
|
|
261
|
+
# TODO this should be moved to a Server::Pipeline spec
|
179
262
|
it "waits for the previous request's response to finish" do
|
180
263
|
server.builder.should_not_receive :response
|
181
264
|
server.process
|
182
265
|
end
|
183
|
-
|
184
|
-
it "initiates the response" do
|
185
|
-
server.builder.should_receive(:response_line) {|code|
|
186
|
-
code.should equal(response[0])
|
187
|
-
}
|
188
|
-
server.builder.should_receive(:headers) {|headers|
|
189
|
-
headers["Key"].should equal(response[1]["Key"])
|
190
|
-
headers["Server"].should == "hatetepe/#{Hatetepe::VERSION}"
|
191
|
-
}
|
192
|
-
previous.succeed
|
193
|
-
server.process
|
194
|
-
end
|
195
266
|
end
|
196
267
|
|
197
268
|
context "env[stream.send].call(chunk)" do
|
@@ -206,55 +277,78 @@ describe Hatetepe::Server do
|
|
206
277
|
|
207
278
|
context "env[stream.close].call" do
|
208
279
|
before {
|
209
|
-
server.stub :close_connection
|
210
280
|
server.builder.stub :complete
|
211
281
|
request.stub :succeed
|
212
282
|
}
|
213
283
|
|
214
|
-
it "
|
215
|
-
server.
|
284
|
+
it "leaves the connection open" do
|
285
|
+
server.should_not_receive :close_connection
|
216
286
|
app.stub(:call) {|e|
|
287
|
+
server.requests << stub("another request")
|
217
288
|
e["stream.close"].call
|
218
289
|
[-1]
|
219
290
|
}
|
220
291
|
server.process
|
221
292
|
end
|
222
293
|
|
223
|
-
it "
|
224
|
-
request.should_receive :succeed
|
294
|
+
it "deletes itself and stream.send from env[] to prevent multiple calls" do
|
225
295
|
app.stub(:call) {|e|
|
226
296
|
e["stream.close"].call
|
297
|
+
e.key?("stream.send").should be_false
|
298
|
+
e.key?("stream.close").should be_false
|
227
299
|
[-1]
|
228
300
|
}
|
229
301
|
server.process
|
230
302
|
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context "#start_response(response)" do
|
306
|
+
let(:previous) { EM::DefaultDeferrable.new }
|
307
|
+
let(:response) { [200, {"Key" => "value"}, Rack::STREAMING] }
|
231
308
|
|
232
|
-
|
233
|
-
server.
|
234
|
-
app.stub(:call) {|e|
|
235
|
-
|
236
|
-
|
237
|
-
|
309
|
+
before {
|
310
|
+
server.requests.unshift previous
|
311
|
+
app.stub(:call) {|e| response }
|
312
|
+
request.stub :succeed
|
313
|
+
server.builder.stub :response_line
|
314
|
+
server.builder.stub :headers
|
315
|
+
}
|
316
|
+
|
317
|
+
it "initiates the response" do
|
318
|
+
server.builder.should_receive(:response_line) {|code|
|
319
|
+
code.should equal(response[0])
|
238
320
|
}
|
321
|
+
server.builder.should_receive(:headers) {|headers|
|
322
|
+
headers["Key"].should equal(response[1]["Key"])
|
323
|
+
headers["Server"].should == "hatetepe/#{Hatetepe::VERSION}"
|
324
|
+
}
|
325
|
+
previous.succeed
|
239
326
|
server.process
|
240
327
|
end
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
328
|
+
end
|
329
|
+
|
330
|
+
context "#close_response(request)" do
|
331
|
+
before do
|
332
|
+
server.builder.stub :complete
|
333
|
+
request.stub :succeed
|
334
|
+
app.stub :call do |e|
|
245
335
|
e["stream.close"].call
|
246
336
|
[-1]
|
247
|
-
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
it "removes the request from the request queue" do
|
248
341
|
server.process
|
342
|
+
server.requests.should be_empty
|
249
343
|
end
|
250
344
|
|
251
|
-
it "
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
345
|
+
it "completes the response" do
|
346
|
+
server.builder.should_receive :complete
|
347
|
+
server.process
|
348
|
+
end
|
349
|
+
|
350
|
+
it "succeeds the request" do
|
351
|
+
request.should_receive :succeed
|
258
352
|
server.process
|
259
353
|
end
|
260
354
|
end
|