0a61bce7075c21630baffe2d66fdad45e2ee0b8b
[feedcatcher.git] / vendor / rails / actionpack / lib / action_controller / vendor / rack-1.0 / rack / utils.rb
1 require 'set'
2 require 'tempfile'
3
4 module Rack
5 # Rack::Utils contains a grab-bag of useful methods for writing web
6 # applications adopted from all kinds of Ruby libraries.
7
8 module Utils
9 # Performs URI escaping so that you can construct proper
10 # query strings faster. Use this rather than the cgi.rb
11 # version since it's faster. (Stolen from Camping).
12 def escape(s)
13 s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
14 '%'+$1.unpack('H2'*$1.size).join('%').upcase
15 }.tr(' ', '+')
16 end
17 module_function :escape
18
19 # Unescapes a URI escaped string. (Stolen from Camping).
20 def unescape(s)
21 s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
22 [$1.delete('%')].pack('H*')
23 }
24 end
25 module_function :unescape
26
27 # Stolen from Mongrel, with some small modifications:
28 # Parses a query string by breaking it up at the '&'
29 # and ';' characters. You can also use this to parse
30 # cookies by changing the characters used in the second
31 # parameter (which defaults to '&;').
32 def parse_query(qs, d = '&;')
33 params = {}
34
35 (qs || '').split(/[#{d}] */n).each do |p|
36 k, v = unescape(p).split('=', 2)
37
38 if cur = params[k]
39 if cur.class == Array
40 params[k] << v
41 else
42 params[k] = [cur, v]
43 end
44 else
45 params[k] = v
46 end
47 end
48
49 return params
50 end
51 module_function :parse_query
52
53 def parse_nested_query(qs, d = '&;')
54 params = {}
55
56 (qs || '').split(/[#{d}] */n).each do |p|
57 k, v = unescape(p).split('=', 2)
58 normalize_params(params, k, v)
59 end
60
61 return params
62 end
63 module_function :parse_nested_query
64
65 def normalize_params(params, name, v = nil)
66 name =~ %r([\[\]]*([^\[\]]+)\]*)
67 k = $1 || ''
68 after = $' || ''
69
70 return if k.empty?
71
72 if after == ""
73 params[k] = v
74 elsif after == "[]"
75 params[k] ||= []
76 raise TypeError unless params[k].is_a?(Array)
77 params[k] << v
78 elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
79 child_key = $1
80 params[k] ||= []
81 raise TypeError unless params[k].is_a?(Array)
82 if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
83 normalize_params(params[k].last, child_key, v)
84 else
85 params[k] << normalize_params({}, child_key, v)
86 end
87 else
88 params[k] ||= {}
89 params[k] = normalize_params(params[k], after, v)
90 end
91
92 return params
93 end
94 module_function :normalize_params
95
96 def build_query(params)
97 params.map { |k, v|
98 if v.class == Array
99 build_query(v.map { |x| [k, x] })
100 else
101 escape(k) + "=" + escape(v)
102 end
103 }.join("&")
104 end
105 module_function :build_query
106
107 # Escape ampersands, brackets and quotes to their HTML/XML entities.
108 def escape_html(string)
109 string.to_s.gsub("&", "&amp;").
110 gsub("<", "&lt;").
111 gsub(">", "&gt;").
112 gsub("'", "&#39;").
113 gsub('"', "&quot;")
114 end
115 module_function :escape_html
116
117 def select_best_encoding(available_encodings, accept_encoding)
118 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
119
120 expanded_accept_encoding =
121 accept_encoding.map { |m, q|
122 if m == "*"
123 (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
124 else
125 [[m, q]]
126 end
127 }.inject([]) { |mem, list|
128 mem + list
129 }
130
131 encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
132
133 unless encoding_candidates.include?("identity")
134 encoding_candidates.push("identity")
135 end
136
137 expanded_accept_encoding.find_all { |m, q|
138 q == 0.0
139 }.each { |m, _|
140 encoding_candidates.delete(m)
141 }
142
143 return (encoding_candidates & available_encodings)[0]
144 end
145 module_function :select_best_encoding
146
147 # Return the bytesize of String; uses String#length under Ruby 1.8 and
148 # String#bytesize under 1.9.
149 if ''.respond_to?(:bytesize)
150 def bytesize(string)
151 string.bytesize
152 end
153 else
154 def bytesize(string)
155 string.size
156 end
157 end
158 module_function :bytesize
159
160 # Context allows the use of a compatible middleware at different points
161 # in a request handling stack. A compatible middleware must define
162 # #context which should take the arguments env and app. The first of which
163 # would be the request environment. The second of which would be the rack
164 # application that the request would be forwarded to.
165 class Context
166 attr_reader :for, :app
167
168 def initialize(app_f, app_r)
169 raise 'running context does not respond to #context' unless app_f.respond_to? :context
170 @for, @app = app_f, app_r
171 end
172
173 def call(env)
174 @for.context(env, @app)
175 end
176
177 def recontext(app)
178 self.class.new(@for, app)
179 end
180
181 def context(env, app=@app)
182 recontext(app).call(env)
183 end
184 end
185
186 # A case-insensitive Hash that preserves the original case of a
187 # header when set.
188 class HeaderHash < Hash
189 def initialize(hash={})
190 @names = {}
191 hash.each { |k, v| self[k] = v }
192 end
193
194 def to_hash
195 inject({}) do |hash, (k,v)|
196 if v.respond_to? :to_ary
197 hash[k] = v.to_ary.join("\n")
198 else
199 hash[k] = v
200 end
201 hash
202 end
203 end
204
205 def [](k)
206 super @names[k.downcase]
207 end
208
209 def []=(k, v)
210 delete k
211 @names[k.downcase] = k
212 super k, v
213 end
214
215 def delete(k)
216 super @names.delete(k.downcase)
217 end
218
219 def include?(k)
220 @names.has_key? k.downcase
221 end
222
223 alias_method :has_key?, :include?
224 alias_method :member?, :include?
225 alias_method :key?, :include?
226
227 def merge!(other)
228 other.each { |k, v| self[k] = v }
229 self
230 end
231
232 def merge(other)
233 hash = dup
234 hash.merge! other
235 end
236 end
237
238 # Every standard HTTP code mapped to the appropriate message.
239 # Stolen from Mongrel.
240 HTTP_STATUS_CODES = {
241 100 => 'Continue',
242 101 => 'Switching Protocols',
243 200 => 'OK',
244 201 => 'Created',
245 202 => 'Accepted',
246 203 => 'Non-Authoritative Information',
247 204 => 'No Content',
248 205 => 'Reset Content',
249 206 => 'Partial Content',
250 300 => 'Multiple Choices',
251 301 => 'Moved Permanently',
252 302 => 'Found',
253 303 => 'See Other',
254 304 => 'Not Modified',
255 305 => 'Use Proxy',
256 307 => 'Temporary Redirect',
257 400 => 'Bad Request',
258 401 => 'Unauthorized',
259 402 => 'Payment Required',
260 403 => 'Forbidden',
261 404 => 'Not Found',
262 405 => 'Method Not Allowed',
263 406 => 'Not Acceptable',
264 407 => 'Proxy Authentication Required',
265 408 => 'Request Timeout',
266 409 => 'Conflict',
267 410 => 'Gone',
268 411 => 'Length Required',
269 412 => 'Precondition Failed',
270 413 => 'Request Entity Too Large',
271 414 => 'Request-URI Too Large',
272 415 => 'Unsupported Media Type',
273 416 => 'Requested Range Not Satisfiable',
274 417 => 'Expectation Failed',
275 500 => 'Internal Server Error',
276 501 => 'Not Implemented',
277 502 => 'Bad Gateway',
278 503 => 'Service Unavailable',
279 504 => 'Gateway Timeout',
280 505 => 'HTTP Version Not Supported'
281 }
282
283 # Responses with HTTP status codes that should not have an entity body
284 STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
285
286 # A multipart form data parser, adapted from IOWA.
287 #
288 # Usually, Rack::Request#POST takes care of calling this.
289
290 module Multipart
291 EOL = "\r\n"
292
293 def self.parse_multipart(env)
294 unless env['CONTENT_TYPE'] =~
295 %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
296 nil
297 else
298 boundary = "--#{$1}"
299
300 params = {}
301 buf = ""
302 content_length = env['CONTENT_LENGTH'].to_i
303 input = env['rack.input']
304
305 boundary_size = boundary.size + EOL.size
306 bufsize = 16384
307
308 content_length -= boundary_size
309
310 status = input.read(boundary_size)
311 raise EOFError, "bad content body" unless status == boundary + EOL
312
313 rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
314
315 loop {
316 head = nil
317 body = ''
318 filename = content_type = name = nil
319
320 until head && buf =~ rx
321 if !head && i = buf.index("\r\n\r\n")
322 head = buf.slice!(0, i+2) # First \r\n
323 buf.slice!(0, 2) # Second \r\n
324
325 filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
326 content_type = head[/Content-Type: (.*)\r\n/ni, 1]
327 name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
328
329 if filename
330 body = Tempfile.new("RackMultipart")
331 body.binmode if body.respond_to?(:binmode)
332 end
333
334 next
335 end
336
337 # Save the read body part.
338 if head && (boundary_size+4 < buf.size)
339 body << buf.slice!(0, buf.size - (boundary_size+4))
340 end
341
342 c = input.read(bufsize < content_length ? bufsize : content_length)
343 raise EOFError, "bad content body" if c.nil? || c.empty?
344 buf << c
345 content_length -= c.size
346 end
347
348 # Save the rest.
349 if i = buf.index(rx)
350 body << buf.slice!(0, i)
351 buf.slice!(0, boundary_size+2)
352
353 content_length = -1 if $1 == "--"
354 end
355
356 if filename == ""
357 # filename is blank which means no file has been selected
358 data = nil
359 elsif filename
360 body.rewind
361
362 # Take the basename of the upload's original filename.
363 # This handles the full Windows paths given by Internet Explorer
364 # (and perhaps other broken user agents) without affecting
365 # those which give the lone filename.
366 filename =~ /^(?:.*[:\\\/])?(.*)/m
367 filename = $1
368
369 data = {:filename => filename, :type => content_type,
370 :name => name, :tempfile => body, :head => head}
371 else
372 data = body
373 end
374
375 Utils.normalize_params(params, name, data) unless data.nil?
376
377 break if buf.empty? || content_length == -1
378 }
379
380 begin
381 input.rewind if input.respond_to?(:rewind)
382 rescue Errno::ESPIPE
383 # Handles exceptions raised by input streams that cannot be rewound
384 # such as when using plain CGI under Apache
385 end
386
387 params
388 end
389 end
390 end
391 end
392 end