serverside 0.2.9 → 0.3.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/CHANGELOG +56 -0
- data/Rakefile +12 -52
- data/bin/serverside +1 -1
- data/lib/serverside/application.rb +2 -1
- data/lib/serverside/caching.rb +62 -50
- data/lib/serverside/controllers.rb +91 -0
- data/lib/serverside/core_ext.rb +6 -0
- data/lib/serverside/daemon.rb +25 -5
- data/lib/serverside/request.rb +17 -11
- data/lib/serverside/routing.rb +11 -10
- data/lib/serverside/server.rb +14 -6
- data/lib/serverside/static.rb +7 -18
- data/lib/serverside/template.rb +20 -12
- data/spec/caching_spec.rb +318 -0
- data/spec/cluster_spec.rb +140 -0
- data/{test/spec → spec}/connection_spec.rb +4 -4
- data/{test/spec/controller_spec.rb → spec/controllers_spec.rb} +15 -12
- data/{test/spec → spec}/core_ext_spec.rb +23 -18
- data/spec/daemon_spec.rb +99 -0
- data/{test/spec → spec}/request_spec.rb +45 -45
- data/spec/routing_spec.rb +240 -0
- data/spec/server_spec.rb +40 -0
- data/spec/static_spec.rb +279 -0
- data/spec/template_spec.rb +129 -0
- metadata +21 -35
- data/lib/serverside/controller.rb +0 -67
- data/test/functional/primitive_static_server_test.rb +0 -61
- data/test/functional/request_body_test.rb +0 -93
- data/test/functional/routing_server.rb +0 -14
- data/test/functional/routing_server_test.rb +0 -41
- data/test/functional/static_profile.rb +0 -17
- data/test/functional/static_rfuzz.rb +0 -31
- data/test/functional/static_server.rb +0 -7
- data/test/functional/static_server_test.rb +0 -31
- data/test/spec/caching_spec.rb +0 -139
- data/test/test_helper.rb +0 -2
- data/test/unit/cluster_test.rb +0 -129
- data/test/unit/connection_test.rb +0 -48
- data/test/unit/core_ext_test.rb +0 -46
- data/test/unit/daemon_test.rb +0 -75
- data/test/unit/request_test.rb +0 -278
- data/test/unit/routing_test.rb +0 -171
- data/test/unit/server_test.rb +0 -28
- data/test/unit/static_test.rb +0 -171
- data/test/unit/template_test.rb +0 -78
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../lib/serverside')
|
2
|
+
require 'stringio'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
include ServerSide::HTTP
|
6
|
+
|
7
|
+
$http_connection_created = false
|
8
|
+
|
9
|
+
class Connection
|
10
|
+
alias_method :orig_initialize, :initialize
|
11
|
+
def initialize(socket, request_class)
|
12
|
+
orig_initialize(socket, request_class)
|
13
|
+
if (request_class == ServerSide::HTTP::Request) && socket.is_a?(TCPSocket)
|
14
|
+
$http_connection_created = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "HTTP::Server" do
|
20
|
+
specify "should open TCP port for listening" do
|
21
|
+
server = Server.new('0.0.0.0', 17863, Request)
|
22
|
+
t = Thread.new {server.start}
|
23
|
+
proc {TCPServer.new('0.0.0.0', 17863)}.should_raise Errno::EADDRINUSE
|
24
|
+
t.exit
|
25
|
+
t.alive?.should_be false
|
26
|
+
server.listener.close
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should loop indefinitely, accepting connections" do
|
30
|
+
$http_connection_created = false
|
31
|
+
server = Server.new('0.0.0.0', 17863, Request)
|
32
|
+
t = Thread.new {server.start}
|
33
|
+
sleep 0.2
|
34
|
+
s = nil
|
35
|
+
proc {s = TCPSocket.new('localhost', 17863)}.should_not_raise
|
36
|
+
sleep 0.2
|
37
|
+
$http_connection_created.should == true
|
38
|
+
server.listener.close
|
39
|
+
end
|
40
|
+
end
|
data/spec/static_spec.rb
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../lib/serverside')
|
2
|
+
require 'stringio'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class Dummy < ServerSide::HTTP::Request
|
6
|
+
def self.mime_types
|
7
|
+
@@mime_types
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :path, :socket, :headers
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super(nil)
|
14
|
+
@headers = {}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
include ServerSide
|
19
|
+
|
20
|
+
context "Static.mime_types" do
|
21
|
+
specify "should be a hash" do
|
22
|
+
Dummy.mime_types.should_be_a_kind_of Hash
|
23
|
+
end
|
24
|
+
|
25
|
+
specify "should return text/plain as the default mime type" do
|
26
|
+
Dummy.mime_types['.rb'].should == 'text/plain'
|
27
|
+
Dummy.mime_types['.invalid'].should == 'text/plain'
|
28
|
+
end
|
29
|
+
|
30
|
+
specify "should return the correct mime type for common files" do
|
31
|
+
Dummy.mime_types['.html'].should == 'text/html'
|
32
|
+
Dummy.mime_types['.css'].should == 'text/css'
|
33
|
+
Dummy.mime_types['.js'].should == 'text/javascript'
|
34
|
+
Dummy.mime_types['.gif'].should == 'image/gif'
|
35
|
+
Dummy.mime_types['.jpg'].should == 'image/jpeg'
|
36
|
+
Dummy.mime_types['.jpeg'].should == 'image/jpeg'
|
37
|
+
Dummy.mime_types['.png'].should == 'image/png'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "Static.serve_file" do
|
42
|
+
specify "should render correctly with file content, etag and size" do
|
43
|
+
c = Dummy.new
|
44
|
+
c.socket = StringIO.new
|
45
|
+
c.serve_file(__FILE__)
|
46
|
+
c.socket.rewind
|
47
|
+
resp = c.socket.read
|
48
|
+
|
49
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
50
|
+
fc = IO.read(__FILE__)
|
51
|
+
stat = File.stat(__FILE__)
|
52
|
+
etag = (ServerSide::Static::ETAG_FORMAT %
|
53
|
+
[stat.mtime.to_i, stat.size, stat.ino])
|
54
|
+
resp.should_match /ETag:\s"#{etag}"\r\n/
|
55
|
+
resp.should_match /Content-Length:\s#{stat.size.to_s}\r\n/
|
56
|
+
end
|
57
|
+
|
58
|
+
specify "should send a not modified response only if a correct etag is specified in the request" do
|
59
|
+
stat = File.stat(__FILE__)
|
60
|
+
etag = (ServerSide::Static::ETAG_FORMAT %
|
61
|
+
[stat.mtime.to_i, stat.size, stat.ino])
|
62
|
+
|
63
|
+
# normal response
|
64
|
+
c = Dummy.new
|
65
|
+
c.socket = StringIO.new
|
66
|
+
c.serve_file(__FILE__)
|
67
|
+
c.socket.rewind
|
68
|
+
resp = c.socket.read
|
69
|
+
|
70
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
71
|
+
resp.should_match /ETag:\s"#{etag}"\r\n/
|
72
|
+
resp.should_match /Content-Length:\s#{stat.size.to_s}\r\n/
|
73
|
+
|
74
|
+
# normal response (invalid etag)
|
75
|
+
c = Dummy.new
|
76
|
+
c.socket = StringIO.new
|
77
|
+
c.headers['If-None-Match'] = "\"xxx-yyy\""
|
78
|
+
c.serve_file(__FILE__)
|
79
|
+
c.socket.rewind
|
80
|
+
resp = c.socket.read
|
81
|
+
|
82
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
83
|
+
resp.should_match /ETag:\s"#{etag}"\r\n/
|
84
|
+
resp.should_match /Content-Length:\s#{stat.size.to_s}\r\n/
|
85
|
+
|
86
|
+
# not modified (etag specified)
|
87
|
+
c.socket = StringIO.new
|
88
|
+
c.headers['If-None-Match'] = "\"#{etag}\""
|
89
|
+
c.valid_etag?(etag).should_be true
|
90
|
+
c.serve_file(__FILE__)
|
91
|
+
c.socket.rewind
|
92
|
+
resp = c.socket.read
|
93
|
+
|
94
|
+
resp.should_match /HTTP\/1.1\s304(.*)\r\n/
|
95
|
+
|
96
|
+
# modified response (file stamp changed)
|
97
|
+
FileUtils.touch(__FILE__)
|
98
|
+
c.socket = StringIO.new
|
99
|
+
c.headers['If-None-Match'] = etag
|
100
|
+
c.serve_file(__FILE__)
|
101
|
+
c.socket.rewind
|
102
|
+
resp = c.socket.read
|
103
|
+
|
104
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
105
|
+
stat = File.stat(__FILE__)
|
106
|
+
etag = (ServerSide::Static::ETAG_FORMAT %
|
107
|
+
[stat.mtime.to_i, stat.size, stat.ino])
|
108
|
+
resp.should_match /ETag:\s"#{etag}"\r\n/
|
109
|
+
resp.should_match /Content-Length:\s#{stat.size.to_s}\r\n/
|
110
|
+
end
|
111
|
+
|
112
|
+
specify "should serve a 404 response for invalid files" do
|
113
|
+
c = Dummy.new
|
114
|
+
c.socket = StringIO.new
|
115
|
+
c.serve_file('invalid_file.html')
|
116
|
+
c.socket.rewind
|
117
|
+
resp = c.socket.read
|
118
|
+
|
119
|
+
resp.should_match /HTTP\/1.1\s404(.*)\r\n/
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class IO
|
124
|
+
def self.write(fn, content)
|
125
|
+
File.open(fn, 'w') {|f| f << content}
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class ServerSide::Template
|
130
|
+
def self.reset
|
131
|
+
@@templates = {}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
context "Static.serve_template" do
|
137
|
+
specify "should render .rhtml file as template" do
|
138
|
+
IO.write('tmp.rhtml', '<%= @t %>')
|
139
|
+
@t = Time.now.to_f
|
140
|
+
c = Dummy.new
|
141
|
+
c.socket = StringIO.new
|
142
|
+
c.serve_template('tmp.rhtml', binding)
|
143
|
+
c.socket.rewind
|
144
|
+
resp = c.socket.read
|
145
|
+
resp.should.match /\r\n\r\n#{@t}$/
|
146
|
+
FileUtils.rm('tmp.rhtml')
|
147
|
+
end
|
148
|
+
|
149
|
+
specify "should use its own binding when none is specified" do
|
150
|
+
Template.reset
|
151
|
+
IO.write('tmp.rhtml', '<%= @path %>')
|
152
|
+
|
153
|
+
c = Dummy.new
|
154
|
+
c.socket = StringIO.new
|
155
|
+
c.path = '/test/hey'
|
156
|
+
c.serve_template('tmp.rhtml')
|
157
|
+
c.socket.rewind
|
158
|
+
resp = c.socket.read
|
159
|
+
resp.should.match /\r\n\r\n\/test\/hey$/
|
160
|
+
FileUtils.rm('tmp.rhtml')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "Static.serve_dir" do
|
165
|
+
specify "should render a directory with all its entries" do
|
166
|
+
dir = File.dirname(__FILE__)
|
167
|
+
|
168
|
+
c = Dummy.new
|
169
|
+
c.socket = StringIO.new
|
170
|
+
c.path = dir
|
171
|
+
c.serve_dir(dir)
|
172
|
+
c.socket.rewind
|
173
|
+
resp = c.socket.read
|
174
|
+
|
175
|
+
Dir.entries(dir).each do |fn|
|
176
|
+
next if fn =~ /^\./
|
177
|
+
resp.should_match /<a href="#{dir/fn}">(#{fn})<\/a>/
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "Static.serve_static" do
|
183
|
+
specify "should serve directories" do
|
184
|
+
dir = File.dirname(__FILE__)
|
185
|
+
|
186
|
+
c = Dummy.new
|
187
|
+
c.socket = StringIO.new
|
188
|
+
c.path = dir
|
189
|
+
c.serve_static(dir)
|
190
|
+
c.socket.rewind
|
191
|
+
resp = c.socket.read
|
192
|
+
|
193
|
+
Dir.entries(dir).each do |fn|
|
194
|
+
next if fn =~ /^\./
|
195
|
+
resp.should_match /<a href="#{dir/fn}">(#{fn})<\/a>/
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
specify "should serve files" do
|
200
|
+
c = Dummy.new
|
201
|
+
c.socket = StringIO.new
|
202
|
+
c.serve_static(__FILE__)
|
203
|
+
c.socket.rewind
|
204
|
+
resp = c.socket.read
|
205
|
+
|
206
|
+
# normal response
|
207
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
208
|
+
stat = File.stat(__FILE__)
|
209
|
+
etag = (ServerSide::Static::ETAG_FORMAT %
|
210
|
+
[stat.mtime.to_i, stat.size, stat.ino])
|
211
|
+
resp.should_match /ETag:\s"#{etag}"\r\n/
|
212
|
+
resp.should_match /Content-Length:\s#{stat.size.to_s}\r\n/
|
213
|
+
end
|
214
|
+
|
215
|
+
specify "should serve templates" do
|
216
|
+
Template.reset
|
217
|
+
IO.write('tmp.rhtml', '<%= 1 + 1%>')
|
218
|
+
|
219
|
+
c = Dummy.new
|
220
|
+
c.socket = StringIO.new
|
221
|
+
c.serve_static('tmp.rhtml')
|
222
|
+
c.socket.rewind
|
223
|
+
resp = c.socket.read
|
224
|
+
|
225
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
226
|
+
resp.should_match /\r\n\r\n2$/
|
227
|
+
|
228
|
+
FileUtils.rm('tmp.rhtml')
|
229
|
+
end
|
230
|
+
|
231
|
+
specify "should serve index.html if exists in directory path" do
|
232
|
+
dir = File.dirname(__FILE__)/:tmp_dir
|
233
|
+
FileUtils.mkdir(dir) rescue nil
|
234
|
+
begin
|
235
|
+
IO.write(dir/'index.html', '<h1>HI</h1>')
|
236
|
+
c = Dummy.new
|
237
|
+
c.socket = StringIO.new
|
238
|
+
c.serve_static(dir)
|
239
|
+
c.socket.rewind
|
240
|
+
resp = c.socket.read
|
241
|
+
|
242
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
243
|
+
resp.should_match /\r\n\r\n<h1>HI<\/h1>$/
|
244
|
+
resp.should_match /Content-Type: text\/html/
|
245
|
+
ensure
|
246
|
+
FileUtils.rmtree(dir) rescue nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
specify "should serve index.rhtml if exists in directory path" do
|
251
|
+
dir = File.dirname(__FILE__)/:tmp_dir
|
252
|
+
FileUtils.mkdir(dir) rescue puts "dir already exists"
|
253
|
+
begin
|
254
|
+
IO.write(dir/'index.rhtml', '<h1><%= @path %></h1>')
|
255
|
+
c = Dummy.new
|
256
|
+
c.socket = StringIO.new
|
257
|
+
c.path = dir
|
258
|
+
c.serve_static(dir)
|
259
|
+
c.socket.rewind
|
260
|
+
resp = c.socket.read
|
261
|
+
|
262
|
+
resp.should_match /HTTP\/1.1\s200(.*)\r\n/
|
263
|
+
resp.should_match /\r\n\r\n<h1>#{dir}<\/h1>$/
|
264
|
+
resp.should_match /Content-Type: text\/html/
|
265
|
+
ensure
|
266
|
+
FileUtils.rmtree(dir) rescue nil
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
specify "should serve a 404 response for invalid files" do
|
271
|
+
c = Dummy.new
|
272
|
+
c.socket = StringIO.new
|
273
|
+
c.serve_static('invalid_file.html')
|
274
|
+
c.socket.rewind
|
275
|
+
resp = c.socket.read
|
276
|
+
|
277
|
+
resp.should_match /HTTP\/1.1\s404(.*)\r\n/
|
278
|
+
end
|
279
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../lib/serverside')
|
2
|
+
require 'stringio'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
class ServerSide::Template
|
6
|
+
def self.templates
|
7
|
+
@@templates
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.reset
|
11
|
+
@@templates = {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class IO
|
16
|
+
def self.write(fn, content)
|
17
|
+
File.open(fn, 'w') {|f| f << content}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include ServerSide
|
22
|
+
|
23
|
+
context "ServerSide::Template.set" do
|
24
|
+
specify "should add an consisting of stamp and template to the templates hash" do
|
25
|
+
Template.reset
|
26
|
+
Template.templates.should_be_empty
|
27
|
+
t = Time.now
|
28
|
+
Template.set('test', 'hello', t)
|
29
|
+
Template.templates.include?('test').should_be true
|
30
|
+
a = Template.templates['test']
|
31
|
+
a.should_be_a_kind_of Array
|
32
|
+
a.size.should == 2
|
33
|
+
a.first.should_be t
|
34
|
+
a.last.should_be_a_kind_of ERB
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "should set stamp to nil by default" do
|
38
|
+
Template.set('test', 'hello')
|
39
|
+
Template.templates['test'].first.should_be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
specify "should construct a new ERB instance with the body" do
|
43
|
+
Template.set('test', 'yo')
|
44
|
+
Template.templates['test'].last.result(binding).should == 'yo'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "ServerSide::Template.validate" do
|
49
|
+
specify "should return nil for a non-existant template" do
|
50
|
+
Template.reset
|
51
|
+
Template.validate('test').should_be_nil
|
52
|
+
Template.validate('invalid_file_ref').should_be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
specify "should load a file as template if the name references a file" do
|
56
|
+
Template.reset
|
57
|
+
t = Template.validate(__FILE__)
|
58
|
+
t.should_be_a_kind_of ERB
|
59
|
+
t.result(binding).should == IO.read(__FILE__)
|
60
|
+
Template.templates.size.should == 1
|
61
|
+
t = Template.templates[__FILE__]
|
62
|
+
t.first.should == File.mtime(__FILE__)
|
63
|
+
t.last.should_be_a_kind_of ERB
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "should return the ERB instance for an existing template" do
|
67
|
+
Template.reset
|
68
|
+
t = Template.validate(__FILE__)
|
69
|
+
t.should_be_a_kind_of ERB
|
70
|
+
t.result(binding).should == IO.read(__FILE__)
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "should reload a file if its stamp changed" do
|
74
|
+
Template.reset
|
75
|
+
IO.write('tmp', '1')
|
76
|
+
Template.validate('tmp').result(binding).should == '1'
|
77
|
+
Template.templates['tmp'].first.should == File.mtime('tmp')
|
78
|
+
sleep 1.5
|
79
|
+
IO.write('tmp', '2')
|
80
|
+
Template.validate('tmp').result(binding).should == '2'
|
81
|
+
Template.templates['tmp'].first.should == File.mtime('tmp')
|
82
|
+
FileUtils.rm('tmp')
|
83
|
+
end
|
84
|
+
|
85
|
+
specify "should return nil and clear the cache if a cached file has been deleted" do
|
86
|
+
Template.reset
|
87
|
+
IO.write('tmp', '1')
|
88
|
+
Template.validate('tmp').result(binding).should == '1'
|
89
|
+
Template.templates['tmp'].first.should == File.mtime('tmp')
|
90
|
+
FileUtils.rm('tmp')
|
91
|
+
Template.validate('tmp').should_be_nil
|
92
|
+
Template.templates['tmp'].should_be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "ServerSide::Template.render" do
|
97
|
+
specify "should raise a RuntimeError for an invalid template" do
|
98
|
+
Template.reset
|
99
|
+
proc {Template.render('invalid', binding)}.should_raise RuntimeError
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "should render an existing ad-hoc template" do
|
103
|
+
Template.reset
|
104
|
+
Template.set('test', 'hello there')
|
105
|
+
Template.render('test', binding).should == 'hello there'
|
106
|
+
end
|
107
|
+
|
108
|
+
specify "should render a file-based template" do
|
109
|
+
Template.reset
|
110
|
+
Template.render(__FILE__, binding).should == IO.read(__FILE__)
|
111
|
+
end
|
112
|
+
|
113
|
+
specify "should validate a file-based template by checking its stamp" do
|
114
|
+
Template.reset
|
115
|
+
IO.write('tmp', '1')
|
116
|
+
Template.render('tmp', binding).should == '1'
|
117
|
+
sleep 1.5
|
118
|
+
IO.write('tmp', '2')
|
119
|
+
Template.render('tmp', binding).should == '2'
|
120
|
+
FileUtils.rm('tmp')
|
121
|
+
end
|
122
|
+
|
123
|
+
specify "should pass the binding to the ERB instance for processing" do
|
124
|
+
@x = 23
|
125
|
+
Template.reset
|
126
|
+
Template.set('test', '<' + '%= @x %' + '>')
|
127
|
+
Template.render('test', binding).should == '23'
|
128
|
+
end
|
129
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: serverside
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2006-11-27 00:00:00 +02:00
|
8
8
|
summary: Performance-oriented web framework.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,46 +33,32 @@ files:
|
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
35
|
- bin/serverside
|
36
|
-
-
|
37
|
-
-
|
38
|
-
-
|
39
|
-
-
|
40
|
-
-
|
41
|
-
-
|
42
|
-
-
|
43
|
-
-
|
44
|
-
-
|
45
|
-
-
|
46
|
-
-
|
47
|
-
-
|
48
|
-
- test/unit/template_test.rb
|
49
|
-
- test/functional/routing_server.rb
|
50
|
-
- test/functional/static_server.rb
|
51
|
-
- test/functional/primitive_static_server_test.rb
|
52
|
-
- test/functional/request_body_test.rb
|
53
|
-
- test/functional/static_profile.rb
|
54
|
-
- test/functional/static_rfuzz.rb
|
55
|
-
- test/functional/static_server_test.rb
|
56
|
-
- test/functional/routing_server_test.rb
|
57
|
-
- test/spec/controller_spec.rb
|
58
|
-
- test/spec/core_ext_spec.rb
|
59
|
-
- test/spec/request_spec.rb
|
60
|
-
- test/spec/caching_spec.rb
|
61
|
-
- test/spec/connection_spec.rb
|
62
|
-
- lib/serverside
|
36
|
+
- doc/rdoc
|
37
|
+
- spec/core_ext_spec.rb
|
38
|
+
- spec/connection_spec.rb
|
39
|
+
- spec/caching_spec.rb
|
40
|
+
- spec/request_spec.rb
|
41
|
+
- spec/template_spec.rb
|
42
|
+
- spec/static_spec.rb
|
43
|
+
- spec/server_spec.rb
|
44
|
+
- spec/routing_spec.rb
|
45
|
+
- spec/controllers_spec.rb
|
46
|
+
- spec/daemon_spec.rb
|
47
|
+
- spec/cluster_spec.rb
|
63
48
|
- lib/serverside.rb
|
64
|
-
- lib/serverside
|
65
|
-
- lib/serverside/
|
49
|
+
- lib/serverside
|
50
|
+
- lib/serverside/server.rb
|
66
51
|
- lib/serverside/daemon.rb
|
67
52
|
- lib/serverside/application.rb
|
68
|
-
- lib/serverside/
|
69
|
-
- lib/serverside/
|
53
|
+
- lib/serverside/core_ext.rb
|
54
|
+
- lib/serverside/connection.rb
|
55
|
+
- lib/serverside/cluster.rb
|
70
56
|
- lib/serverside/static.rb
|
71
57
|
- lib/serverside/routing.rb
|
72
|
-
- lib/serverside/core_ext.rb
|
73
|
-
- lib/serverside/request.rb
|
74
58
|
- lib/serverside/template.rb
|
59
|
+
- lib/serverside/request.rb
|
75
60
|
- lib/serverside/caching.rb
|
61
|
+
- lib/serverside/controllers.rb
|
76
62
|
- CHANGELOG
|
77
63
|
test_files: []
|
78
64
|
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'routing')
|
2
|
-
|
3
|
-
module ServerSide
|
4
|
-
class Controller
|
5
|
-
def self.mount(rule = nil, &block)
|
6
|
-
rule ||= block
|
7
|
-
raise ArgumentError, "No routing rule specified." if rule.nil?
|
8
|
-
c = Class.new(self) {}
|
9
|
-
ServerSide::Router.route(rule) {c.new(self)}
|
10
|
-
c
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(request)
|
14
|
-
@request = request
|
15
|
-
@path = request.path
|
16
|
-
@parameters = request.parameters
|
17
|
-
process
|
18
|
-
render_default if not @rendered
|
19
|
-
end
|
20
|
-
|
21
|
-
def process
|
22
|
-
end
|
23
|
-
|
24
|
-
def render_default
|
25
|
-
@request.send_response(200, 'text/plain', 'no response.')
|
26
|
-
end
|
27
|
-
|
28
|
-
def render(body, content_type)
|
29
|
-
@request.send_response(200, content_type, body)
|
30
|
-
@rendered = true
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
__END__
|
36
|
-
|
37
|
-
require 'rubygems'
|
38
|
-
require 'active_support/inflector'
|
39
|
-
require 'metaid'
|
40
|
-
|
41
|
-
class ActionController
|
42
|
-
def self.default_routing_rule
|
43
|
-
if name.split('::').last =~ /(.+)Controller$/
|
44
|
-
controller = Inflector.underscore($1)
|
45
|
-
{:path => ["/#{controller}/:action", "/#{controller}/:action/:id"]}
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.inherited(c)
|
50
|
-
routing_rule = c.respond_to?(:routing_rule) ?
|
51
|
-
c.routing_rule : c.default_routing_rule
|
52
|
-
if routing_rule
|
53
|
-
ServerSide::Router.route(routing_rule) {c.new(self)}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.route(arg = nil, &block)
|
58
|
-
rule = arg || block
|
59
|
-
meta_def(:get_route) {rule}
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class MyController < ActionController
|
64
|
-
route "hello"
|
65
|
-
end
|
66
|
-
|
67
|
-
p MyController.get_route
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
class StaticServerTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
class StaticRequest < ServerSide::HTTP::Request
|
7
|
-
def respond
|
8
|
-
begin
|
9
|
-
# set_cookie(:hello, 'world', Time.now + 30) if @cookies[:test] == 'hello'
|
10
|
-
rescue => e
|
11
|
-
puts e.message
|
12
|
-
end
|
13
|
-
status = 200
|
14
|
-
body = IO.read('.'/@path)
|
15
|
-
rescue => e
|
16
|
-
status = 404
|
17
|
-
body = "Couldn't open file #{@path}."
|
18
|
-
ensure
|
19
|
-
send_response(status, 'text', body)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_basic
|
24
|
-
@t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17654, StaticRequest)}
|
25
|
-
sleep 0.1
|
26
|
-
|
27
|
-
h = Net::HTTP.new('localhost', 17654)
|
28
|
-
resp, data = h.get('/qqq', nil)
|
29
|
-
assert_equal 404, resp.code.to_i
|
30
|
-
assert_equal "Couldn't open file /qqq.", data
|
31
|
-
|
32
|
-
h = Net::HTTP.new('localhost', 17654)
|
33
|
-
resp, data = h.get("/#{__FILE__}", nil)
|
34
|
-
assert_equal 200, resp.code.to_i
|
35
|
-
assert_equal IO.read(__FILE__), data
|
36
|
-
assert_equal 'text', resp['Content-Type']
|
37
|
-
# Net::HTTP includes this header in the request, so our server returns
|
38
|
-
# likewise.
|
39
|
-
assert_equal 'close', resp['Connection']
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_cookies
|
43
|
-
@t = Thread.new {ServerSide::HTTP::Server.new('0.0.0.0', 17655, StaticRequest)}
|
44
|
-
sleep 0.1
|
45
|
-
|
46
|
-
h = Net::HTTP.new('localhost', 17655)
|
47
|
-
resp, data = h.get('/qqq', nil)
|
48
|
-
assert_equal 404, resp.code.to_i
|
49
|
-
assert_nil resp['Set-Cookie']
|
50
|
-
|
51
|
-
h = Net::HTTP.new('localhost', 17655)
|
52
|
-
resp, data = h.get('/qqq', {'Cookie' => 'test=hello'})
|
53
|
-
assert_equal 404, resp.code.to_i
|
54
|
-
assert_not_nil resp['Set-Cookie'] =~ /hello=world/
|
55
|
-
|
56
|
-
h = Net::HTTP.new('localhost', 17655)
|
57
|
-
resp, data = h.get('/qqq', nil)
|
58
|
-
assert_equal 404, resp.code.to_i
|
59
|
-
assert_nil resp['Set-Cookie']
|
60
|
-
end
|
61
|
-
end
|