rack 1.1.6 → 1.6.9

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.
Files changed (212) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +1 -1
  3. data/HISTORY.md +375 -0
  4. data/KNOWN-ISSUES +23 -0
  5. data/README.rdoc +312 -0
  6. data/Rakefile +124 -0
  7. data/SPEC +125 -32
  8. data/contrib/rack.png +0 -0
  9. data/contrib/rack.svg +150 -0
  10. data/contrib/rack_logo.svg +1 -1
  11. data/contrib/rdoc.css +412 -0
  12. data/example/protectedlobster.rb +1 -1
  13. data/lib/rack/auth/abstract/handler.rb +4 -4
  14. data/lib/rack/auth/abstract/request.rb +7 -5
  15. data/lib/rack/auth/basic.rb +1 -1
  16. data/lib/rack/auth/digest/md5.rb +7 -3
  17. data/lib/rack/auth/digest/nonce.rb +1 -1
  18. data/lib/rack/auth/digest/params.rb +7 -9
  19. data/lib/rack/auth/digest/request.rb +10 -9
  20. data/lib/rack/backports/uri/common_18.rb +56 -0
  21. data/lib/rack/backports/uri/common_192.rb +52 -0
  22. data/lib/rack/backports/uri/common_193.rb +29 -0
  23. data/lib/rack/body_proxy.rb +39 -0
  24. data/lib/rack/builder.rb +106 -22
  25. data/lib/rack/cascade.rb +17 -6
  26. data/lib/rack/chunked.rb +44 -24
  27. data/lib/rack/commonlogger.rb +36 -13
  28. data/lib/rack/conditionalget.rb +49 -17
  29. data/lib/rack/config.rb +5 -0
  30. data/lib/rack/content_length.rb +14 -6
  31. data/lib/rack/content_type.rb +7 -1
  32. data/lib/rack/deflater.rb +73 -15
  33. data/lib/rack/directory.rb +18 -8
  34. data/lib/rack/etag.rb +59 -9
  35. data/lib/rack/file.rb +106 -44
  36. data/lib/rack/handler/cgi.rb +11 -11
  37. data/lib/rack/handler/fastcgi.rb +18 -6
  38. data/lib/rack/handler/lsws.rb +2 -4
  39. data/lib/rack/handler/mongrel.rb +22 -6
  40. data/lib/rack/handler/scgi.rb +16 -8
  41. data/lib/rack/handler/thin.rb +19 -4
  42. data/lib/rack/handler/webrick.rb +72 -19
  43. data/lib/rack/handler.rb +47 -14
  44. data/lib/rack/head.rb +10 -2
  45. data/lib/rack/lint.rb +260 -75
  46. data/lib/rack/lobster.rb +13 -8
  47. data/lib/rack/lock.rb +13 -3
  48. data/lib/rack/logger.rb +0 -2
  49. data/lib/rack/methodoverride.rb +27 -8
  50. data/lib/rack/mime.rb +625 -167
  51. data/lib/rack/mock.rb +78 -53
  52. data/lib/rack/multipart/generator.rb +93 -0
  53. data/lib/rack/multipart/parser.rb +253 -0
  54. data/lib/rack/multipart/uploaded_file.rb +34 -0
  55. data/lib/rack/multipart.rb +34 -0
  56. data/lib/rack/nulllogger.rb +21 -2
  57. data/lib/rack/recursive.rb +10 -5
  58. data/lib/rack/reloader.rb +3 -2
  59. data/lib/rack/request.rb +201 -74
  60. data/lib/rack/response.rb +41 -28
  61. data/lib/rack/rewindable_input.rb +15 -11
  62. data/lib/rack/runtime.rb +16 -3
  63. data/lib/rack/sendfile.rb +47 -29
  64. data/lib/rack/server.rb +223 -47
  65. data/lib/rack/session/abstract/id.rb +289 -30
  66. data/lib/rack/session/cookie.rb +133 -44
  67. data/lib/rack/session/memcache.rb +30 -56
  68. data/lib/rack/session/pool.rb +19 -43
  69. data/lib/rack/showexceptions.rb +53 -15
  70. data/lib/rack/showstatus.rb +14 -7
  71. data/lib/rack/static.rb +124 -12
  72. data/lib/rack/tempfile_reaper.rb +22 -0
  73. data/lib/rack/urlmap.rb +49 -15
  74. data/lib/rack/utils/okjson.rb +600 -0
  75. data/lib/rack/utils.rb +363 -361
  76. data/lib/rack.rb +17 -23
  77. data/rack.gemspec +11 -20
  78. data/test/builder/anything.rb +5 -0
  79. data/test/builder/comment.ru +4 -0
  80. data/test/builder/end.ru +5 -0
  81. data/test/builder/line.ru +1 -0
  82. data/test/builder/options.ru +2 -0
  83. data/test/cgi/assets/folder/test.js +1 -0
  84. data/test/cgi/assets/fonts/font.eot +1 -0
  85. data/test/cgi/assets/images/image.png +1 -0
  86. data/test/cgi/assets/index.html +1 -0
  87. data/test/cgi/assets/javascripts/app.js +1 -0
  88. data/test/cgi/assets/stylesheets/app.css +1 -0
  89. data/test/cgi/lighttpd.conf +26 -0
  90. data/test/cgi/rackup_stub.rb +6 -0
  91. data/test/cgi/sample_rackup.ru +5 -0
  92. data/test/cgi/test +9 -0
  93. data/test/cgi/test+directory/test+file +1 -0
  94. data/test/cgi/test.fcgi +8 -0
  95. data/test/cgi/test.ru +5 -0
  96. data/test/gemloader.rb +10 -0
  97. data/test/multipart/bad_robots +259 -0
  98. data/test/multipart/binary +0 -0
  99. data/test/multipart/content_type_and_no_filename +6 -0
  100. data/test/multipart/empty +10 -0
  101. data/test/multipart/fail_16384_nofile +814 -0
  102. data/test/multipart/file1.txt +1 -0
  103. data/test/multipart/filename_and_modification_param +7 -0
  104. data/test/multipart/filename_and_no_name +6 -0
  105. data/test/multipart/filename_with_escaped_quotes +6 -0
  106. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  107. data/test/multipart/filename_with_null_byte +7 -0
  108. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  109. data/test/multipart/filename_with_unescaped_percentages +6 -0
  110. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  111. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  112. data/test/multipart/filename_with_unescaped_quotes +6 -0
  113. data/test/multipart/ie +6 -0
  114. data/test/multipart/invalid_character +6 -0
  115. data/test/multipart/mixed_files +21 -0
  116. data/test/multipart/nested +10 -0
  117. data/test/multipart/none +9 -0
  118. data/test/multipart/semicolon +6 -0
  119. data/test/multipart/text +15 -0
  120. data/test/multipart/three_files_three_fields +31 -0
  121. data/test/multipart/webkit +32 -0
  122. data/test/rackup/config.ru +31 -0
  123. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  124. data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
  125. data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
  126. data/test/spec_body_proxy.rb +85 -0
  127. data/test/spec_builder.rb +223 -0
  128. data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
  129. data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
  130. data/test/spec_chunked.rb +101 -0
  131. data/test/spec_commonlogger.rb +93 -0
  132. data/test/spec_conditionalget.rb +102 -0
  133. data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
  134. data/test/spec_content_length.rb +85 -0
  135. data/test/spec_content_type.rb +45 -0
  136. data/test/spec_deflater.rb +339 -0
  137. data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
  138. data/test/spec_etag.rb +107 -0
  139. data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
  140. data/test/spec_file.rb +221 -0
  141. data/test/spec_handler.rb +72 -0
  142. data/test/spec_head.rb +45 -0
  143. data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
  144. data/test/spec_lobster.rb +58 -0
  145. data/test/spec_lock.rb +164 -0
  146. data/test/spec_logger.rb +23 -0
  147. data/test/spec_methodoverride.rb +95 -0
  148. data/test/spec_mime.rb +51 -0
  149. data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
  150. data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
  151. data/test/spec_multipart.rb +600 -0
  152. data/test/spec_nulllogger.rb +20 -0
  153. data/test/spec_recursive.rb +72 -0
  154. data/test/spec_request.rb +1227 -0
  155. data/test/spec_response.rb +407 -0
  156. data/test/spec_rewindable_input.rb +118 -0
  157. data/test/spec_runtime.rb +49 -0
  158. data/test/spec_sendfile.rb +130 -0
  159. data/test/spec_server.rb +167 -0
  160. data/test/spec_session_abstract_id.rb +53 -0
  161. data/test/spec_session_cookie.rb +410 -0
  162. data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
  163. data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
  164. data/test/spec_showexceptions.rb +85 -0
  165. data/test/spec_showstatus.rb +103 -0
  166. data/test/spec_static.rb +145 -0
  167. data/test/spec_tempfile_reaper.rb +63 -0
  168. data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
  169. data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
  170. data/test/spec_utils.rb +647 -0
  171. data/test/spec_version.rb +17 -0
  172. data/test/spec_webrick.rb +184 -0
  173. data/test/static/another/index.html +1 -0
  174. data/test/static/index.html +1 -0
  175. data/test/testrequest.rb +78 -0
  176. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  177. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  178. metadata +220 -239
  179. data/RDOX +0 -0
  180. data/README +0 -592
  181. data/lib/rack/adapter/camping.rb +0 -22
  182. data/test/spec_auth.rb +0 -57
  183. data/test/spec_rack_builder.rb +0 -84
  184. data/test/spec_rack_camping.rb +0 -55
  185. data/test/spec_rack_chunked.rb +0 -62
  186. data/test/spec_rack_commonlogger.rb +0 -61
  187. data/test/spec_rack_conditionalget.rb +0 -41
  188. data/test/spec_rack_content_length.rb +0 -43
  189. data/test/spec_rack_content_type.rb +0 -30
  190. data/test/spec_rack_deflater.rb +0 -127
  191. data/test/spec_rack_etag.rb +0 -17
  192. data/test/spec_rack_file.rb +0 -75
  193. data/test/spec_rack_handler.rb +0 -43
  194. data/test/spec_rack_head.rb +0 -30
  195. data/test/spec_rack_lobster.rb +0 -45
  196. data/test/spec_rack_lock.rb +0 -38
  197. data/test/spec_rack_logger.rb +0 -21
  198. data/test/spec_rack_methodoverride.rb +0 -60
  199. data/test/spec_rack_nulllogger.rb +0 -13
  200. data/test/spec_rack_recursive.rb +0 -77
  201. data/test/spec_rack_request.rb +0 -594
  202. data/test/spec_rack_response.rb +0 -221
  203. data/test/spec_rack_rewindable_input.rb +0 -118
  204. data/test/spec_rack_runtime.rb +0 -35
  205. data/test/spec_rack_sendfile.rb +0 -86
  206. data/test/spec_rack_session_cookie.rb +0 -92
  207. data/test/spec_rack_showexceptions.rb +0 -21
  208. data/test/spec_rack_showstatus.rb +0 -72
  209. data/test/spec_rack_static.rb +0 -37
  210. data/test/spec_rack_utils.rb +0 -557
  211. data/test/spec_rack_webrick.rb +0 -130
  212. data/test/spec_rackup.rb +0 -164
@@ -1,557 +0,0 @@
1
- require 'test/spec'
2
-
3
- require 'rack/utils'
4
- require 'rack/lint'
5
- require 'rack/mock'
6
-
7
- context "Rack::Utils" do
8
- specify "should escape correctly" do
9
- Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
10
- Rack::Utils.escape("a space").should.equal "a+space"
11
- Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
12
- should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
13
- end
14
-
15
- specify "should escape correctly for multibyte characters" do
16
- matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
17
- matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
18
- Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
19
- matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
20
- matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
21
- Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
22
- end
23
-
24
- specify "should unescape correctly" do
25
- Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
26
- Rack::Utils.unescape("a+space").should.equal "a space"
27
- Rack::Utils.unescape("a%20space").should.equal "a space"
28
- Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C").
29
- should.equal "q1!2\"'w$5&7/z8)?\\"
30
- end
31
-
32
- specify "should parse query strings correctly" do
33
- Rack::Utils.parse_query("foo=bar").
34
- should.equal "foo" => "bar"
35
- Rack::Utils.parse_query("foo=\"bar\"").
36
- should.equal "foo" => "\"bar\""
37
- Rack::Utils.parse_query("foo=bar&foo=quux").
38
- should.equal "foo" => ["bar", "quux"]
39
- Rack::Utils.parse_query("foo=1&bar=2").
40
- should.equal "foo" => "1", "bar" => "2"
41
- Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
42
- should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
43
- Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
44
- end
45
-
46
- specify "should parse nested query strings correctly" do
47
- Rack::Utils.parse_nested_query("foo").
48
- should.equal "foo" => nil
49
- Rack::Utils.parse_nested_query("foo=").
50
- should.equal "foo" => ""
51
- Rack::Utils.parse_nested_query("foo=bar").
52
- should.equal "foo" => "bar"
53
- Rack::Utils.parse_nested_query("foo=\"bar\"").
54
- should.equal "foo" => "\"bar\""
55
-
56
- Rack::Utils.parse_nested_query("foo=bar&foo=quux").
57
- should.equal "foo" => "quux"
58
- Rack::Utils.parse_nested_query("foo&foo=").
59
- should.equal "foo" => ""
60
- Rack::Utils.parse_nested_query("foo=1&bar=2").
61
- should.equal "foo" => "1", "bar" => "2"
62
- Rack::Utils.parse_nested_query("&foo=1&&bar=2").
63
- should.equal "foo" => "1", "bar" => "2"
64
- Rack::Utils.parse_nested_query("foo&bar=").
65
- should.equal "foo" => nil, "bar" => ""
66
- Rack::Utils.parse_nested_query("foo=bar&baz=").
67
- should.equal "foo" => "bar", "baz" => ""
68
- Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
69
- should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
70
-
71
- Rack::Utils.parse_nested_query("foo[]").
72
- should.equal "foo" => [nil]
73
- Rack::Utils.parse_nested_query("foo[]=").
74
- should.equal "foo" => [""]
75
- Rack::Utils.parse_nested_query("foo[]=bar").
76
- should.equal "foo" => ["bar"]
77
-
78
- Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
79
- should.equal "foo" => ["1", "2"]
80
- Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
81
- should.equal "foo" => "bar", "baz" => ["1", "2", "3"]
82
- Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
83
- should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"]
84
-
85
- Rack::Utils.parse_nested_query("x[y][z]=1").
86
- should.equal "x" => {"y" => {"z" => "1"}}
87
- Rack::Utils.parse_nested_query("x[y][z][]=1").
88
- should.equal "x" => {"y" => {"z" => ["1"]}}
89
- Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2").
90
- should.equal "x" => {"y" => {"z" => "2"}}
91
- Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2").
92
- should.equal "x" => {"y" => {"z" => ["1", "2"]}}
93
-
94
- Rack::Utils.parse_nested_query("x[y][][z]=1").
95
- should.equal "x" => {"y" => [{"z" => "1"}]}
96
- Rack::Utils.parse_nested_query("x[y][][z][]=1").
97
- should.equal "x" => {"y" => [{"z" => ["1"]}]}
98
- Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2").
99
- should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]}
100
-
101
- Rack::Utils.parse_nested_query("x[y][][v][w]=1").
102
- should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]}
103
- Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2").
104
- should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}
105
-
106
- Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2").
107
- should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}
108
- Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
109
- should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
110
-
111
- lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
112
- should.raise(TypeError).
113
- message.should.equal "expected Hash (got String) for param `y'"
114
-
115
- lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
116
- should.raise(TypeError).
117
- message.should.equal "expected Array (got Hash) for param `x'"
118
-
119
- lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
120
- should.raise(TypeError).
121
- message.should.equal "expected Array (got String) for param `y'"
122
- end
123
-
124
- specify "should build query strings correctly" do
125
- Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
126
- Rack::Utils.build_query("foo" => ["bar", "quux"]).
127
- should.equal "foo=bar&foo=quux"
128
- Rack::Utils.build_query("foo" => "1", "bar" => "2").
129
- should.equal "foo=1&bar=2"
130
- Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?").
131
- should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
132
- end
133
-
134
- specify "should build nested query strings correctly" do
135
- Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
136
- Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
137
- Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
138
-
139
- Rack::Utils.build_nested_query("foo" => "1", "bar" => "2").
140
- should.equal "foo=1&bar=2"
141
- Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?").
142
- should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
143
-
144
- Rack::Utils.build_nested_query("foo" => [nil]).
145
- should.equal "foo[]"
146
- Rack::Utils.build_nested_query("foo" => [""]).
147
- should.equal "foo[]="
148
- Rack::Utils.build_nested_query("foo" => ["bar"]).
149
- should.equal "foo[]=bar"
150
-
151
- # The ordering of the output query string is unpredictable with 1.8's
152
- # unordered hash. Test that build_nested_query performs the inverse
153
- # function of parse_nested_query.
154
- [{"foo" => nil, "bar" => ""},
155
- {"foo" => "bar", "baz" => ""},
156
- {"foo" => ["1", "2"]},
157
- {"foo" => "bar", "baz" => ["1", "2", "3"]},
158
- {"foo" => ["bar"], "baz" => ["1", "2", "3"]},
159
- {"foo" => ["1", "2"]},
160
- {"foo" => "bar", "baz" => ["1", "2", "3"]},
161
- {"x" => {"y" => {"z" => "1"}}},
162
- {"x" => {"y" => {"z" => ["1"]}}},
163
- {"x" => {"y" => {"z" => ["1", "2"]}}},
164
- {"x" => {"y" => [{"z" => "1"}]}},
165
- {"x" => {"y" => [{"z" => ["1"]}]}},
166
- {"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
167
- {"x" => {"y" => [{"v" => {"w" => "1"}}]}},
168
- {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
169
- {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
170
- {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
171
- ].each { |params|
172
- qs = Rack::Utils.build_nested_query(params)
173
- Rack::Utils.parse_nested_query(qs).should.equal params
174
- }
175
-
176
- lambda { Rack::Utils.build_nested_query("foo=bar") }.
177
- should.raise(ArgumentError).
178
- message.should.equal "value must be a Hash"
179
- end
180
-
181
- specify "should figure out which encodings are acceptable" do
182
- helper = lambda do |a, b|
183
- request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
184
- Rack::Utils.select_best_encoding(a, b)
185
- end
186
-
187
- helper.call(%w(), [["x", 1]]).should.equal(nil)
188
- helper.call(%w(identity), [["identity", 0.0]]).should.equal(nil)
189
- helper.call(%w(identity), [["*", 0.0]]).should.equal(nil)
190
-
191
- helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("identity")
192
-
193
- helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("compress")
194
- helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).should.equal("gzip")
195
-
196
- helper.call(%w(foo bar identity), []).should.equal("identity")
197
- helper.call(%w(foo bar identity), [["*", 1.0]]).should.equal("foo")
198
- helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).should.equal("bar")
199
-
200
- helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).should.equal("identity")
201
- helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity")
202
- end
203
-
204
- specify "should return the bytesize of String" do
205
- Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
206
- end
207
-
208
- specify "should perform constant time string comparison" do
209
- Rack::Utils.secure_compare('a', 'a').should.equal true
210
- Rack::Utils.secure_compare('a', 'b').should.equal false
211
- end
212
-
213
- specify "should return status code for integer" do
214
- Rack::Utils.status_code(200).should.equal 200
215
- end
216
-
217
- specify "should return status code for string" do
218
- Rack::Utils.status_code("200").should.equal 200
219
- end
220
-
221
- specify "should return status code for symbol" do
222
- Rack::Utils.status_code(:ok).should.equal 200
223
- end
224
- end
225
-
226
- context "Rack::Utils::HeaderHash" do
227
- specify "should retain header case" do
228
- h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
229
- h['ETag'] = 'Boo!'
230
- h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
231
- end
232
-
233
- specify "should check existence of keys case insensitively" do
234
- h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
235
- h.should.include 'content-md5'
236
- h.should.not.include 'ETag'
237
- end
238
-
239
- specify "should merge case-insensitively" do
240
- h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
241
- merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
242
- merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
243
- end
244
-
245
- specify "should overwrite case insensitively and assume the new key's case" do
246
- h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
247
- h["foo-bar"] = "bizzle"
248
- h["FOO-BAR"].should.equal "bizzle"
249
- h.length.should.equal 1
250
- h.to_hash.should.equal "foo-bar" => "bizzle"
251
- end
252
-
253
- specify "should be converted to real Hash" do
254
- h = Rack::Utils::HeaderHash.new("foo" => "bar")
255
- h.to_hash.should.be.instance_of Hash
256
- end
257
-
258
- specify "should convert Array values to Strings when converting to Hash" do
259
- h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
260
- h.to_hash.should.equal({ "foo" => "bar\nbaz" })
261
- end
262
-
263
- specify "should replace hashes correctly" do
264
- h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
265
- j = {"foo" => "bar"}
266
- h.replace(j)
267
- h["foo"].should.equal "bar"
268
- end
269
-
270
- specify "should be able to delete the given key case-sensitively" do
271
- h = Rack::Utils::HeaderHash.new("foo" => "bar")
272
- h.delete("foo")
273
- h["foo"].should.be.nil
274
- h["FOO"].should.be.nil
275
- end
276
-
277
- specify "should be able to delete the given key case-insensitively" do
278
- h = Rack::Utils::HeaderHash.new("foo" => "bar")
279
- h.delete("FOO")
280
- h["foo"].should.be.nil
281
- h["FOO"].should.be.nil
282
- end
283
-
284
- specify "should return the deleted value when #delete is called on an existing key" do
285
- h = Rack::Utils::HeaderHash.new("foo" => "bar")
286
- h.delete("Foo").should.equal("bar")
287
- end
288
-
289
- specify "should return nil when #delete is called on a non-existant key" do
290
- h = Rack::Utils::HeaderHash.new("foo" => "bar")
291
- h.delete("Hello").should.be.nil
292
- end
293
-
294
- specify "should avoid unnecessary object creation if possible" do
295
- a = Rack::Utils::HeaderHash.new("foo" => "bar")
296
- b = Rack::Utils::HeaderHash.new(a)
297
- b.object_id.should.equal(a.object_id)
298
- b.should.equal(a)
299
- end
300
-
301
- specify "should convert Array values to Strings when responding to #each" do
302
- h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
303
- h.each do |k,v|
304
- k.should.equal("foo")
305
- v.should.equal("bar\nbaz")
306
- end
307
- end
308
-
309
- end
310
-
311
- context "Rack::Utils::Context" do
312
- class ContextTest
313
- attr_reader :app
314
- def initialize app; @app=app; end
315
- def call env; context env; end
316
- def context env, app=@app; app.call(env); end
317
- end
318
- test_target1 = proc{|e| e.to_s+' world' }
319
- test_target2 = proc{|e| e.to_i+2 }
320
- test_target3 = proc{|e| nil }
321
- test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
322
- test_app = ContextTest.new test_target4
323
-
324
- specify "should set context correctly" do
325
- test_app.app.should.equal test_target4
326
- c1 = Rack::Utils::Context.new(test_app, test_target1)
327
- c1.for.should.equal test_app
328
- c1.app.should.equal test_target1
329
- c2 = Rack::Utils::Context.new(test_app, test_target2)
330
- c2.for.should.equal test_app
331
- c2.app.should.equal test_target2
332
- end
333
-
334
- specify "should alter app on recontexting" do
335
- c1 = Rack::Utils::Context.new(test_app, test_target1)
336
- c2 = c1.recontext(test_target2)
337
- c2.for.should.equal test_app
338
- c2.app.should.equal test_target2
339
- c3 = c2.recontext(test_target3)
340
- c3.for.should.equal test_app
341
- c3.app.should.equal test_target3
342
- end
343
-
344
- specify "should run different apps" do
345
- c1 = Rack::Utils::Context.new test_app, test_target1
346
- c2 = c1.recontext test_target2
347
- c3 = c2.recontext test_target3
348
- c4 = c3.recontext test_target4
349
- a4 = Rack::Lint.new c4
350
- a5 = Rack::Lint.new test_app
351
- r1 = c1.call('hello')
352
- r1.should.equal 'hello world'
353
- r2 = c2.call(2)
354
- r2.should.equal 4
355
- r3 = c3.call(:misc_symbol)
356
- r3.should.be.nil
357
- r4 = Rack::MockRequest.new(a4).get('/')
358
- r4.status.should.be 200
359
- r5 = Rack::MockRequest.new(a5).get('/')
360
- r5.status.should.be 200
361
- r4.body.should.equal r5.body
362
- end
363
- end
364
-
365
- context "Rack::Utils::Multipart" do
366
- specify "should return nil if content type is not multipart" do
367
- env = Rack::MockRequest.env_for("/",
368
- "CONTENT_TYPE" => 'application/x-www-form-urlencoded')
369
- Rack::Utils::Multipart.parse_multipart(env).should.equal nil
370
- end
371
-
372
- specify "should parse multipart upload with text file" do
373
- env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
374
- params = Rack::Utils::Multipart.parse_multipart(env)
375
- params["submit-name"].should.equal "Larry"
376
- params["files"][:type].should.equal "text/plain"
377
- params["files"][:filename].should.equal "file1.txt"
378
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
379
- "name=\"files\"; filename=\"file1.txt\"\r\n" +
380
- "Content-Type: text/plain\r\n"
381
- params["files"][:name].should.equal "files"
382
- params["files"][:tempfile].read.should.equal "contents"
383
- end
384
-
385
- specify "should parse multipart upload with nested parameters" do
386
- env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
387
- params = Rack::Utils::Multipart.parse_multipart(env)
388
- params["foo"]["submit-name"].should.equal "Larry"
389
- params["foo"]["files"][:type].should.equal "text/plain"
390
- params["foo"]["files"][:filename].should.equal "file1.txt"
391
- params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " +
392
- "name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
393
- "Content-Type: text/plain\r\n"
394
- params["foo"]["files"][:name].should.equal "foo[files]"
395
- params["foo"]["files"][:tempfile].read.should.equal "contents"
396
- end
397
-
398
- specify "should parse multipart upload with binary file" do
399
- env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
400
- params = Rack::Utils::Multipart.parse_multipart(env)
401
- params["submit-name"].should.equal "Larry"
402
- params["files"][:type].should.equal "image/png"
403
- params["files"][:filename].should.equal "rack-logo.png"
404
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
405
- "name=\"files\"; filename=\"rack-logo.png\"\r\n" +
406
- "Content-Type: image/png\r\n"
407
- params["files"][:name].should.equal "files"
408
- params["files"][:tempfile].read.length.should.equal 26473
409
- end
410
-
411
- specify "should parse multipart upload with empty file" do
412
- env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
413
- params = Rack::Utils::Multipart.parse_multipart(env)
414
- params["submit-name"].should.equal "Larry"
415
- params["files"][:type].should.equal "text/plain"
416
- params["files"][:filename].should.equal "file1.txt"
417
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
418
- "name=\"files\"; filename=\"file1.txt\"\r\n" +
419
- "Content-Type: text/plain\r\n"
420
- params["files"][:name].should.equal "files"
421
- params["files"][:tempfile].read.should.equal ""
422
- end
423
-
424
- specify "should parse multipart upload with filename with semicolons" do
425
- env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
426
- params = Rack::Utils::Multipart.parse_multipart(env)
427
- params["files"][:type].should.equal "text/plain"
428
- params["files"][:filename].should.equal "fi;le1.txt"
429
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
430
- "name=\"files\"; filename=\"fi;le1.txt\"\r\n" +
431
- "Content-Type: text/plain\r\n"
432
- params["files"][:name].should.equal "files"
433
- params["files"][:tempfile].read.should.equal "contents"
434
- end
435
-
436
- specify "should not include file params if no file was selected" do
437
- env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
438
- params = Rack::Utils::Multipart.parse_multipart(env)
439
- params["submit-name"].should.equal "Larry"
440
- params["files"].should.equal nil
441
- params.keys.should.not.include "files"
442
- end
443
-
444
- specify "should parse IE multipart upload and clean up filename" do
445
- env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
446
- params = Rack::Utils::Multipart.parse_multipart(env)
447
- params["files"][:type].should.equal "text/plain"
448
- params["files"][:filename].should.equal "file1.txt"
449
- params["files"][:head].should.equal "Content-Disposition: form-data; " +
450
- "name=\"files\"; " +
451
- 'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
452
- "\r\nContent-Type: text/plain\r\n"
453
- params["files"][:name].should.equal "files"
454
- params["files"][:tempfile].read.should.equal "contents"
455
- end
456
-
457
- specify "rewinds input after parsing upload" do
458
- options = multipart_fixture(:text)
459
- input = options[:input]
460
- env = Rack::MockRequest.env_for("/", options)
461
- params = Rack::Utils::Multipart.parse_multipart(env)
462
- params["submit-name"].should.equal "Larry"
463
- params["files"][:filename].should.equal "file1.txt"
464
- input.read.length.should.equal 197
465
- end
466
-
467
- specify "builds multipart body" do
468
- files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
469
- data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
470
-
471
- options = {
472
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
473
- "CONTENT_LENGTH" => data.length.to_s,
474
- :input => StringIO.new(data)
475
- }
476
- env = Rack::MockRequest.env_for("/", options)
477
- params = Rack::Utils::Multipart.parse_multipart(env)
478
- params["submit-name"].should.equal "Larry"
479
- params["files"][:filename].should.equal "file1.txt"
480
- params["files"][:tempfile].read.should.equal "contents"
481
- end
482
-
483
- specify "builds nested multipart body" do
484
- files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
485
- data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
486
-
487
- options = {
488
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
489
- "CONTENT_LENGTH" => data.length.to_s,
490
- :input => StringIO.new(data)
491
- }
492
- env = Rack::MockRequest.env_for("/", options)
493
- params = Rack::Utils::Multipart.parse_multipart(env)
494
- params["people"][0]["submit-name"].should.equal "Larry"
495
- params["people"][0]["files"][:filename].should.equal "file1.txt"
496
- params["people"][0]["files"][:tempfile].read.should.equal "contents"
497
- end
498
-
499
- specify "can parse fields that end at the end of the buffer" do
500
- input = File.read(multipart_file("bad_robots"))
501
-
502
- req = Rack::Request.new Rack::MockRequest.env_for("/",
503
- "CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
504
- "CONTENT_LENGTH" => input.size,
505
- :input => input)
506
-
507
- req.POST['file.path'].should.equal "/var/tmp/uploads/4/0001728414"
508
- req.POST['addresses'].should.not.equal nil
509
- end
510
-
511
- specify "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
512
- data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n")
513
- options = {
514
- "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
515
- "CONTENT_LENGTH" => data.length.to_s,
516
- :input => StringIO.new(data)
517
- }
518
- env = Rack::MockRequest.env_for("/", options)
519
- params = Rack::Utils::Multipart.parse_multipart(env)
520
-
521
- params.should.not.equal nil
522
- params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
523
- params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
524
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
525
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
526
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
527
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
528
- end
529
-
530
- specify "should return nil if no UploadedFiles were used" do
531
- data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
532
- data.should.equal nil
533
- end
534
-
535
- specify "should raise ArgumentError if params is not a Hash" do
536
- lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
537
- should.raise(ArgumentError).
538
- message.should.equal "value must be a Hash"
539
- end
540
-
541
- private
542
- def multipart_fixture(name)
543
- file = multipart_file(name)
544
- data = File.open(file, 'rb') { |io| io.read }
545
-
546
- type = "multipart/form-data; boundary=AaB03x"
547
- length = data.respond_to?(:bytesize) ? data.bytesize : data.size
548
-
549
- { "CONTENT_TYPE" => type,
550
- "CONTENT_LENGTH" => length.to_s,
551
- :input => StringIO.new(data) }
552
- end
553
-
554
- def multipart_file(name)
555
- File.join(File.dirname(__FILE__), "multipart", name.to_s)
556
- end
557
- end