hatetepe 0.4.1 → 0.5.0.pre
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/examples/parallel_requests.rb +32 -0
- data/hatetepe.gemspec +3 -4
- data/lib/hatetepe/builder.rb +1 -0
- data/lib/hatetepe/cli.rb +11 -20
- data/lib/hatetepe/client.rb +210 -181
- data/lib/hatetepe/connection.rb +31 -12
- data/lib/hatetepe/server/keep_alive.rb +15 -53
- data/lib/hatetepe/server/pipeline.rb +14 -18
- data/lib/hatetepe/server/rack_app.rb +39 -0
- data/lib/hatetepe/server.rb +60 -108
- data/lib/hatetepe/version.rb +1 -1
- data/lib/rack/handler/hatetepe.rb +2 -0
- data/spec/integration/cli/start_spec.rb +84 -92
- data/spec/integration/client/keep_alive_spec.rb +5 -56
- data/spec/integration/client/timeout_spec.rb +93 -0
- data/spec/integration/server/keep_alive_spec.rb +8 -80
- data/spec/integration/server/timeout_spec.rb +45 -0
- data/spec/spec_helper.rb +7 -59
- data/spec/unit/client_spec.rb +68 -363
- data/spec/unit/connection_spec.rb +5 -7
- data/spec/unit/server_spec.rb +108 -338
- metadata +58 -43
- data/lib/hatetepe/client/keep_alive.rb +0 -32
- data/lib/hatetepe/client/pipeline.rb +0 -19
- data/lib/hatetepe/server/app.rb +0 -85
- data/lib/hatetepe/server/proxy.rb +0 -48
- data/spec/unit/app_spec.rb +0 -125
- data/spec/unit/client/pipeline_spec.rb +0 -40
- data/spec/unit/proxy_spec.rb +0 -145
data/spec/unit/client_spec.rb
CHANGED
@@ -1,380 +1,85 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "hatetepe/client"
|
3
3
|
|
4
|
-
describe Hatetepe::Client do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
it "sets the config" do
|
24
|
-
client.config.should equal(config)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "creates the builder and parser" do
|
28
|
-
client.parser.should be_a(Hatetepe::Parser)
|
29
|
-
client.builder.should be_a(Hatetepe::Builder)
|
30
|
-
end
|
31
|
-
|
32
|
-
it "creates the requests list" do
|
33
|
-
client.requests.should be_an(Array)
|
34
|
-
client.requests.should be_empty
|
35
|
-
end
|
36
|
-
|
37
|
-
it "creates the lists of requests pending transmission or response" do
|
38
|
-
client.pending_transmission.should be_a(Hash)
|
39
|
-
client.pending_transmission.should be_empty
|
40
|
-
client.pending_response.should be_a(Hash)
|
41
|
-
client.pending_response.should be_empty
|
42
|
-
end
|
43
|
-
|
44
|
-
it "builds the app" do
|
45
|
-
client.app.should be_a(Hatetepe::Client::KeepAlive)
|
46
|
-
client.app.app.should be_a(Hatetepe::Client::Pipeline)
|
47
|
-
client.app.app.app.should == client.method(:send_request)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe "#post_init" do
|
52
|
-
it "wires the builder and parser" do
|
53
|
-
client.post_init
|
54
|
-
client.builder.on_write[0].should == client.method(:send_data)
|
55
|
-
client.parser.on_response[0].should == client.method(:receive_response)
|
4
|
+
describe Hatetepe::Client, "(public API)" do
|
5
|
+
describe ".start"
|
6
|
+
|
7
|
+
describe "#stop"
|
8
|
+
|
9
|
+
describe "#stop!"
|
10
|
+
|
11
|
+
describe "#wait"
|
12
|
+
|
13
|
+
describe ".request"
|
14
|
+
|
15
|
+
describe "#request"
|
16
|
+
|
17
|
+
describe "#<<" do
|
18
|
+
let :config do
|
19
|
+
{
|
20
|
+
:host => "127.0.0.1",
|
21
|
+
:port => 4242
|
22
|
+
}
|
56
23
|
end
|
57
|
-
|
58
|
-
|
24
|
+
|
25
|
+
let :subject do
|
26
|
+
client = Object.new.extend(Hatetepe::Client)
|
27
|
+
client.stub(:send_data)
|
28
|
+
client.stub(:comm_inactivity_timeout=)
|
29
|
+
client.stub(:pending_connect_timeout=)
|
30
|
+
client.send(:initialize, config)
|
59
31
|
client.post_init
|
60
|
-
client
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe "#receive_data(data)" do
|
65
|
-
let(:data) { stub "data" }
|
66
|
-
|
67
|
-
it "feeds the data into the parser" do
|
68
|
-
client.parser.should_receive(:<<).with data
|
69
|
-
client.receive_data data
|
70
|
-
end
|
71
|
-
|
72
|
-
let(:error) { StandardError.new "alarm! eindringlinge! alarm!" }
|
73
|
-
|
74
|
-
it "stops the client if it catches an error" do
|
75
|
-
client.parser.should_receive(:<<).and_raise error
|
76
|
-
client.should_receive :close_connection
|
77
|
-
proc { client.receive_data data }.should raise_error(error)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe "#send_request(request)" do
|
82
|
-
let(:entry) { stub "entry" }
|
83
|
-
|
84
|
-
before do
|
85
|
-
client.pending_transmission[request.object_id] = entry
|
86
|
-
client.builder.stub :request
|
87
|
-
entry.stub :succeed
|
88
|
-
EM::Synchrony.stub :sync
|
89
|
-
end
|
90
|
-
|
91
|
-
it "feeds the request into the builder" do
|
92
|
-
client.builder.should_receive(:request).with request_as_array
|
93
|
-
client.send_request request
|
94
|
-
end
|
95
|
-
|
96
|
-
it "succeeds the request's entry in the pending transmission list" do
|
97
|
-
entry.should_receive :succeed
|
98
|
-
client.send_request request
|
99
|
-
end
|
100
|
-
|
101
|
-
it "adds the request to the pending response list and waits" do
|
102
|
-
EM::Synchrony.should_receive(:sync) do |syncee|
|
103
|
-
syncee.should respond_to(:succeed)
|
104
|
-
client.pending_response[request.object_id].should equal(syncee)
|
105
|
-
end
|
106
|
-
client.send_request request
|
107
|
-
end
|
108
|
-
|
109
|
-
it "returns the waiting result" do
|
110
|
-
EM::Synchrony.should_receive(:sync).and_return response
|
111
|
-
client.send_request(request).should equal(response)
|
112
|
-
end
|
113
|
-
|
114
|
-
it "makes sure the request gets removed from the pending response list" do
|
115
|
-
EM::Synchrony.should_receive(:sync).and_raise StandardError
|
116
|
-
client.send_request request rescue nil
|
117
|
-
client.pending_response.should be_empty
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
describe "#receive_response(response)" do
|
122
|
-
let(:requests) {
|
123
|
-
[
|
124
|
-
stub("request_with_response", :response => stub("response")),
|
125
|
-
request,
|
126
|
-
stub("another_request", :response => nil)
|
127
|
-
]
|
128
|
-
}
|
129
|
-
let(:id) { requests[1].object_id }
|
130
|
-
|
131
|
-
before do
|
132
|
-
client.stub :requests => requests
|
133
|
-
client.pending_response[id] = stub("entry", :succeed => nil)
|
134
|
-
end
|
135
|
-
|
136
|
-
it "succeeds the pending response list entry of the first request without a response" do
|
137
|
-
client.pending_response[id].should_receive(:succeed).with response
|
138
|
-
client.receive_response response
|
139
|
-
end
|
140
|
-
|
141
|
-
it "associates the response with the corresponding request" do
|
142
|
-
client.receive_response response
|
143
|
-
request.response.should equal(response)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe "#<<(request)" do
|
148
|
-
let(:fiber) { stub "fiber", :resume => nil }
|
149
|
-
let(:app) { stub "app", :call => response }
|
150
|
-
|
151
|
-
before do
|
152
|
-
client.processing_enabled = true
|
153
|
-
client.stub :app => app
|
154
|
-
Fiber.stub(:new) {|blk| blk.call; fiber }
|
155
|
-
end
|
156
|
-
|
157
|
-
it "sets a Host header if none is set" do
|
158
|
-
app.should_receive :call do |req|
|
159
|
-
req.headers["Host"].should == "example.net:8080"
|
160
|
-
end
|
161
|
-
client << request
|
162
|
-
end
|
163
|
-
|
164
|
-
it "sets the request's connection" do
|
165
|
-
request.should_receive(:connection=).with client
|
166
|
-
client << request
|
167
|
-
end
|
168
|
-
|
169
|
-
it "adds the request to the requests list" do
|
170
|
-
app.should_receive :call do
|
171
|
-
client.requests[-1].should equal(request)
|
172
|
-
end
|
173
|
-
client << request
|
174
|
-
client.requests.should be_empty
|
175
|
-
end
|
176
|
-
|
177
|
-
it "fails and ignores the request if processing is disabled" do
|
178
|
-
client.processing_enabled = false
|
179
|
-
request.should_receive :fail
|
180
|
-
app.should_not_receive :call
|
181
|
-
|
182
|
-
client << request
|
183
|
-
request.connection.should equal(client)
|
184
|
-
end
|
185
|
-
|
186
|
-
it "adds the request to the pending transmission list" do
|
187
|
-
app.should_receive :call do |req|
|
188
|
-
client.pending_transmission[req.object_id].should respond_to(:succeed)
|
189
|
-
end
|
190
|
-
client << request
|
191
|
-
end
|
192
|
-
|
193
|
-
it "calls the app" do
|
194
|
-
app.should_receive(:call).with request
|
195
|
-
client << request
|
32
|
+
client
|
196
33
|
end
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
client << request
|
201
|
-
end
|
202
|
-
|
203
|
-
it "succeeds the request if the response status indicates success" do
|
204
|
-
request.should_receive(:succeed).with response
|
205
|
-
client << request
|
206
|
-
end
|
207
|
-
|
208
|
-
it "fails the request if the response status indicates failure" do
|
209
|
-
response.status = 403
|
210
|
-
request.should_receive(:fail).with response
|
211
|
-
client << request
|
212
|
-
end
|
213
|
-
|
214
|
-
it "fails the request if no response has been received" do
|
215
|
-
app.stub :call => nil
|
216
|
-
request.should_receive(:fail).with nil
|
217
|
-
client << request
|
218
|
-
end
|
219
|
-
|
220
|
-
it "makes sure the request gets removed from the pending transmission list" do
|
221
|
-
app.should_receive(:call).and_raise StandardError
|
222
|
-
client << request rescue nil
|
223
|
-
client.pending_transmission.should be_empty
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
describe "#request(verb, uri, headers, body)" do
|
228
|
-
before do
|
229
|
-
EM::Synchrony.stub :sync
|
230
|
-
client.stub :<<
|
231
|
-
end
|
232
|
-
|
233
|
-
it "sets the User-Agent header" do
|
234
|
-
client.should_receive :<< do |request|
|
235
|
-
request.headers["User-Agent"].should == "hatetepe/#{Hatetepe::VERSION}"
|
236
|
-
end
|
237
|
-
client.request :get, uri
|
34
|
+
|
35
|
+
let :request do
|
36
|
+
Hatetepe::Request.new :head, "/test"
|
238
37
|
end
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
request.headers["User-Agent"].should equal(user_agent)
|
38
|
+
|
39
|
+
it "calls the app in a Fiber" do
|
40
|
+
fiber = Fiber.current
|
41
|
+
subject.app.should_receive :call do
|
42
|
+
Fiber.current.should_not equal(fiber)
|
245
43
|
end
|
246
|
-
|
44
|
+
|
45
|
+
subject << request
|
247
46
|
end
|
248
47
|
|
249
|
-
it "
|
48
|
+
it "fails the request if the response is a failure" do
|
49
|
+
subject.app.stub :call => Hatetepe::Response.new(400)
|
50
|
+
request.should_receive(:fail).with(subject.app.call)
|
250
51
|
|
251
|
-
|
252
|
-
body.should_not_receive :close_write
|
253
|
-
client.request :get, uri, {}, body
|
254
|
-
end
|
255
|
-
|
256
|
-
it "passes the request to #<<" do
|
257
|
-
client.should_receive :<< do |request|
|
258
|
-
request.verb.should == "GET"
|
259
|
-
request.uri.should == uri
|
260
|
-
request.headers.should == headers
|
261
|
-
request.body.should == [body]
|
262
|
-
end
|
263
|
-
client.request :get, uri, headers, body
|
264
|
-
end
|
265
|
-
|
266
|
-
it "waits until the requests succeeds" do
|
267
|
-
EM::Synchrony.should_receive(:sync).with kind_of(Hatetepe::Request)
|
268
|
-
client.request :get, uri
|
269
|
-
end
|
270
|
-
|
271
|
-
it "closes the response body if the request's method was HEAD" do
|
272
|
-
Hatetepe::Request.any_instance.stub :response => response
|
273
|
-
response.body.should_receive :close_write
|
274
|
-
client.request :head, uri
|
275
|
-
end
|
276
|
-
|
277
|
-
it "returns the response" do
|
278
|
-
Hatetepe::Request.any_instance.stub :response => response
|
279
|
-
client.request(:get, uri).should equal(response)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
describe "#stop" do
|
284
|
-
before do
|
285
|
-
response.stub :body => stub("body")
|
286
|
-
client.stub :requests => [request,
|
287
|
-
stub("another_request", :response => response)]
|
288
|
-
client.stub :close_connection
|
289
|
-
end
|
290
|
-
|
291
|
-
it "waits for the last request to complete and then stops" do
|
292
|
-
EM::Synchrony.should_receive(:sync).with(client.requests.last) { response }
|
293
|
-
EM::Synchrony.should_receive(:sync).with response.body
|
294
|
-
client.should_receive :close_connection
|
295
|
-
client.stop
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
describe "#wrap_body(body)" do
|
300
|
-
let(:body) { stub "body" }
|
301
|
-
|
302
|
-
it "doesn't modify a body that responds to #each" do
|
303
|
-
body.stub :each
|
304
|
-
client.wrap_body(body).should equal(body)
|
52
|
+
subject << request
|
305
53
|
end
|
306
|
-
|
307
|
-
it "makes a body that responds to #read enumerable" do
|
308
|
-
body.stub :read => stub("#read")
|
309
|
-
client.wrap_body(body).should == [body.read]
|
310
|
-
end
|
311
|
-
|
312
|
-
it "makes other bodies enumerable" do
|
313
|
-
client.wrap_body(body).should == [body]
|
314
|
-
end
|
315
|
-
|
316
|
-
it "makes an empty body enumerable" do
|
317
|
-
client.wrap_body(nil).should == []
|
318
|
-
end
|
319
|
-
end
|
320
54
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
let(:config) { {:host => "0.0.0.0", :port => 1234} }
|
327
|
-
let(:client) { stub "client" }
|
328
|
-
|
329
|
-
it "starts an EventMachine connection and returns it" do
|
330
|
-
EM.should_receive(:connect).with(config[:host], config[:port],
|
331
|
-
Hatetepe::Client, config) { client }
|
332
|
-
Hatetepe::Client.start(config).should equal(client)
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
describe ".request(verb, uri, headers, body)" do
|
337
|
-
let(:client) { stub "client" }
|
338
|
-
|
339
|
-
before do
|
340
|
-
Hatetepe::Client.stub :start => client
|
341
|
-
client.stub :request => response, :stop => nil
|
342
|
-
end
|
343
|
-
|
344
|
-
it "starts a client" do
|
345
|
-
Hatetepe::Client.should_receive(:start).with :host => parsed_uri.host,
|
346
|
-
:port => parsed_uri.port
|
347
|
-
Hatetepe::Client.request :get, uri
|
348
|
-
end
|
349
|
-
|
350
|
-
it "feeds the request into the client and returns the response" do
|
351
|
-
client.should_receive(:request).with(:get, parsed_uri.request_uri,
|
352
|
-
headers, body) { response }
|
353
|
-
Hatetepe::Client.request(:get, uri, headers, body).should equal(response)
|
55
|
+
it "fails the request if there is no response (yet)" do
|
56
|
+
subject.app.stub :call => nil
|
57
|
+
request.should_receive(:fail).with(nil)
|
58
|
+
|
59
|
+
subject << request
|
354
60
|
end
|
355
|
-
|
356
|
-
it "
|
357
|
-
|
358
|
-
|
61
|
+
|
62
|
+
it "succeeds the request if the response is a success" do
|
63
|
+
subject.app.stub :call => Hatetepe::Response.new(303)
|
64
|
+
request.should_receive(:succeed).with(subject.app.call)
|
65
|
+
|
66
|
+
subject << request
|
359
67
|
end
|
360
68
|
end
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
end
|
378
|
-
end
|
379
|
-
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe Hatetepe::Client, "EventMachine API" do
|
72
|
+
describe "#initialize"
|
73
|
+
|
74
|
+
describe "#post_init"
|
75
|
+
|
76
|
+
describe "#receive_data"
|
77
|
+
|
78
|
+
describe "#unbind"
|
79
|
+
end
|
80
|
+
|
81
|
+
describe Hatetepe::Client, "private API" do
|
82
|
+
describe "#send_request"
|
83
|
+
|
84
|
+
describe "#receive_response"
|
380
85
|
end
|
@@ -2,7 +2,9 @@ require "spec_helper"
|
|
2
2
|
require "hatetepe/connection"
|
3
3
|
|
4
4
|
describe Hatetepe::Connection do
|
5
|
-
let
|
5
|
+
let :conn do
|
6
|
+
Object.new.extend Hatetepe::Connection
|
7
|
+
end
|
6
8
|
|
7
9
|
let(:peername) { "\x02\x00\x86\xF6\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" }
|
8
10
|
let(:address) { "127.0.0.1" }
|
@@ -10,10 +12,6 @@ describe Hatetepe::Connection do
|
|
10
12
|
|
11
13
|
before { conn.stub :get_peername => peername }
|
12
14
|
|
13
|
-
it "inherits from EM::Connection" do
|
14
|
-
conn.should be_an(EM::Connection)
|
15
|
-
end
|
16
|
-
|
17
15
|
describe "#remote_address" do
|
18
16
|
it "returns the remote peer's address" do
|
19
17
|
conn.remote_address.should == address
|
@@ -49,14 +47,14 @@ describe Hatetepe::Connection do
|
|
49
47
|
|
50
48
|
describe "#unbind" do
|
51
49
|
it "sets the closed-by-remote flag" do
|
52
|
-
conn.unbind
|
50
|
+
conn.unbind(nil)
|
53
51
|
conn.should be_closed
|
54
52
|
conn.should be_closed_by_remote
|
55
53
|
end
|
56
54
|
|
57
55
|
it "doesn't overwrite an existing closed-by flag" do
|
58
56
|
conn.stub :closed? => true
|
59
|
-
conn.unbind
|
57
|
+
conn.unbind(nil)
|
60
58
|
conn.should be_closed
|
61
59
|
conn.should_not be_closed_by_remote
|
62
60
|
end
|