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.
@@ -1,355 +1,125 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  require "spec_helper"
2
4
  require "hatetepe/server"
3
5
 
4
- describe Hatetepe::Server do
5
- let(:server) {
6
- Hatetepe::Server.allocate.tap {|s|
7
- s.send :initialize, config
8
- s.stub :comm_inactivity_timeout=
9
- s.post_init
10
- s.requests << request
11
- }
12
- }
13
- let(:request) { Hatetepe::Request.new :get, "/" }
14
- let(:env) { request.to_h }
15
-
16
- let(:app) { stub "app" }
17
- let(:host) { "127.0.4.1" }
18
- let(:port) { 8081 }
19
- let(:errors) { stub "errors", :<< => nil, :flush => nil }
20
- let(:config) {
21
- {
22
- :app => app,
23
- :host => host,
24
- :port => port,
25
- :errors => errors,
26
- :timeout => 0.0123
27
- }
28
- }
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
6
+ describe Hatetepe::Server, "(public API)" do
7
+ describe ".start(config, &app)" do
8
+ it "starts a server that listens on the supplied interface and port"
9
+ it "passes the config to incoming connections"
10
+ it "uses the passed block as app if any was passed"
37
11
  end
38
-
39
- it "inherits from Hatetepe::Connection" do
40
- server.should be_a(Hatetepe::Connection)
41
- end
42
-
43
- context ".start(config)" do
44
- it "starts an EventMachine server" do
45
- args = [host, port, Hatetepe::Server, config]
46
- EM.should_receive(:start_server).with(*args) { server }
47
-
48
- Hatetepe::Server.start(config).should equal(server)
49
- end
12
+
13
+ describe ".stop" do
14
+ it "waits for all requests to finish"
15
+ it "stops the server"
50
16
  end
51
-
52
- context "#initialize(config)" do
53
- let(:server) { Hatetepe::Server.allocate }
54
-
55
- it "sets up the error stream" do
56
- server.send :initialize, config
57
- server.errors.should equal(errors)
58
- server.config[:errors].should be_nil
59
- end
60
-
61
- it "uses stderr as default error stream" do
62
- config.delete :errors
63
- server.send :initialize, config
64
- server.errors.should equal($stderr)
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
17
+
18
+ describe ".stop!" do
19
+ it "immediately stops the server"
71
20
  end
72
-
73
- context "#post_init" do
74
- let :server do
75
- Hatetepe::Server.allocate.tap do |s|
76
- s.send :initialize, config
77
- s.stub :comm_inactivity_timeout=
21
+
22
+ describe "config[:app].call(env)" do
23
+ let :subject do
24
+ Object.new.tap do |s|
25
+ s.extend Hatetepe::Server
26
+ s.stub({
27
+ :config => {
28
+ :host => "127.0.5.1",
29
+ :port => 3000,
30
+ :app => stub("app")
31
+ },
32
+ :send_data => nil
33
+ })
34
+ s.stub(:comm_inactivity_timeout=)
35
+ s.post_init
78
36
  end
79
37
  end
80
-
81
- it "sets up the request queue" do
82
- server.post_init
83
- server.requests.should be_an(Array)
84
- server.requests.should be_empty
85
- end
86
-
87
- it "sets up the parser" do
88
- server.post_init
89
- server.parser.should respond_to(:<<)
90
- server.parser.on_request[0].should == server.requests.method(:<<)
91
- end
92
-
93
- it "sets up the builder" do
94
- server.post_init
95
- server.builder.on_write[0].should == server.method(:send_data)
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
116
- end
117
-
118
- context "#receive_data(data)" do
119
- before do
120
- ENV.delete "RACK_ENV"
121
- server.stub :close_connection
122
- end
123
-
124
- it "feeds data into the parser" do
125
- data = stub("data")
126
- server.parser.should_receive(:<<).with data
127
- server.receive_data data
128
- end
129
-
130
- it "closes the connection if parsing fails" do
131
- server.parser.should_receive(:<<).and_raise(Hatetepe::ParserError)
132
- server.should_receive :close_connection
133
-
134
- server.receive_data "irrelevant data"
135
- end
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
-
146
- it "closes the connection when catching an exception" do
147
- server.parser.should_receive(:<<).and_raise Exception
148
- server.should_receive :close_connection_after_writing
149
-
150
- server.receive_data ""
151
- end
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
-
160
- it "logs caught exceptions" do
161
- server.parser.should_receive(:<<).and_raise "error message"
162
- errors.should_receive(:<<) {|str|
163
- str.should include("error message")
164
- }
165
- errors.should_receive :flush
166
-
167
- server.receive_data ""
168
- end
169
- end
170
-
171
- context "#process" do
172
- before do
173
- request.stub :to_h => env
174
- app.stub :call => [-1]
175
- end
176
-
177
- it "puts useful stuff into env[]" do
178
- app.should_receive(:call) {|e|
179
- e.should equal(env)
180
- e["rack.url_scheme"].should == "http"
181
- e["hatetepe.connection"].should equal(server)
182
- e["rack.input"].source.should equal(server)
183
- e["rack.errors"].should equal(server.errors)
184
- e["rack.multithread"].should be_false
185
- e["rack.multiprocess"].should be_false
186
- e["rack.run_once"].should be_false
187
-
188
- e["SERVER_NAME"].should == host
189
- e["SERVER_NAME"].should_not equal(host)
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)
194
- e["HTTP_HOST"].should == "#{host}:#{port}"
195
-
196
- [-1]
197
- }
198
- server.process
199
- end
200
-
201
- it "calls the app within a new Fiber" do
202
- outer_fiber = Fiber.current
203
- app.should_receive(:call) {
204
- Fiber.current.should_not equal(outer_fiber)
205
- [-1]
206
- }
207
- server.process
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
235
- end
236
-
237
- context "env[stream.start].call(response)" do
238
- let(:previous) { EM::DefaultDeferrable.new }
239
- let(:response) {
240
- [200, {"Key" => "value"}, Rack::STREAMING]
241
- }
242
-
243
- before {
244
- server.requests.unshift previous
245
- app.stub(:call) {|e| response }
246
- request.stub :succeed
247
- server.builder.stub :response_line
248
- server.builder.stub :headers
249
- }
250
-
251
- it "deletes itself from env[] to prevent multiple calls" do
252
- app.stub(:call) {|e|
253
- e["stream.start"].call response
254
- e.key?("stream.start").should be_false
255
- [-1]
256
- }
257
- previous.succeed
258
- server.process
259
- end
260
-
261
- # TODO this should be moved to a Server::Pipeline spec
262
- it "waits for the previous request's response to finish" do
263
- server.builder.should_not_receive :response
264
- server.process
265
- end
266
- end
267
-
268
- context "env[stream.send].call(chunk)" do
269
- it "passes data to the builder" do
270
- app.stub(:call) {|e|
271
- e["stream.send"].should == server.builder.method(:body_chunk)
272
- [-1]
273
- }
274
- server.process
275
- end
276
- end
277
-
278
- context "env[stream.close].call" do
279
- before {
280
- server.builder.stub :complete
281
- request.stub :succeed
282
- }
283
-
284
- it "leaves the connection open" do
285
- server.should_not_receive :close_connection
286
- app.stub(:call) {|e|
287
- server.requests << stub("another request")
288
- e["stream.close"].call
289
- [-1]
290
- }
291
- server.process
38
+
39
+ let :app do
40
+ subject.config[:app]
292
41
  end
293
-
294
- it "deletes itself and stream.send from env[] to prevent multiple calls" do
295
- app.stub(:call) {|e|
296
- e["stream.close"].call
297
- e.key?("stream.send").should be_false
298
- e.key?("stream.close").should be_false
299
- [-1]
300
- }
301
- server.process
42
+
43
+ let :http_request do
44
+ [
45
+ "POST /foo/bar?key=value HTTP/1.1",
46
+ "Host: themachine.local",
47
+ "Content-Length: 13",
48
+ "",
49
+ "Hello, world!"
50
+ ].join("\r\n")
302
51
  end
303
- end
304
-
305
- context "#start_response(response)" do
306
- let(:previous) { EM::DefaultDeferrable.new }
307
- let(:response) { [200, {"Key" => "value"}, Rack::STREAMING] }
308
-
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])
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
326
- server.process
52
+
53
+ let :http_response do
54
+ [
55
+ "HTTP/1.1 403 Forbidden",
56
+ "Content-Type: text/plain",
57
+ "Transfer-Encoding: chunked",
58
+ "",
59
+ "b",
60
+ "Mmh, nöö.",
61
+ "0",
62
+ "",
63
+ ""
64
+ ].join("\r\n")
327
65
  end
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|
335
- e["stream.close"].call
336
- [-1]
66
+
67
+ it "receives the Rack Env hash as parameter" do
68
+ app.should_receive :call do |env|
69
+ Rack::Lint.new(app).check_env(env)
70
+ env["REQUEST_METHOD"].should == "POST"
71
+ env["REQUEST_URI"].should == "/foo/bar?key=value"
72
+ env["HTTP_HOST"].should == "themachine.local"
73
+ env["rack.input"].read.should == "Hello, world!"
74
+ [ 200, {}, [] ]
337
75
  end
76
+
77
+ subject.receive_data(http_request)
338
78
  end
339
-
340
- it "removes the request from the request queue" do
341
- server.process
342
- server.requests.should be_empty
343
- end
344
-
345
- it "completes the response" do
346
- server.builder.should_receive :complete
347
- server.process
79
+
80
+ it "returns a response array that will be sent to the client" do
81
+ app.should_receive :call do |env|
82
+ [ 403, { "Content-Type" => "text/plain" }, [ "Mmh, nöö." ] ]
83
+ end
84
+
85
+ sent = ""
86
+ subject.stub(:send_data) {|data| sent << data }
87
+
88
+ subject.receive_data(http_request)
89
+ sent.should == http_response
348
90
  end
349
-
350
- it "succeeds the request" do
351
- request.should_receive :succeed
352
- server.process
91
+
92
+ it "returns asynchronously" do
93
+ app.should_receive :call do |env|
94
+ EM::Synchrony.add_timer 0.5 do
95
+ response = [ 403, { "Content-Type" => "text/plain" }, [ "Mmh, nöö." ] ]
96
+ env["async.callback"].call(response)
97
+ end
98
+ [ -1, {}, [] ]
99
+ end
100
+
101
+ sent = ""
102
+ subject.stub(:send_data) {|data| sent << data }
103
+
104
+ subject.receive_data(http_request)
105
+ EM::Synchrony.sleep(0.55)
106
+ sent.should == http_response
353
107
  end
354
108
  end
355
109
  end
110
+
111
+ describe Hatetepe::Server, "(EventMachine/sermipublic API)" do
112
+ describe "#initialize(config)"
113
+
114
+ describe "#post_init"
115
+
116
+ describe "#receive_data(data)"
117
+
118
+ describe "#unbind(reason)"
119
+ end
120
+
121
+ describe Hatetepe::Server, "(private API)" do
122
+ describe "#process_request(request)"
123
+
124
+ describe "#send_response(response)"
125
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hatetepe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
5
- prerelease:
4
+ version: 0.5.0.pre
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Lars Gierth
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-13 00:00:00.000000000 Z
12
+ date: 2012-04-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: http_parser.rb
16
- requirement: &69946770 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,21 +21,31 @@ dependencies:
21
21
  version: 0.5.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *69946770
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.5.3
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: eventmachine
27
- requirement: &69946160 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
- - - ! '>='
35
+ - - ~>
31
36
  - !ruby/object:Gem::Version
32
- version: '0'
37
+ version: 1.0.0.beta.4
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *69946160
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0.beta.4
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: em-synchrony
38
- requirement: &69945450 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '1.0'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *69945450
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rack
49
- requirement: &69944990 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,21 +69,15 @@ dependencies:
54
69
  version: '0'
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *69944990
58
- - !ruby/object:Gem::Dependency
59
- name: async-rack
60
- requirement: &69944610 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
61
73
  none: false
62
74
  requirements:
63
75
  - - ! '>='
64
76
  - !ruby/object:Gem::Version
65
77
  version: '0'
66
- type: :runtime
67
- prerelease: false
68
- version_requirements: *69944610
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: thor
71
- requirement: &69883990 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
72
81
  none: false
73
82
  requirements:
74
83
  - - ! '>='
@@ -76,10 +85,15 @@ dependencies:
76
85
  version: '0'
77
86
  type: :runtime
78
87
  prerelease: false
79
- version_requirements: *69883990
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
80
94
  - !ruby/object:Gem::Dependency
81
95
  name: rspec
82
- requirement: &69883740 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
83
97
  none: false
84
98
  requirements:
85
99
  - - ! '>='
@@ -87,21 +101,15 @@ dependencies:
87
101
  version: '0'
88
102
  type: :development
89
103
  prerelease: false
90
- version_requirements: *69883740
91
- - !ruby/object:Gem::Dependency
92
- name: fakefs
93
- requirement: &69883520 !ruby/object:Gem::Requirement
104
+ version_requirements: !ruby/object:Gem::Requirement
94
105
  none: false
95
106
  requirements:
96
107
  - - ! '>='
97
108
  - !ruby/object:Gem::Version
98
109
  version: '0'
99
- type: :development
100
- prerelease: false
101
- version_requirements: *69883520
102
110
  - !ruby/object:Gem::Dependency
103
111
  name: yard
104
- requirement: &69883290 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
105
113
  none: false
106
114
  requirements:
107
115
  - - ! '>='
@@ -109,10 +117,15 @@ dependencies:
109
117
  version: '0'
110
118
  type: :development
111
119
  prerelease: false
112
- version_requirements: *69883290
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
113
126
  - !ruby/object:Gem::Dependency
114
127
  name: rdiscount
115
- requirement: &69883080 !ruby/object:Gem::Requirement
128
+ requirement: !ruby/object:Gem::Requirement
116
129
  none: false
117
130
  requirements:
118
131
  - - ! '>='
@@ -120,7 +133,12 @@ dependencies:
120
133
  version: '0'
121
134
  type: :development
122
135
  prerelease: false
123
- version_requirements: *69883080
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
124
142
  description:
125
143
  email:
126
144
  - lars.gierth@gmail.com
@@ -134,14 +152,13 @@ files:
134
152
  - README.md
135
153
  - Rakefile
136
154
  - bin/hatetepe
155
+ - examples/parallel_requests.rb
137
156
  - hatetepe.gemspec
138
157
  - lib/hatetepe.rb
139
158
  - lib/hatetepe/body.rb
140
159
  - lib/hatetepe/builder.rb
141
160
  - lib/hatetepe/cli.rb
142
161
  - lib/hatetepe/client.rb
143
- - lib/hatetepe/client/keep_alive.rb
144
- - lib/hatetepe/client/pipeline.rb
145
162
  - lib/hatetepe/connection.rb
146
163
  - lib/hatetepe/deferred_status_fix.rb
147
164
  - lib/hatetepe/events.rb
@@ -150,25 +167,23 @@ files:
150
167
  - lib/hatetepe/request.rb
151
168
  - lib/hatetepe/response.rb
152
169
  - lib/hatetepe/server.rb
153
- - lib/hatetepe/server/app.rb
154
170
  - lib/hatetepe/server/keep_alive.rb
155
171
  - lib/hatetepe/server/pipeline.rb
156
- - lib/hatetepe/server/proxy.rb
172
+ - lib/hatetepe/server/rack_app.rb
157
173
  - lib/hatetepe/version.rb
158
174
  - lib/rack/handler/hatetepe.rb
159
175
  - spec/integration/cli/start_spec.rb
160
176
  - spec/integration/client/keep_alive_spec.rb
177
+ - spec/integration/client/timeout_spec.rb
161
178
  - spec/integration/server/keep_alive_spec.rb
179
+ - spec/integration/server/timeout_spec.rb
162
180
  - spec/spec_helper.rb
163
- - spec/unit/app_spec.rb
164
181
  - spec/unit/body_spec.rb
165
182
  - spec/unit/builder_spec.rb
166
- - spec/unit/client/pipeline_spec.rb
167
183
  - spec/unit/client_spec.rb
168
184
  - spec/unit/connection_spec.rb
169
185
  - spec/unit/events_spec.rb
170
186
  - spec/unit/parser_spec.rb
171
- - spec/unit/proxy_spec.rb
172
187
  - spec/unit/rack_handler_spec.rb
173
188
  - spec/unit/server_spec.rb
174
189
  homepage: https://github.com/lgierth/hatetepe
@@ -186,12 +201,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
201
  required_rubygems_version: !ruby/object:Gem::Requirement
187
202
  none: false
188
203
  requirements:
189
- - - ! '>='
204
+ - - ! '>'
190
205
  - !ruby/object:Gem::Version
191
- version: '0'
206
+ version: 1.3.1
192
207
  requirements: []
193
208
  rubyforge_project:
194
- rubygems_version: 1.8.12
209
+ rubygems_version: 1.8.21
195
210
  signing_key:
196
211
  specification_version: 3
197
212
  summary: The HTTP toolkit