serverside 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/spec/caching_spec.rb DELETED
@@ -1,318 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
- require 'stringio'
3
- include ServerSide::HTTP
4
-
5
- class DummyRequest < Request
6
- attr_accessor :socket, :persistent
7
- include Caching
8
-
9
- def initialize
10
- super(StringIO.new)
11
- @headers = {}
12
- end
13
- end
14
-
15
- context "Caching#disable_caching" do
16
- specify "should set the Cache-Control header to no-cache" do
17
- r = DummyRequest.new
18
- r.response_headers['Cache-Control'].should_be_nil
19
- r.disable_caching
20
- r.response_headers['Cache-Control'].should == 'no-cache'
21
- end
22
-
23
- specify "should remove all other cache-related headers" do
24
- r = DummyRequest.new
25
- r.response_headers['ETag'] = 'something'
26
- r.response_headers['Vary'] = 'something'
27
- r.response_headers['Expires'] = 'something'
28
- r.response_headers['Last-Modified'] = 'something'
29
- r.disable_caching
30
- r.response_headers['ETag'].should_be_nil
31
- r.response_headers['Vary'].should_be_nil
32
- r.response_headers['Expires'].should_be_nil
33
- r.response_headers['Last-Modified'].should_be_nil
34
- end
35
- end
36
-
37
- context "Caching#etag_validators" do
38
- specify "should return an empty array if no validators are present" do
39
- r = DummyRequest.new
40
- r.etag_validators.should == []
41
- end
42
-
43
- specify "should return an array containing all etag validators" do
44
- r = DummyRequest.new
45
- r.headers['If-None-Match'] = '"aaa-bbb"'
46
- r.etag_validators.should == ['aaa-bbb']
47
-
48
- r.headers['If-None-Match'] = '"aaa-bbb", "ccc-ddd"'
49
- r.etag_validators.should == ['aaa-bbb', 'ccc-ddd']
50
- end
51
-
52
- specify "should handle etags with and without quotes" do
53
- r = DummyRequest.new
54
- r.headers['If-None-Match'] = 'aaa-bbb'
55
- r.etag_validators.should == ['aaa-bbb']
56
-
57
- r.headers['If-None-Match'] = 'aaa-bbb, "ccc-ddd"'
58
- r.etag_validators.should == ['aaa-bbb', 'ccc-ddd']
59
- end
60
-
61
- specify "should handle a wildcard validator" do
62
- r = DummyRequest.new
63
- r.headers['If-None-Match'] = '*'
64
- r.etag_validators.should == ['*']
65
- end
66
- end
67
-
68
- context "Caching#valid_etag?" do
69
- specify "should return nil if no validator matches the specified etag" do
70
- r = DummyRequest.new
71
- r.valid_etag?('xxx-yyy').should_be_nil
72
-
73
- r.headers['If-None-Match'] = 'xx-yy, aaa-bbb'
74
- r.valid_etag?('xxx-yyy').should_be_nil
75
- end
76
-
77
- specify "should return true if a validator matches the specifed etag" do
78
- r = DummyRequest.new
79
-
80
- r.headers['If-None-Match'] = 'xxx-yyy'
81
- r.valid_etag?('xxx-yyy').should_be true
82
-
83
- r.headers['If-None-Match'] = '"xxx-yyy"'
84
- r.valid_etag?('xxx-yyy').should_be true
85
-
86
- r.headers['If-None-Match'] = 'aaa-bbb, xxx-yyy'
87
- r.valid_etag?('xxx-yyy').should_be true
88
-
89
- r.headers['If-None-Match'] = 'xxx-yyy, aaa-bbb'
90
- r.valid_etag?('xxx-yyy').should_be true
91
- end
92
-
93
- specify "should return true if a wildcard is included in If-None-Match" do
94
- r = DummyRequest.new
95
-
96
- r.headers['If-None-Match'] = '*'
97
- r.valid_etag?('xxx-yyy').should_be true
98
-
99
- r.headers['If-None-Match'] = 'aaa-bbb, *'
100
- r.valid_etag?('xxx-yyy').should_be true
101
- end
102
- end
103
-
104
- context "Caching#valid_etag? in expiry etag mode (no etag specified)" do
105
- specify "should return nil if no etag validator is included" do
106
- r = DummyRequest.new
107
- r.valid_etag?.should_be_nil
108
- end
109
-
110
- specify "should return true if If-None-Match includes a wildcard" do
111
- r = DummyRequest.new
112
-
113
- r.headers['If-None-Match'] = '*'
114
- r.valid_etag?.should_be true
115
- end
116
-
117
- specify "should ignore validators not formatted as expiry etags" do
118
- r = DummyRequest.new
119
-
120
- r.headers['If-None-Match'] = 'abcd'
121
- r.valid_etag?.should_be_nil
122
-
123
- r.headers['If-None-Match'] = 'xxx-yyy, zzz-zzz'
124
- r.valid_etag?.should_be_nil
125
- end
126
-
127
- specify "should parse expiry etags and check the expiration stamp" do
128
- r = DummyRequest.new
129
- t = Time.now
130
- fmt = Caching::EXPIRY_ETAG_FORMAT
131
-
132
- r.headers['If-None-Match'] = fmt % [t.to_i, (t - 20).to_i]
133
- r.valid_etag?.should_be_nil
134
-
135
- r.headers['If-None-Match'] = fmt % [t.to_i, (t + 20).to_i]
136
- r.valid_etag?.should_be true
137
-
138
- r.headers['If-None-Match'] = "xxx-yyy, #{fmt % [t.to_i, (t + 20).to_i]}, #{fmt % [t.to_i, (t - 20).to_i]}"
139
- r.valid_etag?.should_be true
140
- end
141
- end
142
-
143
- context "Caching#expiry_etag" do
144
- specify "should return an expiry etag with the stamp and expiration time" do
145
- r = DummyRequest.new
146
-
147
- t = Time.now
148
- fmt = Caching::EXPIRY_ETAG_FORMAT
149
- max_age = 54321
150
-
151
- r.expiry_etag(t, max_age).should == (fmt % [t.to_i, (t + max_age).to_i])
152
- end
153
- end
154
-
155
- context "Caching#valid_stamp?" do
156
- specify "should return nil if no If-Modified-Since header is included" do
157
- r = DummyRequest.new
158
- r.valid_stamp?(Time.now).should_be_nil
159
- end
160
-
161
- specify "should return nil if the If-Modified-Since header is different than the specified stamp" do
162
- t = Time.now
163
- r = DummyRequest.new
164
- r.headers['If-Modified-Since'] = t.httpdate
165
- r.valid_stamp?(t + 1).should_be_nil
166
- r.valid_stamp?(t - 1).should_be_nil
167
- end
168
-
169
- specify "should return true if the If-Modified-Since header matches the specified stamp" do
170
- t = Time.now
171
- r = DummyRequest.new
172
- r.headers['If-Modified-Since'] = t.httpdate
173
- r.valid_stamp?(t).should_be true
174
- end
175
- end
176
-
177
- context "Caching#validate_cache" do
178
- specify "should return nil if no validators are present" do
179
- r = DummyRequest.new
180
- r.validate_cache(Time.now, 360).should_be_nil
181
- end
182
-
183
- specify "should check for a stamp validator" do
184
- r = DummyRequest.new
185
- t = Time.now
186
-
187
- r.headers['If-Modified-Since'] = t.httpdate
188
- r.validate_cache(t + 1, 360).should_be_nil
189
- r.validate_cache(t - 1, 360).should_be_nil
190
- r.validate_cache(t, 360).should_be true
191
- end
192
-
193
- specify "should check for an etag validator" do
194
- r = DummyRequest.new
195
- t = Time.now
196
- etag = 'abcdef'
197
-
198
- r.validate_cache(t, 360, etag).should_be_nil
199
- r.headers['If-None-Match'] = 'aaa-bbb'
200
- r.validate_cache(t, 360, etag).should_be_nil
201
- r.headers['If-None-Match'] = "aaa-bbb, #{etag}"
202
- r.validate_cache(t, 360, etag).should_be true
203
- r.headers['If-None-Match'] = '*'
204
- r.validate_cache(t, 360, etag).should_be true
205
- end
206
-
207
- specify "should check for an expiry etag validator if etag is unspecified" do
208
- r = DummyRequest.new
209
- t = Time.now
210
- fmt = Caching::EXPIRY_ETAG_FORMAT
211
-
212
- r.headers['If-None-Match'] = 'aaa-bbb'
213
- r.validate_cache(t, 360).should_be_nil
214
- r.headers['If-None-Match'] = "aaa-bbb, #{fmt % [t.to_i, (t + 20).to_i]}"
215
- r.validate_cache(t, 360).should_be true
216
- r.headers['If-None-Match'] = '*'
217
- r.validate_cache(t, 360).should_be true
218
- end
219
-
220
- specify "should set the response headers with caching info if request did not validate" do
221
- r = DummyRequest.new
222
- t = Time.now
223
- r.validate_cache(t, 360, 'aaa-bbb', :public, 'Cookie')
224
- r.response_headers['ETag'].should == '"aaa-bbb"'
225
- r.response_headers['Last-Modified'].should == t.httpdate
226
- r.response_headers['Expires'].should == ((t + 360).httpdate)
227
- r.response_headers['Cache-Control'].should == :public
228
- r.response_headers['Vary'].should == 'Cookie'
229
- end
230
-
231
- specify "should set an expiry etag if no etag is specified" do
232
- r = DummyRequest.new
233
- t = Time.now
234
- fmt = Caching::EXPIRY_ETAG_FORMAT
235
- r.validate_cache(t, 360)
236
- r.response_headers['ETag'].should == (
237
- "\"#{fmt % [t.to_i, (t + 360).to_i]}\"")
238
- end
239
-
240
- specify "should send a 304 response if the cache validates" do
241
- r = DummyRequest.new
242
- t = Time.now
243
- fmt = Caching::EXPIRY_ETAG_FORMAT
244
-
245
- r.headers['If-None-Match'] = "aaa-bbb, #{fmt % [t.to_i, (t + 20).to_i]}"
246
- r.validate_cache(t, 360).should_be true
247
- r.socket.rewind
248
- resp = r.socket.read
249
- resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
250
-
251
- r = DummyRequest.new
252
- t = Time.now
253
-
254
- r.headers['If-Modified-Since'] = t.httpdate
255
- r.validate_cache(t, 360).should_be true
256
- r.socket.rewind
257
- resp = r.socket.read
258
- resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
259
- end
260
-
261
- specify "should not send anything if the cache doesn't validate" do
262
- r = DummyRequest.new
263
- t = Time.now
264
-
265
- r.validate_cache(t, 360).should_be_nil
266
- r.socket.rewind
267
- resp = r.socket.read
268
- resp.should_be_empty
269
- end
270
-
271
- specify "should not execute the given block if the cache validates" do
272
- r = DummyRequest.new
273
- t = Time.now
274
- r.headers['If-Modified-Since'] = t.httpdate
275
- proc {r.validate_cache(t, 360) {raise}}.should_not_raise
276
- end
277
-
278
- specify "should return the result of the given block if the cache doesn't validate" do
279
- x = nil
280
- l = proc {x = :executed}
281
-
282
- r = DummyRequest.new
283
- t = Time.now
284
- r.validate_cache(t, 360, &l).should == :executed
285
- x.should == :executed
286
- end
287
- end
288
-
289
- context "Caching#send_not_modified_response" do
290
- specify "should render a 304 response" do
291
- r = DummyRequest.new
292
- r.send_not_modified_response
293
- r.socket.rewind
294
- resp = r.socket.read
295
- resp.should_match /^HTTP\/1.1 304 Not Modified\r\n/
296
- resp.should_match /Content-Length: 0\r\n/
297
- resp.should_match /\r\n\r\n$/ # empty response body
298
- end
299
-
300
- specify "should exclude Connection header if persistent" do
301
- r = DummyRequest.new
302
- r.persistent = true
303
- r.send_not_modified_response
304
- r.socket.rewind
305
- resp = r.socket.read
306
- resp.should_not_match /Connection: close\r\n/
307
- end
308
-
309
- specify "should include Connection header if persistent" do
310
- r = DummyRequest.new
311
- r.persistent = false
312
- r.send_not_modified_response
313
- r.socket.rewind
314
- resp = r.socket.read
315
- resp.should_match /Connection: close\r\n/
316
- end
317
- end
318
-
data/spec/cluster_spec.rb DELETED
@@ -1,140 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
-
3
- __END__
4
-
5
- context "Daemon::Cluster::PidFile" do
6
- setup do
7
- @fn = Daemon::Cluster::PidFile::FN
8
- end
9
-
10
- specify "::FN should be the cluster's pid file" do
11
- Daemon::Cluster::PidFile::FN.should == 'serverside_cluster.pid'
12
- end
13
-
14
- specify "should delete the cluster's pid file" do
15
- FileUtils.touch(@fn)
16
- File.file?(@fn).should == true
17
- Daemon::Cluster::PidFile.delete
18
- File.file?(@fn).should == false
19
- end
20
-
21
- specify "should store multiple pids" do
22
- Daemon::Cluster::PidFile.delete
23
- Daemon::Cluster::PidFile.store_pid(1111)
24
- IO.read(@fn).should == "1111\n"
25
- Daemon::Cluster::PidFile.store_pid(2222)
26
- IO.read(@fn).should == "1111\n2222\n"
27
- end
28
-
29
- def test_pid_recall_pids
30
- Daemon::Cluster::PidFile.delete
31
- proc {Daemon::Cluster::PidFile.recall_pids}.should_raise Errno::ENOENT
32
- File.open(@fn, 'w') {|f| f.puts 3333; f.puts 4444}
33
- Daemon::Cluster::PidFile.recall_pids.should == [3333, 4444]
34
-
35
- FileUtils.rm(@fn)
36
- Daemon::Cluster::PidFile.store_pid(6666)
37
- Daemon::Cluster::PidFile.store_pid(7777)
38
- Daemon::Cluster::PidFile.recall_pids.should == [6666, 7777]
39
- end
40
- end
41
-
42
- class DummyCluster < Daemon::Cluster
43
- FN = 'result'
44
-
45
- def self.server_loop(port)
46
- at_exit {File.open(FN, 'a') {|f| f.puts port}}
47
- loop {sleep 10}
48
- end
49
-
50
- def self.ports
51
- 5555..5556
52
- end
53
- end
54
-
55
- context "Cluster.fork_server" do
56
- specify "should fork a server on the specified port" do
57
- FileUtils.rm(DummyCluster::FN) rescue nil
58
- port = rand(5_000)
59
- pid = DummyCluster.fork_server(port)
60
- sleep 1
61
- Process.kill('TERM', pid)
62
- sleep 0.1
63
- File.file?(DummyCluster::FN).should == true
64
- File.open(DummyCluster::FN, 'r') do |f|
65
- f.gets.to_i.should == port
66
- f.eof?.should == true
67
- end
68
- FileUtils.rm(DummyCluster::FN) rescue nil
69
- end
70
- end
71
-
72
- context "Cluster.start_servers" do
73
- specify "should start a cluster of servers" do
74
- FileUtils.rm(DummyCluster::FN) rescue nil
75
- DummyCluster.start_servers
76
- sleep 0.5
77
- pids = Daemon::Cluster::PidFile.recall_pids
78
- pids.length.should == 2
79
- pids.each {|pid| Process.kill('TERM', pid)}
80
- sleep 0.5
81
- File.open(DummyCluster::FN, 'r') do |f|
82
- p1, p2 = f.gets.to_i, f.gets.to_i
83
- DummyCluster.ports.include?(p1).should == true
84
- DummyCluster.ports.include?(p2).should == true
85
- p1.should_not == p2
86
- f.eof?.should == true
87
- end
88
- FileUtils.rm(DummyCluster::FN) rescue nil
89
- end
90
- end
91
-
92
- context "Cluster.stop_servers" do
93
- specify "should stop the cluster of servers" do
94
- DummyCluster.start_servers
95
- sleep 0.5
96
- pids = Daemon::Cluster::PidFile.recall_pids
97
- DummyCluster.stop_servers
98
- sleep 0.5
99
- File.file?(Daemon::Cluster::PidFile::FN).should == false
100
- File.open(DummyCluster::FN, 'r') do |f|
101
- p1, p2 = f.gets.to_i, f.gets.to_i
102
- DummyCluster.ports.include?(p1).should == true
103
- DummyCluster.ports.include?(p2).should == true
104
- p1.should_not == p2
105
- f.eof?.should == true
106
- end
107
- FileUtils.rm(DummyCluster::FN) rescue nil
108
- end
109
- end
110
-
111
- class DummyCluster2 < Daemon::Cluster
112
- def self.daemon_loop
113
- @@a = true
114
- end
115
-
116
- def self.start_servers
117
- @@b = true
118
- end
119
-
120
- def self.stop_servers
121
- @@c = true
122
- end
123
-
124
- def self.a; @@a; end
125
- def self.b; @@b; end
126
- def self.c; @@c; end
127
- end
128
-
129
- context "Cluster.start and stop" do
130
- specify "should start and stop the cluster daemon" do
131
- DummyCluster2.start
132
- DummyCluster2.a.should == true
133
- DummyCluster2.b.should == true
134
-
135
- proc {DummyCluster2.c}.should_raise
136
- DummyCluster2.stop
137
- DummyCluster2.c.should == true
138
- end
139
- end
140
-
@@ -1,59 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
-
3
- class ServerSide::HTTP::Connection
4
- attr_reader :socket, :request_class, :thread
5
- end
6
-
7
- $pause_request = false
8
-
9
- class DummyRequest1 < ServerSide::HTTP::Request
10
- @@instance_count = 0
11
-
12
- def initialize(socket)
13
- @@instance_count += 1
14
- super(socket)
15
- end
16
-
17
- def self.instance_count
18
- @@instance_count
19
- end
20
-
21
- def process
22
- sleep 0.1 while $pause_request
23
-
24
- @socket[:count] ||= 0
25
- @socket[:count] += 1
26
- @socket[:count] < 1000
27
- end
28
- end
29
-
30
- class DummySocket < Hash
31
- attr_accessor :closed
32
- def close; @closed = true; end
33
- end
34
-
35
- include ServerSide::HTTP
36
-
37
- context "Connection.initialize" do
38
- specify "should take two parameters: socket and request_class" do
39
- proc {Connection.new}.should_raise ArgumentError
40
- proc {Connection.new(nil)}.should_raise ArgumentError
41
- s = 'socket'
42
- r = 'request_class'
43
- c = Connection.new(s, r)
44
- c.socket.should_be s
45
- c.request_class.should_be r
46
- end
47
-
48
- specify "should spawn a thread that invokes Connection.process" do
49
- $pause_request = true
50
- c = Connection.new(DummySocket.new, DummyRequest1)
51
- c.thread.should_be_an_instance_of Thread
52
- c.thread.alive?.should == true
53
- DummyRequest1.instance_count.should == 1
54
- $pause_request = false
55
- sleep 0.1 while c.thread.alive?
56
- DummyRequest1.instance_count.should == 1000
57
- end
58
- end
59
-
@@ -1,142 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
- require 'stringio'
3
-
4
- class ServerSide::Router
5
- def self.rules
6
- @@rules
7
- end
8
-
9
- def self.reset_rules
10
- @@rules = []
11
- end
12
-
13
- attr_accessor :path
14
- end
15
-
16
- context "ServerSide::Controller.mount" do
17
- specify "should accept a routing rule as argument" do
18
- proc {ServerSide::Controller.mount}.should_raise ArgumentError
19
- end
20
-
21
- specify "should return a subclass of ServerSide::Controller" do
22
- c = ServerSide::Controller.mount(:path => '/test')
23
- c.should_be_a_kind_of Class
24
- c.superclass.should_be ServerSide::Controller
25
- end
26
-
27
- specify "should add a routing rule using ServerSide::Router.route" do
28
- ServerSide::Router.reset_rules
29
- rule = {:path => '/test'}
30
- c = ServerSide::Controller.mount(rule)
31
- sub_class = Class.new(c)
32
- r = ServerSide::Router.rules.first
33
- r.first.should == rule
34
- r.last.should_be_a_kind_of Proc
35
- c.module_eval do
36
- define_method(:initialize) {|req| $req = req}
37
- end
38
- res = r.last.call
39
- res.should_be_a_kind_of sub_class
40
-
41
- r = ServerSide::Router.new(StringIO.new)
42
- r.path = '/test'
43
- r.respond
44
- $req.should_be_a_kind_of ServerSide::Router
45
- end
46
-
47
- specify "should accept either an argument or block as the rule" do
48
- ServerSide::Router.reset_rules
49
- rule = {:path => '/test'}
50
- c = Class.new(ServerSide::Controller.mount(rule))
51
- r = ServerSide::Router.rules.first
52
- r.first.should_be rule
53
-
54
- ServerSide::Router.reset_rules
55
- rule = proc {true}
56
- c = Class.new(ServerSide::Controller.mount(&rule))
57
- r = ServerSide::Router.rules.first
58
- r.first.should_be rule
59
- end
60
- end
61
-
62
- class ServerSide::Controller
63
- attr_accessor :request, :path, :parameters, :rendered
64
- end
65
-
66
- class ServerSide::HTTP::Request
67
- attr_accessor :path, :parameters
68
- end
69
-
70
- require 'metaid'
71
-
72
- class DummyController < ServerSide::Controller
73
- attr_reader :response_called
74
-
75
- def response
76
- @response_called = true
77
- end
78
-
79
- def render_default
80
- @rendered = :default
81
- end
82
- end
83
-
84
- context "ServerSide::Controller new instance" do
85
- specify "should set @request, @path, and @parameters instance variables" do
86
- req = ServerSide::HTTP::Request.new(StringIO.new)
87
- req.path = '/aa/bb/cc'
88
- req.parameters = {:q => 'node_state', :f => 'xml'}
89
- c = ServerSide::Controller.new(req)
90
- c.request.should_be req
91
- c.path.should_be req.path
92
- c.parameters.should_be req.parameters
93
- end
94
-
95
- specify "should invoke the response method" do
96
- req = ServerSide::HTTP::Request.new(StringIO.new)
97
- c = DummyController.new(req)
98
- c.response_called.should_be true
99
- end
100
-
101
- specify "should invoke render_default unless @rendered" do
102
- req = ServerSide::HTTP::Request.new(StringIO.new)
103
- c = DummyController.new(req)
104
- c.rendered.should_be :default
105
-
106
- c_class = Class.new(DummyController) do
107
- define_method(:response) {@rendered = true}
108
- end
109
- c = c_class.new(req)
110
- c.rendered.should_be true
111
- end
112
- end
113
-
114
- context "ServerSide::Controller.render_default" do
115
- specify "should render a default 200 response" do
116
- req = ServerSide::HTTP::Request.new(StringIO.new)
117
- c = ServerSide::Controller.new(req)
118
- req.socket.rewind
119
- resp = req.socket.read
120
- resp.should_match /HTTP\/1\.1\s200/
121
- end
122
- end
123
-
124
- context "ServerSide::Controller.render" do
125
- specify "should render a 200 response with body and content type arguments" do
126
- req = ServerSide::HTTP::Request.new(StringIO.new)
127
- c = ServerSide::Controller.new(req)
128
- c.render('hello world', 'text/plain')
129
- req.socket.rewind
130
- resp = req.socket.read
131
- resp.should_match /Content-Type:\stext\/plain\r\n/
132
- end
133
-
134
- specify "should set @rendered to true" do
135
- req = ServerSide::HTTP::Request.new(StringIO.new)
136
- c = ServerSide::Controller.new(req)
137
- c.render('hello world', 'text/plain')
138
- c.rendered.should == true
139
- end
140
- end
141
-
142
-