serverside 0.4.1 → 0.4.3

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/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