serverside 0.3.1 → 0.4.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 +15 -11
- data/Rakefile +18 -18
- data/bin/serverside +20 -16
- data/lib/serverside/cluster.rb +4 -33
- data/lib/serverside/core_ext.rb +56 -7
- data/lib/serverside/daemon.rb +10 -17
- data/lib/serverside/http/caching.rb +79 -0
- data/lib/serverside/http/const.rb +69 -0
- data/lib/serverside/http/error.rb +24 -0
- data/lib/serverside/http/parsing.rb +175 -0
- data/lib/serverside/http/response.rb +91 -0
- data/lib/serverside/http/server.rb +194 -0
- data/lib/serverside/http/static.rb +72 -0
- data/lib/serverside/http.rb +14 -0
- data/lib/serverside/js.rb +173 -0
- data/lib/serverside/log.rb +79 -0
- data/lib/serverside/template.rb +5 -4
- data/lib/serverside/xml.rb +84 -0
- data/lib/serverside.rb +11 -2
- data/spec/core_ext_spec.rb +13 -58
- data/spec/daemon_spec.rb +61 -28
- data/spec/http_spec.rb +259 -0
- data/spec/template_spec.rb +9 -7
- metadata +42 -28
- data/CHANGELOG +0 -261
- data/lib/serverside/application.rb +0 -26
- data/lib/serverside/caching.rb +0 -91
- data/lib/serverside/connection.rb +0 -34
- data/lib/serverside/controllers.rb +0 -91
- data/lib/serverside/request.rb +0 -210
- data/lib/serverside/routing.rb +0 -133
- data/lib/serverside/server.rb +0 -27
- data/lib/serverside/static.rb +0 -82
- data/spec/caching_spec.rb +0 -318
- data/spec/cluster_spec.rb +0 -140
- data/spec/connection_spec.rb +0 -59
- data/spec/controllers_spec.rb +0 -142
- data/spec/request_spec.rb +0 -288
- data/spec/routing_spec.rb +0 -240
- data/spec/server_spec.rb +0 -40
- data/spec/static_spec.rb +0 -279
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
|
-
|
data/spec/connection_spec.rb
DELETED
@@ -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
|
-
|
data/spec/controllers_spec.rb
DELETED
@@ -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
|
-
|