serverside 0.4.1 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/spec/http_spec.rb CHANGED
@@ -1,41 +1,7 @@
1
- require File.join(File.dirname(__FILE__), '../lib/serverside')
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
3
  include ServerSide::HTTP
4
4
 
5
- class SpecHTTPServer
6
- include Server
7
-
8
- attr_accessor :in, :state, :request_headers, :request_header_count,
9
- :request_cookies, :response_headers
10
-
11
- attr_accessor :method, :uri, :query, :http_version
12
- attr_accessor :params, :persistent, :content_length
13
-
14
- def initialize
15
- reset
16
- end
17
-
18
- def reset
19
- post_init
20
- @response = ''
21
- @closed = false
22
- end
23
-
24
- attr_reader :response, :closed
25
-
26
- def send_data(data)
27
- @response << data
28
- end
29
-
30
- def close_connection_after_writing
31
- @closed = true
32
- end
33
-
34
- def send_error_response(e)
35
- raise e
36
- end
37
- end
38
-
39
5
  context "An HTTP Request should be considered malformed" do
40
6
  setup do
41
7
  @server = SpecHTTPServer.new
@@ -43,56 +9,74 @@ context "An HTTP Request should be considered malformed" do
43
9
 
44
10
  specify "if the request-line size is too big" do
45
11
  l = "GET /#{'x' * (MAX_REQUEST_LINE_SIZE - 12)}"
46
- proc {@server.receive_data(l)}.should_not raise_error(MalformedRequestError)
12
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
47
13
 
48
14
  @server.reset
49
- l = "GET /#{'x' * MAX_REQUEST_LINE_SIZE}"
50
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
15
+ l = "GET /#{'x' * MAX_REQUEST_LINE_SIZE} HTTP/1.1\r\n"
16
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
51
17
  end
52
18
 
53
19
  specify "if the request-line is malformed" do
54
20
  l = "abcde\r\n"
55
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
21
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
56
22
 
57
23
  @server.reset
58
24
  l = "GET /\r\n"
59
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
25
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
60
26
 
61
27
  @server.reset
62
28
  l = "GET HTTP/\r\n"
63
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
29
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
30
+ end
31
+
32
+ specify "if a query parameter name is too big" do
33
+ l = "GET /?#{'x' * MAX_PARAMETER_NAME_SIZE}=2 HTTP/1.1\r\n"
34
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
35
+
36
+ @server.reset
37
+ l = "GET /?#{'x' * MAX_PARAMETER_NAME_SIZE + 'y'}=2 HTTP/1.1\r\n"
38
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
64
39
  end
65
40
 
66
41
  specify "if the header count is too big" do
67
- l = "GET / HTTP/1.1\r\n" + "Accept: *\r\n" * (MAX_HEADER_COUNT - 1)
68
- proc {@server.receive_data(l)}.should_not raise_error(MalformedRequestError)
42
+ l = "GET / HTTP/1.1\r\n" + ("Accept: *\r\n" * (MAX_HEADER_COUNT - 1))
43
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
69
44
 
70
45
  @server.reset
71
46
  l = "GET / HTTP/1.1\r\n" + "Accept: *\r\n" * (MAX_HEADER_COUNT + 10)
72
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
47
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
73
48
  end
74
49
 
75
50
  specify "if a header is too big" do
76
- l = "GET / HTTP/1.1\r\n#{'x' * MAX_HEADER_SIZE}: 1\r\n"
77
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
51
+ l = "GET / HTTP/1.1\r\nAccept: #{'x' * MAX_HEADER_SIZE}\r\n"
52
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
53
+ end
54
+
55
+ specify "if a header name is too big" do
56
+ l = "GET / HTTP/1.1\r\n#{'x' * MAX_HEADER_NAME_SIZE}: 1\r\n"
57
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
58
+
59
+ @server.reset
60
+ l = "GET / HTTP/1.1\r\n#{'x' * MAX_HEADER_NAME_SIZE + 'y'}: 1\r\n"
61
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
78
62
  end
79
63
 
80
64
  specify "if a header is malformed" do
81
65
  l = %[GET / HTTP/1.1\r\ntest: 231\r\n]
82
- proc {@server.receive_data(l)}.should_not raise_error(MalformedRequestError)
66
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
83
67
 
84
68
  @server.reset
85
69
  l = %[GET / HTTP/1.1\r\nmitchesunu no haha\r\n]
86
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
70
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
87
71
  end
88
72
 
89
73
  specify "if it contains a malformed cookie header" do
90
74
  l = %[GET / HTTP/1.1\r\nCookie: a=b\r\n]
91
- proc {@server.receive_data(l)}.should_not raise_error(MalformedRequestError)
75
+ proc {@server.receive_data(l)}.should_not raise_error(BadRequestError)
92
76
 
93
77
  @server.reset
94
78
  l = %[GET / HTTP/1.1\r\nCookie: zzxxcc\r\n]
95
- proc {@server.receive_data(l)}.should raise_error(MalformedRequestError)
79
+ proc {@server.receive_data(l)}.should raise_error(BadRequestError)
96
80
  end
97
81
  end
98
82
 
@@ -101,17 +85,12 @@ context "A server in the initial state" do
101
85
  @server = SpecHTTPServer.new
102
86
  end
103
87
 
104
- specify "should initialize all header-related variables" do
105
- @server.request_headers = {1 => 2}
106
- @server.request_header_count = 20
107
- @server.request_cookies = {:z => :y}
108
- @server.response_headers = [1, 2, 3]
109
-
88
+ specify "should create a new request instance" do
89
+ req1 = @server.request
110
90
  @server.set_state(:state_initial)
111
- @server.request_headers.should == {}
112
- @server.request_header_count.should == 0
113
- @server.request_cookies.should == {}
114
- @server.response_headers.should == []
91
+ req2 = @server.request
92
+
93
+ req1.should_not == req2
115
94
  end
116
95
 
117
96
  specify "should transition to state_request_line" do
@@ -137,26 +116,26 @@ context "A server in the request_line state" do
137
116
  @server.state.should_not == :state_request_line
138
117
  end
139
118
 
140
- specify "should extract method, uri, query and http version from the request line" do
119
+ specify "should extract method, path, query and http version from the request line" do
141
120
  @server.receive_data("GET /abc?q=z HTTP/1.1\r\n")
142
- @server.method.should == :get
143
- @server.uri.should == '/abc'
144
- @server.query.should == 'q=z'
145
- @server.http_version.should == '1.1'
121
+ @server.request.method.should == :get
122
+ @server.request.path.should == '/abc'
123
+ @server.request.query.should == 'q=z'
124
+ @server.request.http_version.should == '1.1'
146
125
  end
147
126
 
148
127
  specify "should set persistent to true if the http version is 1.1" do
149
128
  @server.receive_data("GET / HTTP/1.1\r\n")
150
- @server.persistent.should be_true
129
+ @server.request.persistent.should be_true
151
130
 
152
131
  @server.reset
153
132
  @server.receive_data("GET / HTTP/1.0\r\n")
154
- @server.persistent.should be_false
133
+ @server.request.persistent.should be_false
155
134
  end
156
135
 
157
136
  specify "should parse the query into params and unescape the values" do
158
137
  @server.receive_data("GET /?x=1&y=2%203 HTTP/1.1\r\n")
159
- @server.params.should == {:x => '1', :y => '2 3'}
138
+ @server.request.params.should == {:x => '1', :y => '2 3'}
160
139
  end
161
140
 
162
141
  specify "should transition to state_request_headers" do
@@ -165,25 +144,26 @@ context "A server in the request_line state" do
165
144
  @server.state.should == :state_request_headers
166
145
  end
167
146
 
168
- specify "should raise MalformedRequestError on invalid request line size" do
169
- proc {@server.receive_data("abc" * MAX_REQUEST_LINE_SIZE)}.should \
170
- raise_error(MalformedRequestError)
147
+ specify "should raise BadRequestError on invalid request line size" do
148
+ l = "GET /#{'x' * MAX_REQUEST_LINE_SIZE} HTTP/1.1\r\n"
149
+ proc {@server.receive_data(l)}.should \
150
+ raise_error(BadRequestError)
171
151
  end
172
152
 
173
- specify "should raise MalformedRequestError if the request line is invalid" do
174
- proc {@server.receive_data("GET\r\n")}.should raise_error(MalformedRequestError)
153
+ specify "should raise BadRequestError if the request line is invalid" do
154
+ proc {@server.receive_data("GET\r\n")}.should raise_error(BadRequestError)
175
155
 
176
156
  @server.reset
177
- proc {@server.receive_data("GET /\r\n")}.should raise_error(MalformedRequestError)
157
+ proc {@server.receive_data("GET /\r\n")}.should raise_error(BadRequestError)
178
158
 
179
159
  @server.reset
180
- proc {@server.receive_data("GET / 1.1\r\n")}.should raise_error(MalformedRequestError)
160
+ proc {@server.receive_data("GET / 1.1\r\n")}.should raise_error(BadRequestError)
181
161
 
182
162
  @server.reset
183
- proc {@server.receive_data("GET ?q=1 HTTP/1.1\r\n")}.should raise_error(MalformedRequestError)
163
+ proc {@server.receive_data("GET ?q=1 HTTP/1.1\r\n")}.should raise_error(BadRequestError)
184
164
 
185
165
  @server.reset
186
- proc {@server.receive_data("GET / HTTP 1.1\r\n")}.should raise_error(MalformedRequestError)
166
+ proc {@server.receive_data("GET / HTTP 1.1\r\n")}.should raise_error(BadRequestError)
187
167
  end
188
168
  end
189
169
 
@@ -200,39 +180,39 @@ context "A server in the request_headers state" do
200
180
 
201
181
  specify "should parse each header as it arrives" do
202
182
  @server.receive_data("Accept: text/xml\r\n")
203
- @server.request_headers.should == {'Accept' => 'text/xml'}
204
- @server.request_header_count.should == 1
183
+ @server.request.headers.should == {:accept => 'text/xml'}
184
+ @server.request.header_count.should == 1
205
185
 
206
186
  @server.receive_data("Cookie: x=1\r\n")
207
- @server.request_headers.should == {'Accept' => 'text/xml', 'Cookie' => 'x=1'}
208
- @server.request_header_count.should == 2
187
+ @server.request.headers.should == {:accept => 'text/xml', :cookie => 'x=1'}
188
+ @server.request.header_count.should == 2
209
189
  end
210
190
 
211
191
  specify "should parse the Content-Length header into content_length" do
212
192
  @server.receive_data("Content-Length: 1234\r\n")
213
- @server.content_length.should == 1234
193
+ @server.request.content_length.should == 1234
214
194
  end
215
195
 
216
196
  specify "should set persistent if a Connection header is received" do
217
197
  # HTTP 1.0 mode
218
- @server.persistent = false
198
+ @server.request.persistent = false
219
199
  @server.receive_data("Connection: keep-alive\r\n")
220
- @server.persistent.should be_true
200
+ @server.request.persistent.should be_true
221
201
 
222
202
  @server.reset
223
203
  @server.receive_data("GET / HTTP/1.1\r\n")
224
204
  @server.receive_data("Connection: close\r\n")
225
- @server.persistent.should be_false
205
+ @server.request.persistent.should be_false
226
206
 
227
207
  @server.reset
228
208
  @server.receive_data("GET / HTTP/1.1\r\n")
229
209
  @server.receive_data("Connection: xxxxzzzz\r\n")
230
- @server.persistent.should be_false
210
+ @server.request.persistent.should be_false
231
211
  end
232
212
 
233
213
  specify "Should parse the Cookie header" do
234
214
  @server.receive_data("Cookie: x=1; y=2%203\r\n")
235
- @server.request_cookies.should == {:x => '1', :y => '2 3'}
215
+ @server.request.cookies.should == {:x => '1', :y => '2 3'}
236
216
  end
237
217
 
238
218
  specify "should transition to stat_response once an empty line is received" do
@@ -248,12 +228,34 @@ context "A server in the request_headers state" do
248
228
  @server.state.should == :state_request_body
249
229
  end
250
230
 
251
- specify "should raise MalformedRequestError on invalid header size" do
231
+ specify "should raise BadRequestError on invalid header size" do
252
232
  proc {@server.receive_data("#{'x' * MAX_HEADER_SIZE}: 13\r\n\r\n")}.should \
253
- raise_error(MalformedRequestError)
233
+ raise_error(BadRequestError)
254
234
  end
255
235
 
256
- specify "should raise MalformedRequestError on malformed header" do
257
- proc {@server.receive_data("abc\r\n")}.should raise_error(MalformedRequestError)
236
+ specify "should raise BadRequestError on malformed header" do
237
+ proc {@server.receive_data("abc\r\n")}.should raise_error(BadRequestError)
258
238
  end
259
- end
239
+ end
240
+
241
+ context "A persistent connection" do
242
+ setup do
243
+ @server = SpecHTTPServer.new
244
+
245
+ m = Module.new do
246
+ define_method(:handle) {|req| raise "hi there"}
247
+ define_method(:handle_error) {|e| send_response(Response.error(e))}
248
+ end
249
+ @server.extend(m)
250
+ end
251
+
252
+ specify "should correctly handle errors" do
253
+ @server.receive_data("GET / HTTP/1.1\r\n\r\n")
254
+ @server.response.scan('500').should == ['500']
255
+ end
256
+
257
+ specify "should correctly handle parsing errors" do
258
+ @server.receive_data("GET abb\r\n\r\n")
259
+ @server.response.scan('400').should == ['400']
260
+ end
261
+ end
data/spec/js_spec.rb ADDED
@@ -0,0 +1,86 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ context "A new JS representation" do
4
+ specify "should work with no arguments" do
5
+ js = ServerSide::JS.new
6
+ js.to_s.should == 'null'
7
+
8
+ js.abc 123
9
+ js.to_s.should == '{"abc":123}'
10
+ end
11
+
12
+ specify "should accept a callback" do
13
+ js = ServerSide::JS.new('fn')
14
+ js.to_s.should == 'fn(null);'
15
+ end
16
+
17
+ specify "should accept a block and run it" do
18
+ js = ServerSide::JS.new do |j|
19
+ j.abc 123
20
+ end
21
+ js.to_s.should == '{"abc":123}'
22
+ end
23
+ end
24
+
25
+ context "JS instance methods" do
26
+ setup do
27
+ @js = ServerSide::JS.new
28
+ end
29
+
30
+ specify "should accept strings" do
31
+ @js.x 'zzz'
32
+ @js.to_s.should == '{"x":"zzz"}'
33
+ end
34
+
35
+ specify "should accept numbers" do
36
+ @js.x 3
37
+ @js.to_s.should == '{"x":3}'
38
+
39
+ @js = ServerSide::JS.new
40
+ @js.x 4.5
41
+ @js.to_s.should == '{"x":4.5}'
42
+ end
43
+
44
+ specify "should accept true, false, nil" do
45
+ @js.x true
46
+ @js.to_s.should == '{"x":true}'
47
+
48
+ @js = ServerSide::JS.new
49
+ @js.x false
50
+ @js.to_s.should == '{"x":false}'
51
+
52
+ @js = ServerSide::JS.new
53
+ @js.x nil
54
+ @js.to_s.should == '{"x":null}'
55
+ end
56
+
57
+ specify "should accept arrays" do
58
+ @js.x [1, 2, 3]
59
+ @js.to_s.should == '{"x":[1,2,3]}'
60
+ end
61
+
62
+ specify "should accept blocks" do
63
+ @js.x {|j| j.y 'hello'}
64
+ @js.to_s.should == '{"x":{"y":"hello"}}'
65
+ end
66
+
67
+ specify "should support << operator" do
68
+ @js << 1
69
+ @js << 2
70
+ @js.to_s.should == '[1,2]'
71
+
72
+ @js = ServerSide::JS.new
73
+ @js.xxx {|j| j << 1 << 2}
74
+ @js.to_s.should == '{"xxx":[1,2]}'
75
+ end
76
+
77
+ specify "should accept JS objects" do
78
+ @js.y [1, 2, 3, 4]
79
+
80
+ @outer = ServerSide::JS.new do |j|
81
+ j.x @js
82
+ end
83
+
84
+ @outer.to_s.should == '{"x":{"y":[1,2,3,4]}}'
85
+ end
86
+ end
@@ -0,0 +1,133 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ include ServerSide::HTTP
4
+
5
+ context "A Request" do
6
+ setup do
7
+ @server = SpecHTTPServer.new
8
+
9
+ m = Module.new do
10
+ attr_reader :req, :error
11
+ define_method(:handle) {|req| @req = req}
12
+ define_method(:handle_error) {|e| @error = e}
13
+ end
14
+ @server.extend(m)
15
+ end
16
+
17
+ MOCK_GET1 = "GET / HTTP/1.1\r\n\r\n"
18
+
19
+ specify "should be passed to the handle request" do
20
+ @server.receive_data(MOCK_GET1)
21
+
22
+ @server.req.should be_a_kind_of(ServerSide::HTTP::Request)
23
+ end
24
+
25
+ MOCK_POST1 = "POST /abcd HTTP/1.1\r\n\r\n"
26
+
27
+ specify "should provide the HTTP method" do
28
+ @server.receive_data(MOCK_GET1)
29
+ @server.request.method.should == :get
30
+
31
+ @server.set_state(:state_initial)
32
+ @server.receive_data(MOCK_POST1)
33
+ @server.request.method.should == :post
34
+ end
35
+
36
+ MOCK_HTTP_1_0 = "GET /xxx HTTP/1.0\r\n\r\n"
37
+
38
+ specify "should provide the HTTP version" do
39
+ @server.receive_data(MOCK_GET1)
40
+ @server.request.http_version.should == '1.1'
41
+ @server.request.persistent.should == true
42
+
43
+ @server.set_state(:state_initial)
44
+ @server.receive_data(MOCK_HTTP_1_0)
45
+ @server.request.http_version.should == '1.0'
46
+ @server.request.persistent.should == false
47
+ end
48
+
49
+ specify "should provide the request line" do
50
+ @server.receive_data(MOCK_GET1)
51
+ @server.request.request_line.should == 'GET / HTTP/1.1'
52
+ end
53
+
54
+ MOCK_GET_PARAMS = "GET /?q=state&f=xml HTTP/1.1\r\n\r\n"
55
+ MOCK_POST_PARAMS = "POST / HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 12\r\n\r\nq=state&f=js"
56
+
57
+ specify "should provide the parameters" do
58
+ @server.receive_data(MOCK_GET1)
59
+ @server.request.params.should == {}
60
+
61
+ @server.set_state(:state_initial)
62
+ @server.receive_data(MOCK_GET_PARAMS)
63
+ @server.request.params.should == {:q => 'state', :f => 'xml'}
64
+
65
+ @server.set_state(:state_initial)
66
+ @server.receive_data(MOCK_POST_PARAMS)
67
+ @server.request.params.should == {:q => 'state', :f => 'js'}
68
+ end
69
+
70
+ MOCK_GET2 = "GET /xxx HTTP/1.0\r\nHost: reality-scada.net\r\n\r\n"
71
+ MOCK_GET3 = "GET /xxx HTTP/1.0\r\nHost: abc.net:443\r\n\r\n"
72
+ MOCK_GET4 = "GET /xxx HTTP/1.0\r\nHost: xyz.net:3321\r\n\r\n"
73
+
74
+ specify "should provide the host and port" do
75
+ @server.receive_data(MOCK_GET2)
76
+ @server.request.host.should == 'reality-scada.net'
77
+ @server.request.port.should be_nil
78
+ @server.request.should_not be_encrypted
79
+
80
+ @server.set_state(:state_initial)
81
+ @server.receive_data(MOCK_GET3)
82
+ @server.request.host.should == 'abc.net'
83
+ @server.request.port.should == 443
84
+ @server.request.should be_encrypted
85
+
86
+ @server.set_state(:state_initial)
87
+ @server.receive_data(MOCK_GET4)
88
+ @server.request.host.should == 'xyz.net'
89
+ @server.request.port.should == 3321
90
+ @server.request.should_not be_encrypted
91
+ end
92
+
93
+ MOCK_GET5 = "GET / HTTP/1.1\r\nX-Forwarded-For: 12.33.44.55\r\n\r\n"
94
+
95
+ specify "should provide the client name" do
96
+ m = Module.new do
97
+ def get_peername; end
98
+ end
99
+ @server.extend(m)
100
+
101
+ @server.receive_data(MOCK_GET1)
102
+ @server.request.client_name.should be_nil
103
+
104
+ @server.set_state(:state_initial)
105
+ @server.receive_data(MOCK_GET5)
106
+ @server.request.client_name.should == '12.33.44.55'
107
+ end
108
+ end
109
+
110
+ context "Request.accept?" do
111
+ setup do
112
+ @req = Request.new(nil)
113
+ end
114
+
115
+ specify "should return nil if the Accept header is not there" do
116
+ @req.accept?(//).should be_nil
117
+ end
118
+
119
+ specify "should match the Accept header to the supplied pattern" do
120
+ @req.headers[:accept] = 'text/html; text/plain'
121
+ @req.accept?(/html/).should be_true
122
+ @req.accept?(/plain/).should be_true
123
+ @req.accept?(/^text/).should be_true
124
+ @req.accept?(/text$/).should_not be_true
125
+ end
126
+
127
+ specify "should support String patterns as well" do
128
+ @req.headers[:accept] = 'text/html; text/plain'
129
+ @req.accept?('html').should be_true
130
+ @req.accept?('plain').should be_true
131
+ @req.accept?('xml').should_not be_true
132
+ end
133
+ end