strelka 0.0.1pre4 → 0.0.1.pre129
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +1 -1
- data/IDEAS.rdoc +62 -0
- data/Manifest.txt +38 -7
- data/README.rdoc +124 -5
- data/Rakefile +22 -6
- data/bin/leash +102 -157
- data/contrib/hoetemplate/.autotest.erb +23 -0
- data/contrib/hoetemplate/History.rdoc.erb +4 -0
- data/contrib/hoetemplate/Manifest.txt.erb +8 -0
- data/contrib/hoetemplate/README.rdoc.erb +17 -0
- data/contrib/hoetemplate/Rakefile.erb +24 -0
- data/contrib/hoetemplate/data/file_name/apps/file_name_app +36 -0
- data/contrib/hoetemplate/data/file_name/templates/layout.tmpl.erb +13 -0
- data/contrib/hoetemplate/data/file_name/templates/top.tmpl.erb +8 -0
- data/contrib/hoetemplate/lib/file_name.rb.erb +18 -0
- data/contrib/hoetemplate/spec/file_name_spec.rb.erb +21 -0
- data/data/strelka/apps/hello-world +30 -0
- data/lib/strelka/app/defaultrouter.rb +49 -30
- data/lib/strelka/app/errors.rb +121 -0
- data/lib/strelka/app/exclusiverouter.rb +40 -0
- data/lib/strelka/app/filters.rb +18 -7
- data/lib/strelka/app/negotiation.rb +122 -0
- data/lib/strelka/app/parameters.rb +171 -14
- data/lib/strelka/app/paramvalidator.rb +751 -0
- data/lib/strelka/app/plugins.rb +66 -46
- data/lib/strelka/app/restresources.rb +499 -0
- data/lib/strelka/app/router.rb +73 -0
- data/lib/strelka/app/routing.rb +140 -18
- data/lib/strelka/app/templating.rb +12 -3
- data/lib/strelka/app.rb +174 -24
- data/lib/strelka/constants.rb +0 -20
- data/lib/strelka/exceptions.rb +29 -0
- data/lib/strelka/httprequest/acceptparams.rb +377 -0
- data/lib/strelka/httprequest/negotiation.rb +257 -0
- data/lib/strelka/httprequest.rb +155 -7
- data/lib/strelka/httpresponse/negotiation.rb +579 -0
- data/lib/strelka/httpresponse.rb +140 -0
- data/lib/strelka/logging.rb +4 -1
- data/lib/strelka/mixins.rb +53 -0
- data/lib/strelka.rb +22 -1
- data/spec/data/error.tmpl +1 -0
- data/spec/lib/constants.rb +0 -1
- data/spec/lib/helpers.rb +21 -0
- data/spec/strelka/app/defaultrouter_spec.rb +41 -35
- data/spec/strelka/app/errors_spec.rb +212 -0
- data/spec/strelka/app/exclusiverouter_spec.rb +220 -0
- data/spec/strelka/app/filters_spec.rb +196 -0
- data/spec/strelka/app/negotiation_spec.rb +73 -0
- data/spec/strelka/app/parameters_spec.rb +149 -0
- data/spec/strelka/app/paramvalidator_spec.rb +1059 -0
- data/spec/strelka/app/plugins_spec.rb +26 -19
- data/spec/strelka/app/restresources_spec.rb +393 -0
- data/spec/strelka/app/router_spec.rb +63 -0
- data/spec/strelka/app/routing_spec.rb +183 -9
- data/spec/strelka/app/templating_spec.rb +1 -2
- data/spec/strelka/app_spec.rb +265 -32
- data/spec/strelka/exceptions_spec.rb +53 -0
- data/spec/strelka/httprequest/acceptparams_spec.rb +282 -0
- data/spec/strelka/httprequest/negotiation_spec.rb +246 -0
- data/spec/strelka/httprequest_spec.rb +204 -14
- data/spec/strelka/httpresponse/negotiation_spec.rb +464 -0
- data/spec/strelka/httpresponse_spec.rb +114 -0
- data/spec/strelka/mixins_spec.rb +99 -0
- data.tar.gz.sig +1 -0
- metadata +175 -79
- metadata.gz.sig +2 -0
- data/IDEAS.textile +0 -174
- data/data/strelka/apps/strelka-admin +0 -65
- data/data/strelka/apps/strelka-setup +0 -26
- data/data/strelka/bootstrap-config.rb +0 -34
- data/data/strelka/templates/admin/console.tmpl +0 -21
- data/data/strelka/templates/layout.tmpl +0 -30
- data/lib/strelka/process.rb +0 -19
@@ -7,6 +7,7 @@ BEGIN {
|
|
7
7
|
$LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
|
8
8
|
}
|
9
9
|
|
10
|
+
require 'uri'
|
10
11
|
require 'rspec'
|
11
12
|
require 'spec/lib/helpers'
|
12
13
|
require 'strelka/httprequest'
|
@@ -27,27 +28,216 @@ describe Strelka::HTTPRequest do
|
|
27
28
|
reset_logging()
|
28
29
|
end
|
29
30
|
|
31
|
+
context "instance" do
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
before( :each ) do
|
34
|
+
@req = @request_factory.get( '/directory/userinfo/ged' )
|
35
|
+
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
it "knows what the request's parsed URI is" do
|
38
|
+
@req.uri.should be_a( URI )
|
39
|
+
@req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "knows what Mongrel2 route it followed" do
|
43
|
+
@req.pattern.should == '/directory'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "knows what the URI of the route handling the request is" do
|
47
|
+
@req.base_uri.should be_a( URI )
|
48
|
+
@req.base_uri.to_s.should == 'http://localhost:8080/directory'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "knows what the path of the request past its route is" do
|
52
|
+
@req.app_path.should == '/userinfo/ged'
|
53
|
+
end
|
54
|
+
|
55
|
+
it "knows what HTTP verb the request used" do
|
56
|
+
@req.verb.should == :GET
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can get and set notes for communication between plugins" do
|
60
|
+
@req.notes.should be_a( Hash )
|
61
|
+
@req.notes[:routing].should be_a( Hash )
|
62
|
+
@req.notes[:routing][:route].should be_a( Hash )
|
63
|
+
end
|
40
64
|
|
41
|
-
it "knows what Mongrel2 route it followed" do
|
42
|
-
@req.pattern.should == '/directory'
|
43
65
|
end
|
44
66
|
|
45
|
-
|
46
|
-
|
67
|
+
|
68
|
+
context "instance with a query string" do
|
69
|
+
|
70
|
+
before( :each ) do
|
71
|
+
@req = @request_factory.get( '/directory/userinfo/ged?limit=10;offset=20' )
|
72
|
+
end
|
73
|
+
|
74
|
+
it "knows what the request's parsed URI is" do
|
75
|
+
@req.uri.should be_a( URI )
|
76
|
+
@req.uri.to_s.should == 'http://localhost:8080/directory/userinfo/ged?limit=10;offset=20'
|
77
|
+
end
|
78
|
+
|
79
|
+
it "knows what Mongrel2 route it followed" do
|
80
|
+
@req.pattern.should == '/directory'
|
81
|
+
end
|
82
|
+
|
83
|
+
it "knows what the URI of the route handling the request is" do
|
84
|
+
@req.base_uri.should be_a( URI )
|
85
|
+
@req.base_uri.to_s.should == 'http://localhost:8080/directory'
|
86
|
+
end
|
87
|
+
|
88
|
+
it "knows what the path of the request past its route is" do
|
89
|
+
@req.app_path.should == '/userinfo/ged'
|
90
|
+
@req.app_path.should == '/userinfo/ged' # make sure the slice is non-destructive
|
91
|
+
end
|
92
|
+
|
93
|
+
it "knows what HTTP verb the request used" do
|
94
|
+
@req.verb.should == :GET
|
95
|
+
end
|
96
|
+
|
47
97
|
end
|
48
98
|
|
49
|
-
|
50
|
-
|
99
|
+
|
100
|
+
describe "request-parameter parsing" do
|
101
|
+
|
102
|
+
context "a GET request" do
|
103
|
+
it "has an empty params Hash if the request doesn't have a query string " do
|
104
|
+
req = @request_factory.get( '/directory/path' )
|
105
|
+
req.params.should == {}
|
106
|
+
end
|
107
|
+
|
108
|
+
it "has a params Hash with the key/value pair in it if the query string has " +
|
109
|
+
"one key/value pair" do
|
110
|
+
req = @request_factory.get( '/directory/path?foo=bar' )
|
111
|
+
req.params.should == {'foo' => 'bar'}
|
112
|
+
end
|
113
|
+
|
114
|
+
it "has a params Hash with the key/value pairs in it if the query string has " +
|
115
|
+
"two pairs seperated with a an ampersand" do
|
116
|
+
req = @request_factory.get( '/directory/path?foo=bar&chunky=pork' )
|
117
|
+
req.params.should == {
|
118
|
+
'foo' => 'bar',
|
119
|
+
'chunky' => 'pork',
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "has a params Hash with the key/value pairs in it if the query string has " +
|
124
|
+
"two pairs with a semi-colon separator" do
|
125
|
+
req = @request_factory.get( '/directory/path?potato=gun;legume=bazooka' )
|
126
|
+
req.params.should == {
|
127
|
+
'potato' => 'gun',
|
128
|
+
'legume' => 'bazooka',
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
it "has a params Hash with an Array of values if the query string has two values " +
|
133
|
+
"for the same key" do
|
134
|
+
req = @request_factory.get( '/directory/path?foo=bar&foo=baz' )
|
135
|
+
req.params.should == {
|
136
|
+
'foo' => ['bar', 'baz'],
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
it "has a params Hash with one Array of values and a scalar value if the query " +
|
141
|
+
"string has three values and two keys" do
|
142
|
+
req = @request_factory.get( '/directory/path?foo=bar&foo=pork;mirror=sequel' )
|
143
|
+
req.params.should == {
|
144
|
+
'foo' => ['bar', 'pork'],
|
145
|
+
'mirror' => 'sequel',
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "a POST request with a 'application/x-www-form-urlencoded' body" do
|
151
|
+
|
152
|
+
before( :each ) do
|
153
|
+
@req = @request_factory.post( '/directory/path', '',
|
154
|
+
'Content-type' => 'application/x-www-form-urlencoded' )
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns an empty Hash for an empty body" do
|
158
|
+
@req.body = ''
|
159
|
+
@req.params.should == {}
|
160
|
+
end
|
161
|
+
|
162
|
+
it "has a params Hash with the key/value pair in it if the form data has " +
|
163
|
+
"one key/value pair" do
|
164
|
+
@req.body = 'foo=bar'
|
165
|
+
@req.params.should == {'foo' => 'bar'}
|
166
|
+
end
|
167
|
+
|
168
|
+
it "has a params Hash with the key/value pairs in it if the form data has " +
|
169
|
+
"two pairs seperated with a an ampersand" do
|
170
|
+
@req.body = 'foo=bar&chunky=pork'
|
171
|
+
@req.params.should == {
|
172
|
+
'foo' => 'bar',
|
173
|
+
'chunky' => 'pork',
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
it "has a params Hash with the key/value pairs in it if the form data has " +
|
178
|
+
"two pairs with a semi-colon separator" do
|
179
|
+
@req.body = 'potato=gun;legume=bazooka'
|
180
|
+
@req.params.should == {
|
181
|
+
'potato' => 'gun',
|
182
|
+
'legume' => 'bazooka',
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
it "has a params Hash with an Array of values if the form data has two values " +
|
187
|
+
"for the same key" do
|
188
|
+
@req.body = 'foo=bar&foo=baz'
|
189
|
+
@req.params.should == { 'foo' => ['bar', 'baz'] }
|
190
|
+
end
|
191
|
+
|
192
|
+
it "has a params Hash with one Array of values and a scalar value if the form " +
|
193
|
+
"data has three values and two keys" do
|
194
|
+
@req.body = 'foo=bar&foo=pork;mirror=sequel'
|
195
|
+
@req.params.should == {
|
196
|
+
'foo' => ['bar', 'pork'],
|
197
|
+
'mirror' => 'sequel',
|
198
|
+
}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "a POST request with a 'multipart/form-data' body" do
|
203
|
+
|
204
|
+
before( :each ) do
|
205
|
+
@req = @request_factory.post( '/directory/path', '',
|
206
|
+
'Content-type' => 'multipart/form-data' )
|
207
|
+
end
|
208
|
+
|
209
|
+
it "returns nil for an empty body" do
|
210
|
+
pending "multipart/form-data support" do
|
211
|
+
@req.body = ''
|
212
|
+
@req.params.should be_nil()
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
context "a POST request with a 'application/json' body" do
|
220
|
+
before( :each ) do
|
221
|
+
@req = @request_factory.post( '/directory/path', '',
|
222
|
+
'Content-type' => 'application/json' )
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns nil for an empty body" do
|
226
|
+
@req.body = ''
|
227
|
+
@req.params.should be_nil()
|
228
|
+
end
|
229
|
+
|
230
|
+
it "has the JSON data as the params if it has a body with JSON object in it" do
|
231
|
+
data = {
|
232
|
+
'animal' => 'ducky',
|
233
|
+
'adjectives' => ['fluffy', 'puddle-ey'],
|
234
|
+
}
|
235
|
+
@req.body = Yajl.dump( data )
|
236
|
+
@req.params.should == data
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
51
241
|
end
|
52
242
|
|
53
243
|
end
|
@@ -0,0 +1,464 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
BEGIN {
|
5
|
+
require 'pathname'
|
6
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent.parent
|
7
|
+
|
8
|
+
libdir = basedir + "lib"
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift( basedir ) unless $LOAD_PATH.include?( basedir )
|
11
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
12
|
+
}
|
13
|
+
|
14
|
+
require 'rspec'
|
15
|
+
|
16
|
+
require 'spec/lib/helpers'
|
17
|
+
|
18
|
+
require 'strelka'
|
19
|
+
require 'strelka/httprequest/negotiation'
|
20
|
+
require 'strelka/httpresponse/negotiation'
|
21
|
+
|
22
|
+
|
23
|
+
#####################################################################
|
24
|
+
### C O N T E X T S
|
25
|
+
#####################################################################
|
26
|
+
|
27
|
+
describe Strelka::HTTPResponse::Negotiation do
|
28
|
+
|
29
|
+
before( :all ) do
|
30
|
+
setup_logging( :fatal )
|
31
|
+
@request_factory = Mongrel2::RequestFactory.new( route: '/service/user' )
|
32
|
+
end
|
33
|
+
|
34
|
+
after( :all ) do
|
35
|
+
reset_logging()
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
before( :each ) do
|
40
|
+
@app = Class.new( Strelka::App ) { plugins :negotiation }
|
41
|
+
@req = @request_factory.get( '/service/user/estark' )
|
42
|
+
@res = @req.response
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
describe "content-alternative callback methods" do
|
47
|
+
|
48
|
+
it "can provide blocks for bodies of several different mediatypes" do
|
49
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7, text/xml; q=0.2'
|
50
|
+
|
51
|
+
@res.for( 'application/json' ) { %{["a JSON dump"]} }
|
52
|
+
@res.for( 'application/x-yaml' ) { "---\na: YAML dump\n\n" }
|
53
|
+
|
54
|
+
@res.negotiated_body.should == "---\na: YAML dump\n\n"
|
55
|
+
@res.content_type.should == "application/x-yaml"
|
56
|
+
@res.header_data.should =~ /accept(?!-)/i
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can provide a single block for bodies of several different mediatypes" do
|
60
|
+
@req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
|
61
|
+
|
62
|
+
@res.for( 'application/json', 'application/x-yaml' ) do
|
63
|
+
{ uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
|
64
|
+
end
|
65
|
+
|
66
|
+
@res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
|
67
|
+
"\"name\":\"Harlot's Garden\"}"
|
68
|
+
@res.content_type.should == "application/json"
|
69
|
+
@res.header_data.should =~ /accept(?!-)/i
|
70
|
+
end
|
71
|
+
|
72
|
+
it "can provide a block for bodies of several different symbolic mediatypes" do
|
73
|
+
@req.headers.accept = 'application/x-yaml; q=0.7, application/json; q=0.9'
|
74
|
+
|
75
|
+
@res.for( :json, :yaml ) do
|
76
|
+
{ uuid: 'fc85e35b-c9c3-4675-a882-25bf98d11e1b', name: "Harlot's Garden" }
|
77
|
+
end
|
78
|
+
|
79
|
+
@res.negotiated_body.should == "{\"uuid\":\"fc85e35b-c9c3-4675-a882-25bf98d11e1b\"," +
|
80
|
+
"\"name\":\"Harlot's Garden\"}"
|
81
|
+
@res.content_type.should == "application/json"
|
82
|
+
@res.header_data.should =~ /accept(?!-)/i
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
describe "automatic content transcoding" do
|
89
|
+
|
90
|
+
it "transcodes String entity bodies if the charset is not acceptable" do
|
91
|
+
@req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
|
92
|
+
|
93
|
+
@res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
|
94
|
+
@res.content_type = 'text/plain'
|
95
|
+
|
96
|
+
@res.negotiated_body.encoding.should == Encoding::KOI8_R
|
97
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
98
|
+
end
|
99
|
+
|
100
|
+
it "transcodes String entity bodies if the charset is not acceptable" do
|
101
|
+
@req.headers.accept_charset = 'utf-8'
|
102
|
+
|
103
|
+
@res.body = File.read( __FILE__, encoding: 'iso-8859-5' )
|
104
|
+
@res.content_type = 'application/json'
|
105
|
+
|
106
|
+
@res.negotiated_body.encoding.should == Encoding::UTF_8
|
107
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
108
|
+
end
|
109
|
+
|
110
|
+
it "transcodes File entity bodies if the charset is not acceptable" do
|
111
|
+
pending "implementation of IO transcoding" do
|
112
|
+
@req.headers.accept_charset = 'koi8-r, koi8-u;q=0.9, utf-8;q=0.8'
|
113
|
+
|
114
|
+
@res.body = File.open( __FILE__, 'r:iso-8859-5' )
|
115
|
+
@res.content_type = 'text/plain'
|
116
|
+
|
117
|
+
@res.negotiated_body.encoding.should == Encoding::KOI8_R
|
118
|
+
@res.header_data.should =~ /accept-charset(?!-)/i
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
describe "language alternative callback methods" do
|
126
|
+
|
127
|
+
it "can provide blocks for alternative bodies of several different languages" do
|
128
|
+
@req.headers.accept = 'text/plain'
|
129
|
+
@req.headers.accept_language = 'de'
|
130
|
+
|
131
|
+
@res.puts( "the English body" )
|
132
|
+
@res.languages << :en
|
133
|
+
@res.content_type = 'text/plain'
|
134
|
+
@res.for_language( :de ) { "German translation" }
|
135
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
136
|
+
|
137
|
+
@res.negotiated_body.should == "German translation"
|
138
|
+
@res.languages.should == ["de"]
|
139
|
+
@res.header_data.should =~ /accept-language/i
|
140
|
+
end
|
141
|
+
|
142
|
+
it "can provide blocks for bodies of several different languages without setting a " +
|
143
|
+
"default entity body" do
|
144
|
+
@req.headers.accept = 'text/plain'
|
145
|
+
@req.headers.accept_language = 'de, en-gb;q=0.9, en;q=0.7'
|
146
|
+
|
147
|
+
@res.content_type = 'text/plain'
|
148
|
+
@res.for_language( :en ) { "English translation" }
|
149
|
+
@res.for_language( :de ) { "German translation" }
|
150
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
151
|
+
|
152
|
+
@res.negotiated_body.should == "German translation"
|
153
|
+
@res.languages.should == ["de"]
|
154
|
+
@res.header_data.should =~ /accept-language/i
|
155
|
+
end
|
156
|
+
|
157
|
+
it "can provide a single block for bodies of several different languages" do
|
158
|
+
@req.headers.accept_language = 'fr;q=0.9, de;q=0.7, en;q=0.7, pt'
|
159
|
+
translations = {
|
160
|
+
:pt => "Portuguese translation",
|
161
|
+
:fr => "French translation",
|
162
|
+
:de => "German translation",
|
163
|
+
:en => "English translation",
|
164
|
+
}
|
165
|
+
|
166
|
+
@res.content_type = 'text/plain'
|
167
|
+
@res.for_language( translations.keys ) do |lang|
|
168
|
+
translations[ lang.to_sym ]
|
169
|
+
end
|
170
|
+
|
171
|
+
@res.negotiated_body.should == "Portuguese translation"
|
172
|
+
@res.languages.should == ["pt"]
|
173
|
+
@res.header_data.should =~ /accept-language/i
|
174
|
+
end
|
175
|
+
|
176
|
+
it "calls the first block for requests with no accept-language header" do
|
177
|
+
@req.headers.delete( :accept_language )
|
178
|
+
@req.headers.accept = 'text/plain'
|
179
|
+
|
180
|
+
@res.content_type = 'text/plain'
|
181
|
+
@res.for_language( :en ) { "English translation" }
|
182
|
+
@res.for_language( :de ) { "German translation" }
|
183
|
+
@res.for_language( :sl ) { "Slovenian translation" }
|
184
|
+
|
185
|
+
@res.negotiated_body.should == "English translation"
|
186
|
+
@res.languages.should == ["en"]
|
187
|
+
@res.header_data.should =~ /accept-language/i
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
describe "content coding alternative callback methods" do
|
193
|
+
|
194
|
+
it "can provide blocks for content coding" do
|
195
|
+
@req.headers.accept = 'text/plain'
|
196
|
+
@req.headers.accept_encoding = 'gzip'
|
197
|
+
|
198
|
+
@res << "the text body"
|
199
|
+
@res.content_type = 'text/plain'
|
200
|
+
@res.for_encoding( :deflate ) { @res.body + " (deflated)" }
|
201
|
+
@res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
|
202
|
+
|
203
|
+
@res.negotiated_body.should == "the text body (gzipped)"
|
204
|
+
@res.encodings.should include( "gzip" )
|
205
|
+
@res.header_data.should =~ /accept-encoding/i
|
206
|
+
@res.header_data.should_not =~ /identity/i
|
207
|
+
end
|
208
|
+
|
209
|
+
it "chooses the content coding with the highest qvalue" do
|
210
|
+
@req.headers.accept = 'text/plain'
|
211
|
+
@req.headers.accept_encoding = 'gzip;q=0.7, deflate'
|
212
|
+
|
213
|
+
@res << "the text body"
|
214
|
+
@res.content_type = 'text/plain'
|
215
|
+
@res.for_encoding( :deflate ) { @res.body + " (deflated)" }
|
216
|
+
@res.for_encoding( :gzip ) { @res.body + " (gzipped)" }
|
217
|
+
|
218
|
+
@res.negotiated_body.should == "the text body (deflated)"
|
219
|
+
@res.encodings.should include( "deflate" )
|
220
|
+
@res.header_data.should =~ /accept-encoding/i
|
221
|
+
@res.header_data.should_not =~ /identity/i
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
describe "content-type acceptance predicates" do
|
228
|
+
|
229
|
+
it "knows that it is acceptable if its content_type is in the list of accepted types " +
|
230
|
+
"in its request" do
|
231
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7'
|
232
|
+
@res.content_type = 'application/json'
|
233
|
+
|
234
|
+
@res.should have_acceptable_content_type()
|
235
|
+
end
|
236
|
+
|
237
|
+
it "knows that it is acceptable if its request doesn't have accepted types" do
|
238
|
+
@req.headers.delete( :accept )
|
239
|
+
@res.content_type = 'application/x-ruby-marshalled'
|
240
|
+
|
241
|
+
@res.should have_acceptable_content_type()
|
242
|
+
end
|
243
|
+
|
244
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
245
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
246
|
+
res.extend( Strelka::HTTPResponse::Negotiation )
|
247
|
+
res.content_type = 'application/x-ruby-marshalled'
|
248
|
+
|
249
|
+
res.should have_acceptable_content_type()
|
250
|
+
end
|
251
|
+
|
252
|
+
it "knows that it is not acceptable if its content_type isn't in the list of " +
|
253
|
+
"accepted types in its request" do
|
254
|
+
@req.headers.accept = 'application/x-yaml, application/json; q=0.7'
|
255
|
+
@res.content_type = 'application/x-ruby-marshalled'
|
256
|
+
|
257
|
+
@res.should_not have_acceptable_content_type()
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
describe "charset acceptance predicates" do
|
264
|
+
|
265
|
+
it "knows that it is acceptable if its explicit charset is in the list of accepted " +
|
266
|
+
"charsets in its request" do
|
267
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
268
|
+
@res.charset = 'iso-8859-5'
|
269
|
+
|
270
|
+
@res.should have_acceptable_charset()
|
271
|
+
end
|
272
|
+
|
273
|
+
it "knows that it is acceptable if its request doesn't have accepted types" do
|
274
|
+
@req.headers.delete( :accept_charset )
|
275
|
+
@res.charset = 'koi8-u'
|
276
|
+
@res.should have_acceptable_charset()
|
277
|
+
end
|
278
|
+
|
279
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
280
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
281
|
+
res.charset = 'iso8859-15'
|
282
|
+
|
283
|
+
res.should have_acceptable_charset()
|
284
|
+
end
|
285
|
+
|
286
|
+
it "knows that it is acceptable if its explicit charset is set to ascii-8bit" do
|
287
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
288
|
+
@res.content_type = 'image/jpeg'
|
289
|
+
@res.charset = Encoding::ASCII_8BIT
|
290
|
+
|
291
|
+
@res.should have_acceptable_charset()
|
292
|
+
end
|
293
|
+
|
294
|
+
it "knows that it is acceptable if no charset can be derived, but the list of " +
|
295
|
+
"acceptable charsets includes ISO8859-1" do
|
296
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
297
|
+
@res.content_type = 'text/plain'
|
298
|
+
@res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
|
299
|
+
|
300
|
+
@res.should have_acceptable_charset()
|
301
|
+
end
|
302
|
+
|
303
|
+
it "knows that it is not acceptable if no charset can be derived, the content is a " +
|
304
|
+
"text subtype, and the list of acceptable charsets doesn't include ISO8859-1" do
|
305
|
+
@req.headers.accept_charset = 'iso-8859-15, utf-8;q=0.8'
|
306
|
+
@res.content_type = 'text/plain'
|
307
|
+
@res.body = "some stuff".force_encoding( Encoding::ASCII_8BIT )
|
308
|
+
|
309
|
+
@res.should_not have_acceptable_charset()
|
310
|
+
end
|
311
|
+
|
312
|
+
it "knows that it is not acceptable if its explicit charset isn't in the list of " +
|
313
|
+
"accepted charsets in its request" do
|
314
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
315
|
+
@res.charset = 'sjis'
|
316
|
+
|
317
|
+
@res.should_not have_acceptable_charset()
|
318
|
+
end
|
319
|
+
|
320
|
+
it "knows that it is not acceptable if the charset in its content-type header isn't in " +
|
321
|
+
"the list of accepted charsets in its request" do
|
322
|
+
@req.headers.accept_charset = 'iso-8859-5, utf-8;q=0.8'
|
323
|
+
@res.content_type = 'text/plain; charset=sjis'
|
324
|
+
|
325
|
+
@res.should_not have_acceptable_charset()
|
326
|
+
end
|
327
|
+
|
328
|
+
it "knows that it is not acceptable if the charset derived from its entity body isn't in " +
|
329
|
+
"the list of accepted charsets in its request" do
|
330
|
+
@req.headers.accept_charset = 'iso-8859-1, utf-8;q=0.8'
|
331
|
+
@res.content_type = 'text/plain'
|
332
|
+
@res.body = File.open( __FILE__, 'r:iso8859-5' )
|
333
|
+
|
334
|
+
@res.should_not have_acceptable_charset()
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
describe "language acceptance predicates" do
|
341
|
+
|
342
|
+
it "knows that it is acceptable if it has a single language that's in the list of " +
|
343
|
+
"languages accepted by its originating request" do
|
344
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
345
|
+
@res.languages << 'ja'
|
346
|
+
|
347
|
+
@res.should have_acceptable_language()
|
348
|
+
end
|
349
|
+
|
350
|
+
it "knows that it is acceptable if all of its multiple languages are in the list of " +
|
351
|
+
"languages accepted by its originating request" do
|
352
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
353
|
+
@res.languages << 'ja' << 'en-us'
|
354
|
+
|
355
|
+
@res.should have_acceptable_language()
|
356
|
+
end
|
357
|
+
|
358
|
+
# I'm not sure if this is what RFC1616 means. It might be that *all* of its languages
|
359
|
+
# have to be in the accept-language: list.
|
360
|
+
it "knows that it is acceptable if one of its multiple languages is in the " +
|
361
|
+
"list of languages accepted by its originating request" do
|
362
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
363
|
+
@res.languages << 'pt' << 'en'
|
364
|
+
|
365
|
+
@res.should have_acceptable_language()
|
366
|
+
end
|
367
|
+
|
368
|
+
it "knows that it is acceptable if it has a body but doesn't have a language set" do
|
369
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
370
|
+
@res.languages.clear
|
371
|
+
@res.puts( "Some content in an unspecified language." )
|
372
|
+
|
373
|
+
@res.should have_acceptable_language()
|
374
|
+
end
|
375
|
+
|
376
|
+
it "knows that it is acceptable if it has no body yet" do
|
377
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
378
|
+
@res.languages.clear
|
379
|
+
|
380
|
+
@res.should have_acceptable_language()
|
381
|
+
end
|
382
|
+
|
383
|
+
it "knows that it is acceptable if it doesn't have an originating request" do
|
384
|
+
res = Strelka::HTTPResponse.new( 'appid', 88 )
|
385
|
+
res.extend( Strelka::HTTPResponse::Negotiation )
|
386
|
+
res.languages << 'kh'
|
387
|
+
|
388
|
+
res.should have_acceptable_language()
|
389
|
+
end
|
390
|
+
|
391
|
+
it "knows that it is not acceptable if it has a single language that isn't in the " +
|
392
|
+
"list of languages accepted by its originating request" do
|
393
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
394
|
+
@res.languages << 'pt'
|
395
|
+
|
396
|
+
@res.should_not have_acceptable_language()
|
397
|
+
end
|
398
|
+
|
399
|
+
it "knows that it is not acceptable if it has multiple languages, none of which are " +
|
400
|
+
"in the list of languages accepted by its originating request" do
|
401
|
+
@req.headers.accept_language = 'en-gb, en; q=0.7, ja;q=0.2'
|
402
|
+
@res.languages << 'pt-br' << 'fr-ca'
|
403
|
+
|
404
|
+
@res.should_not have_acceptable_language()
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
describe "encoding acceptance predicates" do
|
411
|
+
|
412
|
+
it "knows that it is acceptable if its content coding is in the list of accepted " +
|
413
|
+
"codings in its originating request" do
|
414
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
415
|
+
@res.encodings << 'gzip'
|
416
|
+
|
417
|
+
@res.should have_acceptable_encoding()
|
418
|
+
end
|
419
|
+
|
420
|
+
it "knows that it is acceptable if all of its content codings are in the list of accepted " +
|
421
|
+
"codings in its originating request" do
|
422
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, frobnify;q=0.9, identity; q=0.5, *;q=0'
|
423
|
+
@res.encodings << 'gzip' << 'frobnify'
|
424
|
+
|
425
|
+
@res.should have_acceptable_encoding()
|
426
|
+
end
|
427
|
+
|
428
|
+
it "knows that it is not acceptable if one of its content codings is not in the list " +
|
429
|
+
"of accepted codings in its originating request" do
|
430
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0.5, *;q=0'
|
431
|
+
@res.encodings << 'gzip' << 'frobnify'
|
432
|
+
|
433
|
+
@res.should_not have_acceptable_encoding()
|
434
|
+
end
|
435
|
+
|
436
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings " +
|
437
|
+
"and 'identity' is explicitly not accepted in its originating request" do
|
438
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
|
439
|
+
@res.encodings.clear
|
440
|
+
|
441
|
+
@res.should_not have_acceptable_encoding()
|
442
|
+
end
|
443
|
+
|
444
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings " +
|
445
|
+
"and 'identity' is explicitly not accepted in its originating request" do
|
446
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, identity; q=0'
|
447
|
+
@res.encodings.clear
|
448
|
+
|
449
|
+
@res.should_not have_acceptable_encoding()
|
450
|
+
end
|
451
|
+
|
452
|
+
it "knows that it is not acceptable if it doesn't have any explicit content codings, " +
|
453
|
+
"the wildcard content-coding is disallowed, and 'identity' is not explicitly accepted" do
|
454
|
+
@req.headers.accept_encoding = 'gzip;q=1.0, *;q=0'
|
455
|
+
@res.encodings.clear
|
456
|
+
|
457
|
+
@res.should_not have_acceptable_encoding()
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
end
|
464
|
+
|