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/request_spec.rb DELETED
@@ -1,288 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
- require 'stringio'
3
-
4
- class DummyRequest2 < ServerSide::HTTP::Request
5
- attr_accessor :calls, :parse_result, :persistent
6
-
7
- def parse
8
- @calls ||= []
9
- @calls << :parse
10
- @parse_result
11
- end
12
-
13
- def respond
14
- @calls ||= []
15
- @calls << :respond
16
- end
17
- end
18
-
19
- context "HTTP::Request.process" do
20
- specify "should call parse and and short-circuit if the result is nil" do
21
- r = DummyRequest2.new(nil)
22
- r.process.should_be_nil
23
- r.calls.should == [:parse]
24
-
25
- r.calls = []
26
- r.parse_result = false
27
- r.process.should_be false
28
- r.calls.should == [:parse]
29
- end
30
-
31
- specify "should follow parse with respond and return @persistent" do
32
- r = DummyRequest2.new(nil)
33
- r.parse_result = true
34
- r.process.should_be_nil
35
- r.calls.should == [:parse, :respond]
36
-
37
- r.calls = []
38
- r.persistent = 'mau'
39
- r.process.should == 'mau'
40
- r.calls.should == [:parse, :respond]
41
- end
42
- end
43
-
44
- class ServerSide::HTTP::Request
45
- attr_writer :socket, :persistent, :response_headers
46
- end
47
-
48
- context "HTTP::Request.parse" do
49
- specify "should return nil for invalid requests" do
50
- r = ServerSide::HTTP::Request.new(nil)
51
- r.socket = StringIO.new('POST /test')
52
- r.parse.should_be_nil
53
- r.socket = StringIO.new('invalid string')
54
- r.parse.should_be_nil
55
- r.socket = StringIO.new('GET HTTP/1.1')
56
- r.parse.should_be_nil
57
- r.socket = StringIO.new('GET /test http')
58
- r.parse.should_be_nil
59
- r.socket = StringIO.new('GET /test HTTP')
60
- r.parse.should_be_nil
61
- r.socket = StringIO.new('GET /test HTTP/')
62
- r.parse.should_be_nil
63
- r.socket = StringIO.new('POST /test HTTP/1.1')
64
- r.parse.should_be_nil
65
- end
66
-
67
- specify "should parse valid requests and return request headers" do
68
- r = ServerSide::HTTP::Request.new(nil)
69
- r.socket = StringIO.new(
70
- "POST /test HTTP/1.1\r\nContent-Type: text/html\r\n\r\n")
71
- r.parse.should_be r.headers
72
- r.method.should == :post
73
- r.path.should == '/test'
74
- r.query.should_be_nil
75
- r.version.should == '1.1'
76
- r.parameters.should == {}
77
- r.headers.should == {'Content-Type' => 'text/html'}
78
- r.cookies.should == {}
79
- r.response_cookies.should_be_nil
80
- r.persistent.should == true
81
- end
82
-
83
- specify "should correctly handle trailing slash in path" do
84
- r = ServerSide::HTTP::Request.new(nil)
85
- r.socket = StringIO.new("POST /test/asdf/qw/ HTTP/1.1\r\n\r\n")
86
- r.parse.should_not_be_nil
87
- r.path.should == '/test/asdf/qw'
88
-
89
- r.socket = StringIO.new(
90
- "POST /test/asdf/qw/?time=24%20hours HTTP/1.1\r\n\r\n")
91
- r.parse.should_not_be_nil
92
- r.path.should == '/test/asdf/qw'
93
- end
94
-
95
- specify "should parse URL-encoded parameters" do
96
- r = ServerSide::HTTP::Request.new(nil)
97
- r.socket = StringIO.new(
98
- "POST /test?q=node_history&time=24%20hours HTTP/1.1\r\n\r\n")
99
- r.parse.should_not_be_nil
100
- r.parameters.size.should == 2
101
- r.parameters[:time].should == '24 hours'
102
- r.parameters[:q].should == 'node_history'
103
- end
104
-
105
- specify "should correctly parse the HTTP version" do
106
- r = ServerSide::HTTP::Request.new(nil)
107
- r.socket = StringIO.new(
108
- "POST / HTTP/1.0\r\n\r\n")
109
- r.parse.should_not_be_nil
110
- r.version.should == '1.0'
111
- r.socket = StringIO.new(
112
- "POST / HTTP/3.2\r\n\r\n")
113
- r.parse.should_not_be_nil
114
- r.version.should == '3.2'
115
- end
116
-
117
- specify "should set @persistent correctly" do
118
- r = ServerSide::HTTP::Request.new(nil)
119
- r.socket = StringIO.new("POST / HTTP/1.0\r\n\r\n")
120
- r.parse.should_not_be_nil
121
- r.persistent.should == false
122
- r.socket = StringIO.new("POST / HTTP/1.1\r\n\r\n")
123
- r.parse.should_not_be_nil
124
- r.persistent.should == true
125
- r.socket = StringIO.new("POST / HTTP/0.6\r\n\r\n")
126
- r.parse.should_not_be_nil
127
- r.persistent.should == false
128
- r.socket = StringIO.new("POST / HTTP/1.1\r\nConnection: close\r\n\r\n")
129
- r.parse.should_not_be_nil
130
- r.persistent.should == false
131
- r.socket = StringIO.new("POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n")
132
- r.parse.should_not_be_nil
133
- r.persistent.should == true
134
- end
135
-
136
- specify "should parse cookies" do
137
- r = ServerSide::HTTP::Request.new(nil)
138
- r.socket = StringIO.new(
139
- "POST / HTTP/1.0\r\nCookie: abc=1342; def=7%2f4\r\n\r\n")
140
- r.parse.should_not_be_nil
141
- r.headers['Cookie'].should == 'abc=1342; def=7%2f4'
142
- r.cookies.size.should == 2
143
- r.cookies[:abc].should == '1342'
144
- r.cookies[:def].should == '7/4'
145
- end
146
-
147
- specify "should parse the post body" do
148
- r = ServerSide::HTTP::Request.new(nil)
149
- r.socket = StringIO.new(
150
- "POST /?q=node_history HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 15\r\n\r\ntime=24%20hours")
151
- r.parse.should_not_be_nil
152
- r.parameters.size.should == 2
153
- r.parameters[:q].should == 'node_history'
154
- r.parameters[:time].should == '24 hours'
155
- end
156
- end
157
-
158
- context "HTTP::Request.send_response" do
159
- specify "should format a response with status and body" do
160
- r = ServerSide::HTTP::Request.new(nil)
161
- r.socket = StringIO.new
162
- r.send_response(200, 'text', 'Hello there!')
163
- r.socket.rewind
164
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!"
165
- end
166
-
167
- specify "should format a response without connect-close when persistent" do
168
- r = ServerSide::HTTP::Request.new(nil)
169
- r.socket = StringIO.new
170
- r.persistent = true
171
- r.send_response(200, 'text', 'Hello there!')
172
- r.socket.rewind
173
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nContent-Type: text\r\nContent-Length: 12\r\n\r\nHello there!"
174
- end
175
-
176
- specify "should format a response without content-length for streaming response" do
177
- r = ServerSide::HTTP::Request.new(nil)
178
- r.socket = StringIO.new
179
- r.persistent = true
180
- r.send_response(200, 'text')
181
- r.socket.rewind
182
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\n\r\n"
183
- r.stream('hey there')
184
- r.socket.rewind
185
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\n\r\nhey there"
186
- end
187
-
188
- specify "should include response_headers and headers in the response" do
189
- r = ServerSide::HTTP::Request.new(nil)
190
- r.socket = StringIO.new
191
- r.persistent = true
192
- r.response_headers['XXX'] = 'Test'
193
- r.send_response(200, 'text')
194
- r.socket.rewind
195
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nXXX: Test\r\n\r\n"
196
-
197
- r = ServerSide::HTTP::Request.new(nil)
198
- r.socket = StringIO.new
199
- r.persistent = true
200
- r.send_response(200, 'text', nil, nil, {'YYY' => 'TTT'})
201
- r.socket.rewind
202
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nYYY: TTT\r\n\r\n"
203
- end
204
-
205
- specify "should set persistent to false if exception is raised" do
206
- r = ServerSide::HTTP::Request.new(nil)
207
- r.persistent = true
208
- proc {r.send_response(200, 'text', 'Hello there!')}.should_not_raise
209
- r.persistent.should == false
210
- end
211
-
212
- specify "should include cookies in the response" do
213
- r = ServerSide::HTTP::Request.new(nil)
214
- r.socket = StringIO.new
215
- t = Time.now + 360
216
- r.set_cookie(:session, "ABCDEFG", t)
217
- r.response_cookies.should == "Set-Cookie: session=ABCDEFG; path=/; expires=#{t.rfc2822}\r\n"
218
- r.send_response(200, 'text', 'Hi!')
219
- r.socket.rewind
220
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nSet-Cookie: session=ABCDEFG; path=/; expires=#{t.rfc2822}\r\nContent-Length: 3\r\n\r\nHi!"
221
-
222
- r = ServerSide::HTTP::Request.new(nil)
223
- r.socket = StringIO.new
224
- r.delete_cookie(:session)
225
- r.response_cookies.should == "Set-Cookie: session=; path=/; expires=#{Time.at(0).rfc2822}\r\n"
226
- r.send_response(200, 'text', 'Hi!')
227
- r.socket.rewind
228
- r.socket.read.should == "HTTP/1.1 200\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nContent-Type: text\r\nSet-Cookie: session=; path=/; expires=#{Time.at(0).rfc2822}\r\nContent-Length: 3\r\n\r\nHi!"
229
- end
230
- end
231
-
232
- context "HTTP::Request.send_file" do
233
- specify "should include a disposition header in the response" do
234
- socket = StringIO.new
235
- r = ServerSide::HTTP::Request.new(socket)
236
- r.send_file('text', 'text/plain', :attachment)
237
- socket.rewind
238
- socket.read.should_match /Content-Disposition: attachment\r\n/
239
- end
240
-
241
- specify "should use :inline as default disposition" do
242
- socket = StringIO.new
243
- r = ServerSide::HTTP::Request.new(socket)
244
- r.send_file('text', 'text/plain')
245
- socket.rewind
246
- socket.read.should_match /Content-Disposition: inline\r\n/
247
- end
248
-
249
- specify "should include filename parameter if specified" do
250
- socket = StringIO.new
251
- r = ServerSide::HTTP::Request.new(socket)
252
- r.send_file('text', 'text/plain', :attachment, 'text.txt')
253
- socket.rewind
254
- socket.read.should_match /Content-Disposition: attachment; filename=text.txt\r\n/
255
- end
256
-
257
- specify "should include a description header if specified" do
258
- socket = StringIO.new
259
- r = ServerSide::HTTP::Request.new(socket)
260
- r.send_file('text', 'text/plain', :attachment, 'text.txt')
261
- socket.rewind
262
- socket.read.should_not_match /Content-Description:\s/
263
-
264
- socket = StringIO.new
265
- r = ServerSide::HTTP::Request.new(socket)
266
- r.send_file('text', 'text/plain', :attachment, 'text.txt', 'this is text.')
267
- socket.rewind
268
- socket.read.should_match /Content-Description:\sthis is text\./
269
- end
270
- end
271
-
272
- context "HTTP::Request.redirect" do
273
- specify "should send a 302 response for temporary redirect" do
274
- r = ServerSide::HTTP::Request.new(nil)
275
- r.socket = StringIO.new
276
- r.redirect('http://mau.com/132')
277
- r.socket.rewind
278
- r.socket.read.should == "HTTP/1.1 302\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n"
279
- end
280
-
281
- specify "should send a 301 response for permanent redirect" do
282
- r = ServerSide::HTTP::Request.new(nil)
283
- r.socket = StringIO.new
284
- r.redirect('http://mau.com/132', true)
285
- r.socket.rewind
286
- r.socket.read.should == "HTTP/1.1 301\r\nDate: #{Time.now.httpdate}\r\nConnection: close\r\nLocation: http://mau.com/132\r\n\r\n"
287
- end
288
- end
data/spec/routing_spec.rb DELETED
@@ -1,240 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
2
- require 'stringio'
3
-
4
- class ServerSide::Router
5
- attr_accessor :t, :parameters, :path
6
-
7
- def self.rules
8
- @@rules
9
- end
10
-
11
- def self.reset
12
- @@rules = []
13
- @@default_route = nil
14
- define_method(:respond) {nil}
15
- end
16
- end
17
-
18
- R = ServerSide::Router
19
-
20
- context "Router.routes_defined?" do
21
- specify "should return nil if no routes were defined" do
22
- R.reset
23
- R.routes_defined?.should_be_nil
24
- end
25
-
26
- specify "should return true if routes were defined" do
27
- R.reset
28
- R.route('/controller') {}
29
- R.routes_defined?.should_be true
30
- end
31
- end
32
-
33
- context "Router.route" do
34
- specify "should add the rule to @@rules" do
35
- l = proc {1 + 1}
36
- R.reset
37
- R.route(:path => '/t', &l)
38
- R.rules.size.should == 1
39
- R.rules[0][0].should == {:path => '/t'}
40
- R.rules[0][1].should == l
41
- end
42
-
43
- specify "should convert a string argument to a path rule" do
44
- R.reset
45
- R.route('/test') {}
46
- R.rules[0][0].should == {:path => '/test'}
47
- end
48
-
49
- specify "should convert a regexp argument to a path rule" do
50
- R.reset
51
- R.route(/abc/) {}
52
- R.rules[0][0].should == {:path => /abc/}
53
- end
54
-
55
- specify "should convert an array argument into a multiple path rule" do
56
- R.reset
57
- R.route(['/a', '/b', '/c']) {}
58
- R.rules[0][0].should == {:path => ['/a', '/b', '/c']}
59
- end
60
-
61
- specify "should store a hash argument as the rule" do
62
- R.reset
63
- R.route(:a => 'abc', :b => 'def') {}
64
- R.rules[0][0].should_be_a_kind_of Hash
65
- R.rules[0][0].size.should == 2
66
- R.rules[0][0][:a].should == 'abc'
67
- R.rules[0][0][:b].should == 'def'
68
- end
69
-
70
- specify "should unshift new rules into the rules array" do
71
- R.reset
72
- R.route('abc') {}
73
- R.route('def') {}
74
- R.route('ghi') {}
75
- R.rules.size.should == 3
76
- R.rules[0][0][:path].should == 'ghi'
77
- R.rules[1][0][:path].should == 'def'
78
- R.rules[2][0][:path].should == 'abc'
79
- end
80
-
81
- specify "should accept a proc as a rule" do
82
- R.reset
83
- l1 = proc {}
84
- l2 = proc {}
85
- R.route(l1, &l2)
86
- R.rules.size.should == 1
87
- R.rules[0][0].should_be l1
88
- R.rules[0][1].should_be l2
89
- end
90
- end
91
-
92
- context "Router.compile_rules" do
93
- specify "should compile a respond method for routing requests" do
94
- R.reset
95
- R.new(StringIO.new).respond.should_be_nil
96
- R.rules << [{:t => 'abc'}, proc{:abc}]
97
- R.rules << [{:t => 'def'}, proc{:def}]
98
- R.default_route {:default}
99
- # R.compile_rules - already called by default_route
100
- r = R.new(StringIO.new)
101
- r.t = 'abc'
102
- r.respond.should == :abc
103
- r.t = 'def'
104
- r.respond.should == :def
105
- r.t = ''
106
- r.respond.should == :default
107
- end
108
-
109
- specify "should allow handlers to give up on a request, and then pass it on." do
110
- R.reset
111
- R.default_route {:default}
112
- R.new(StringIO.new).respond.should == :default
113
- R.route('.*') {@path == '/first' ? :first : nil}
114
- R.route('.*') {@path == '/second' ? :second : nil}
115
- r = R.new(StringIO.new)
116
- r.path = '/second'
117
- r.respond.should == :second
118
- r.path = '/first'
119
- r.respond.should == :first
120
- r.path = '/other'
121
- r.respond.should == :default
122
- end
123
- end
124
-
125
- context "Router.rule_to_statement" do
126
- specify "should define procs as methods and construct a test expression" do
127
- l1 = proc {}
128
- l2 = proc {}
129
- R.rule_to_statement(l1, l2).should == "if #{l1.proc_tag} && (r = #{l2.proc_tag}); return r; end\n"
130
- r = R.new(StringIO.new)
131
- r.should_respond_to l1.proc_tag
132
- r.should_respond_to l2.proc_tag
133
- end
134
-
135
- specify "should convert hash rule with single key-value to a test expression" do
136
- l3 = proc {}
137
- s = R.rule_to_statement({:path => '/.*'}, l3)
138
- s =~ /^if \(@path =~ ([^\(]*)\)/
139
- eval("R::#{$1}").should == /\/.*/
140
- r = R.new(StringIO.new)
141
- r.should_respond_to l3.proc_tag
142
- end
143
-
144
- specify "should convert hash with multiple key-values to an OR test expression" do
145
- l4 = proc {}
146
-
147
- s = R.rule_to_statement({:path => '/controller', :host => 'static'}, l4)
148
- s.should_match /\(@path\s=~\s([^\)]+)\)/
149
- s =~ /\(@path\s=~\s([^\)]+)\)/
150
- eval("R::#{$1}").should == /\/controller/
151
- s.should_match /\(@host\s=~\s([^\)]+)\)/
152
- s =~ /\(@host\s=~\s([^\)]+)\)/
153
- eval("R::#{$1}").should == /static/
154
- r = R.new(StringIO.new)
155
- r.should_respond_to l4.proc_tag
156
- end
157
-
158
- specify "should convert hash with Array value to a test expression" do
159
- l5 = proc {}
160
- s = R.rule_to_statement({:path => ['/x', '/y']}, l5)
161
- s =~ /^if\s\(\(@path\s=~\s([^\)]*)\)\|\|\(@path\s=~\s([^\)]*)\)\)/
162
- eval("R::#{$1}").should == /\/x/
163
- eval("R::#{$2}").should == /\/y/
164
- r = R.new(StringIO.new)
165
- r.should_respond_to l5.proc_tag
166
- end
167
- end
168
-
169
- context "Router.condition part" do
170
- specify "should compile a condition expression with key and value" do
171
- s = R.condition_part(:path, 'abc')
172
- s.should_match /\(@path\s=~\s(.*)\)$/
173
- s =~ /\(@path\s=~\s(.*)\)$/
174
- eval("R::#{$1}").should == /abc/
175
- end
176
-
177
- specify "should parse parametrized value and compile it into a lambda" do
178
- s = R.condition_part(:t, ':action/:id')
179
- (s =~ /^\((.*)\)$/).should_not_be_nil
180
- tag = $1
181
- r = R.new(StringIO.new)
182
- r.should_respond_to tag
183
- r.parameters = {}
184
- r.t = 'abc'
185
- r.send(tag).should_be false
186
- r.t = 'show/16'
187
- r.send(tag).should_be true
188
- r.parameters[:action].should == 'show'
189
- r.parameters[:id].should == '16'
190
- end
191
- end
192
-
193
- context "Router.define_proc" do
194
- specify "should convert a lambda into an instance method" do
195
- l1 = proc {1 + 1}
196
- tag = R.define_proc(&l1)
197
- tag.should_be_a_kind_of Symbol
198
- tag.should == l1.proc_tag.to_sym
199
- r = R.new(StringIO.new)
200
- r.should_respond_to(tag)
201
- r.send(tag).should == 2
202
- end
203
- end
204
-
205
- context "Router.cache_constant" do
206
- specify "should cache a value as a constant inside the Router namespace" do
207
- c = rand(100000)
208
- tag = R.cache_constant(c)
209
- tag.should_be_a_kind_of String
210
- tag.should == c.const_tag
211
- eval("R::#{tag}").should == c
212
- end
213
- end
214
-
215
- context "Router.default_route" do
216
- specify "should set the default route" do
217
- R.default_route {'mau m'}
218
- R.new(StringIO.new).default_handler.should == 'mau m'
219
-
220
- R.default_route {654321}
221
- R.new(StringIO.new).default_handler.should == 654321
222
- end
223
-
224
- specify "should affect the result of routes_defined?" do
225
- R.reset
226
- R.routes_defined?.should_be_nil
227
- R.default_route {654321}
228
- R.routes_defined?.should_not_be_nil
229
- end
230
- end
231
-
232
- context "Router.unhandled" do
233
- specify "should send a 403 response" do
234
- r = R.new(StringIO.new)
235
- r.unhandled
236
- r.socket.rewind
237
- resp = r.socket.read
238
- resp.should_match /HTTP\/1.1\s403(.*)\r\n/
239
- end
240
- end
data/spec/server_spec.rb DELETED
@@ -1,40 +0,0 @@
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