Skip to content
This repository
Newer
Older
100644 652 lines (557 sloc) 19.371 kb
50713029 » raggi
2009-04-29 Switch to emacs style encoding comment
1 # -*- encoding: binary -*-
44ed4640 » raggi
2009-04-29 Fix multipart uploads on 1.9
2
a6320b93 » chneukirchen
2010-02-07 rack/utils needs FileUtils
3 require 'fileutils'
ea814ff1 » rtomayko
2008-12-23 use Set instead of Array for STATUS_WITH_NO_ENTITY_BODY
4 require 'set'
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
5 require 'tempfile'
6
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
7 module Rack
376fa1e3 » chneukirchen
2007-03-01 Add RDocs
8 # Rack::Utils contains a grab-bag of useful methods for writing web
9 # applications adopted from all kinds of Ruby libraries.
230d62c7 » chneukirchen
2007-05-16 Fix trailing whitespace. Sigh.
10
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
11 module Utils
12 # Performs URI escaping so that you can construct proper
13 # query strings faster. Use this rather than the cgi.rb
14 # version since it's faster. (Stolen from Camping).
15 def escape(s)
16 s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
ce26ede5 » takahashim
2009-06-25 use Rack::Utils::bytesize, not String#size for multibyte characters i…
17 '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
18 }.tr(' ', '+')
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
19 end
20 module_function :escape
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
21
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
22 # Unescapes a URI escaped string. (Stolen from Camping).
23 def unescape(s)
24 s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
25 [$1.delete('%')].pack('H*')
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
26 }
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
27 end
28 module_function :unescape
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
29
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
30 DEFAULT_SEP = /[&;] */n
5c4bd17d » josh
2009-12-10 Reverse hash for looking up status codes by symbol
31
3adf1af2 » qerub
2008-05-02 utils.rb - Cleaned up parse_query
32 # Stolen from Mongrel, with some small modifications:
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
33 # Parses a query string by breaking it up at the '&'
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
34 # and ';' characters. You can also use this to parse
35 # cookies by changing the characters used in the second
c5b420a5 » chneukirchen
2007-05-16 Small docfixes
36 # parameter (which defaults to '&;').
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
37 def parse_query(qs, d = nil)
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
38 params = {}
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
39
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
40 (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
7a55c98a » tlrobinson
2009-05-22 Fix for form names containing "=": split first then unescape components
41 k, v = p.split('=', 2).map { |x| unescape(x) }
fb4f2b5f » scytrin
2009-12-02 Test added to check to ensure that quoted values are properly parsed
42 if v =~ /^("|')(.*)\1$/
43 v = $2.gsub('\\'+$1, $1)
44 end
5c00dd69 » josh
2009-02-14 Split parse_query utility into parse_query and parse_nested_query. pa…
45 if cur = params[k]
46 if cur.class == Array
47 params[k] << v
48 else
49 params[k] = [cur, v]
50 end
51 else
52 params[k] = v
53 end
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
54 end
55
56 return params
57 end
58 module_function :parse_query
59
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
60 def parse_nested_query(qs, d = nil)
5c00dd69 » josh
2009-02-14 Split parse_query utility into parse_query and parse_nested_query. pa…
61 params = {}
62
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
63 (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
5c00dd69 » josh
2009-02-14 Split parse_query utility into parse_query and parse_nested_query. pa…
64 k, v = unescape(p).split('=', 2)
65 normalize_params(params, k, v)
66 end
67
68 return params
69 end
70 module_function :parse_nested_query
71
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
72 def normalize_params(params, name, v = nil)
fb4f2b5f » scytrin
2009-12-02 Test added to check to ensure that quoted values are properly parsed
73 if v and v =~ /^("|')(.*)\1$/
74 v = $2.gsub('\\'+$1, $1)
75 end
40eba67d » josh
2009-04-28 Add anchor to nested params parsing regexp to prevent stack overflows
76 name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
77 k = $1 || ''
78 after = $' || ''
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
79
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
80 return if k.empty?
81
82 if after == ""
5c00dd69 » josh
2009-02-14 Split parse_query utility into parse_query and parse_nested_query. pa…
83 params[k] = v
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
84 elsif after == "[]"
85 params[k] ||= []
880d9be0 » rob-at-thewebfellas
2009-03-18 Added extra nested params error handling and added messages to except…
86 raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
87 params[k] << v
88 elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
89 child_key = $1
90 params[k] ||= []
880d9be0 » rob-at-thewebfellas
2009-03-18 Added extra nested params error handling and added messages to except…
91 raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
92 if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
93 normalize_params(params[k].last, child_key, v)
94 else
95 params[k] << normalize_params({}, child_key, v)
96 end
97 else
98 params[k] ||= {}
880d9be0 » rob-at-thewebfellas
2009-03-18 Added extra nested params error handling and added messages to except…
99 raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
100 params[k] = normalize_params(params[k], after, v)
3adf1af2 » qerub
2008-05-02 utils.rb - Cleaned up parse_query
101 end
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
102
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
103 return params
104 end
6e330bdd » josh
2009-01-31 Add support for nested parameter parsing
105 module_function :normalize_params
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
106
31f8a934 » qerub
2008-05-02 utils.rb, spec_rack_utils.rb - Added build_query, the inverse of pars…
107 def build_query(params)
108 params.map { |k, v|
109 if v.class == Array
110 build_query(v.map { |x| [k, x] })
111 else
1189b1e7 » thinkerbot
2009-05-15 optimizations of parse/build query
112 "#{escape(k)}=#{escape(v)}"
31f8a934 » qerub
2008-05-02 utils.rb, spec_rack_utils.rb - Added build_query, the inverse of pars…
113 end
114 }.join("&")
115 end
116 module_function :build_query
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
117
c4eb1ae8 » josh
2009-04-25 Add Utils.build_nested_query to complement Utils.parse_nested_query a…
118 def build_nested_query(value, prefix = nil)
119 case value
120 when Array
121 value.map { |v|
122 build_nested_query(v, "#{prefix}[]")
123 }.join("&")
124 when Hash
125 value.map { |k, v|
126 build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
127 }.join("&")
128 when String
129 raise ArgumentError, "value must be a Hash" if prefix.nil?
130 "#{prefix}=#{escape(value)}"
131 else
132 prefix
133 end
134 end
135 module_function :build_nested_query
136
376fa1e3 » chneukirchen
2007-03-01 Add RDocs
137 # Escape ampersands, brackets and quotes to their HTML/XML entities.
69f11f60 » chneukirchen
2007-02-17 Add Rack::ShowExceptions
138 def escape_html(string)
139 string.to_s.gsub("&", "&amp;").
140 gsub("<", "&lt;").
141 gsub(">", "&gt;").
142 gsub("'", "&#39;").
143 gsub('"', "&quot;")
144 end
145 module_function :escape_html
146
0f2dab57 » qerub
2008-07-01 Added support for Accept-Encoding (via Request#accept_encoding and Ut…
147 def select_best_encoding(available_encodings, accept_encoding)
148 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
149
150 expanded_accept_encoding =
151 accept_encoding.map { |m, q|
152 if m == "*"
153 (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
154 else
155 [[m, q]]
156 end
157 }.inject([]) { |mem, list|
158 mem + list
159 }
160
161 encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
162
163 unless encoding_candidates.include?("identity")
164 encoding_candidates.push("identity")
165 end
166
167 expanded_accept_encoding.find_all { |m, q|
168 q == 0.0
169 }.each { |m, _|
170 encoding_candidates.delete(m)
171 }
172
173 return (encoding_candidates & available_encodings)[0]
174 end
175 module_function :select_best_encoding
176
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
177 def set_cookie_header!(header, key, value)
178 case value
179 when Hash
180 domain = "; domain=" + value[:domain] if value[:domain]
181 path = "; path=" + value[:path] if value[:path]
182 # According to RFC 2109, we need dashes here.
183 # N.B.: cgi.rb uses spaces...
184 expires = "; expires=" + value[:expires].clone.gmtime.
185 strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
186 secure = "; secure" if value[:secure]
187 httponly = "; HttpOnly" if value[:httponly]
188 value = value[:value]
189 end
190 value = [value] unless Array === value
191 cookie = escape(key) + "=" +
192 value.map { |v| escape v }.join("&") +
193 "#{domain}#{path}#{expires}#{secure}#{httponly}"
194
195 case header["Set-Cookie"]
c028a23b » rtomayko
2010-03-09 cookie utility methods use multiline strings instead of arrays
196 when nil, ''
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
197 header["Set-Cookie"] = cookie
c028a23b » rtomayko
2010-03-09 cookie utility methods use multiline strings instead of arrays
198 when String
199 header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
200 when Array
201 header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
202 end
203
204 nil
205 end
206 module_function :set_cookie_header!
207
208 def delete_cookie_header!(header, key, value = {})
c028a23b » rtomayko
2010-03-09 cookie utility methods use multiline strings instead of arrays
209 case header["Set-Cookie"]
210 when nil, ''
211 cookies = []
212 when String
213 cookies = header["Set-Cookie"].split("\n")
214 when Array
215 cookies = header["Set-Cookie"]
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
216 end
217
c028a23b » rtomayko
2010-03-09 cookie utility methods use multiline strings instead of arrays
218 cookies.reject! { |cookie|
55cbbc91 » zbrock
2010-01-07 allow delete of cookies with same name but different domain
219 if value[:domain]
220 cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
221 else
222 cookie =~ /\A#{escape(key)}=/
223 end
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
224 }
225
c028a23b » rtomayko
2010-03-09 cookie utility methods use multiline strings instead of arrays
226 header["Set-Cookie"] = cookies.join("\n")
227
39fec318 » josh
2009-08-03 Don't buffer response bodies in session store by creating an unnecess…
228 set_cookie_header!(header, key,
229 {:value => '', :path => nil, :domain => nil,
230 :expires => Time.at(0) }.merge(value))
231
232 nil
233 end
234 module_function :delete_cookie_header!
235
980d6816 » rtomayko
2009-03-05 Add Rack::Utils.bytesize function, use everywhere
236 # Return the bytesize of String; uses String#length under Ruby 1.8 and
237 # String#bytesize under 1.9.
238 if ''.respond_to?(:bytesize)
239 def bytesize(string)
240 string.bytesize
241 end
242 else
243 def bytesize(string)
244 string.size
245 end
246 end
247 module_function :bytesize
248
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
249 # Context allows the use of a compatible middleware at different points
250 # in a request handling stack. A compatible middleware must define
251 # #context which should take the arguments env and app. The first of which
252 # would be the request environment. The second of which would be the rack
253 # application that the request would be forwarded to.
254 class Context
ea7f9f7b » scytrin
2008-03-16 utils.rb - Utils::Context - addition of introspection methods
255 attr_reader :for, :app
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
256
257 def initialize(app_f, app_r)
19900575 » scytrin
2008-06-06 Added documentation, checks, and tests for Rack::Utils::Context
258 raise 'running context does not respond to #context' unless app_f.respond_to? :context
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
259 @for, @app = app_f, app_r
14fdd96f » scytrin
2007-11-18 Addition of Rack::Utils::Context
260 end
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
261
262 def call(env)
263 @for.context(env, @app)
14fdd96f » scytrin
2007-11-18 Addition of Rack::Utils::Context
264 end
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
265
266 def recontext(app)
267 self.class.new(@for, app)
ba05ad9e » scytrin
2008-03-29 utils.rb - addition of recontexting from a Context
268 end
8eac3203 » scytrin
2009-01-06 Simplification of Rack::Utils::Context
269
270 def context(env, app=@app)
271 recontext(app).call(env)
14fdd96f » scytrin
2007-11-18 Addition of Rack::Utils::Context
272 end
273 end
274
76c3aefa » rtomayko
2008-12-29 Use HeaderHash where header case should be insensitive
275 # A case-insensitive Hash that preserves the original case of a
276 # header when set.
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
277 class HeaderHash < Hash
cdf13618 » Eric Wong
2009-12-10 HeaderHash.new avoids unnecessary object creation
278 def self.new(hash={})
279 HeaderHash === hash ? hash : super(hash)
280 end
281
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
282 def initialize(hash={})
07c18145 » FooBarWidget
2009-06-17 Fix Rack::Utils::HeaderHash#delete: it's supposed to return the delet…
283 super()
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
284 @names = {}
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
285 hash.each { |k, v| self[k] = v }
286 end
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
287
0f1bd252 » Eric Wong
2009-12-10 HeaderHash#each yields Lint-OK multivalue headers
288 def each
289 super do |k, v|
290 yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
291 end
292 end
293
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
294 def to_hash
bebd5893 » rtomayko
2009-02-01 Normalize Array header values to Strings in HeaderHash#to_hash
295 inject({}) do |hash, (k,v)|
296 if v.respond_to? :to_ary
297 hash[k] = v.to_ary.join("\n")
298 else
299 hash[k] = v
300 end
301 hash
302 end
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
303 end
304
305 def [](k)
f6f3c609 » Alex Beregszaszi
2010-02-08 Don't set header to nil on HeaderHash#include?
306 super(@names[k]) if @names[k]
307 super(@names[k.downcase])
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
308 end
116f0ef5 » chneukirchen
2007-02-17 Remove trailing whitespace *sigh*
309
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
310 def []=(k, v)
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
311 delete k
0bc56622 » jeremy
2009-05-15 Speed up common header reads by avoiding downcase
312 @names[k] = @names[k.downcase] = k
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
313 super k, v
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
314 end
315
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
316 def delete(k)
0bc56622 » jeremy
2009-05-15 Speed up common header reads by avoiding downcase
317 canonical = k.downcase
07c18145 » FooBarWidget
2009-06-17 Fix Rack::Utils::HeaderHash#delete: it's supposed to return the delet…
318 result = super @names.delete(canonical)
0bc56622 » jeremy
2009-05-15 Speed up common header reads by avoiding downcase
319 @names.delete_if { |name,| name.downcase == canonical }
07c18145 » FooBarWidget
2009-06-17 Fix Rack::Utils::HeaderHash#delete: it's supposed to return the delet…
320 result
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
321 end
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
322
323 def include?(k)
0bc56622 » jeremy
2009-05-15 Speed up common header reads by avoiding downcase
324 @names.include?(k) || @names.include?(k.downcase)
4e174436 » rtomayko
2008-12-11 Non-normalizing HeaderHash with case-insensitive lookups
325 end
326
327 alias_method :has_key?, :include?
328 alias_method :member?, :include?
329 alias_method :key?, :include?
c00b3862 » rtomayko
2008-12-29 Implement HeaderHash#merge! and HeaderHash#merge
330
331 def merge!(other)
332 other.each { |k, v| self[k] = v }
333 self
334 end
335
336 def merge(other)
337 hash = dup
338 hash.merge! other
339 end
e0322ed0 » kamal
2009-06-15 Add Rack::Utils::HeaderHash#replace
340
341 def replace(other)
342 clear
343 other.each { |k, v| self[k] = v }
344 self
345 end
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
346 end
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
347
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
348 # Every standard HTTP code mapped to the appropriate message.
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
349 # Generated with:
350 # curl -s http://www.iana.org/assignments/http-status-codes | \
351 # ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and
352 # puts " #{m[1]} => \x27#{m[2].strip}x27,"'
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
353 HTTP_STATUS_CODES = {
354 100 => 'Continue',
355 101 => 'Switching Protocols',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
356 102 => 'Processing',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
357 200 => 'OK',
358 201 => 'Created',
359 202 => 'Accepted',
360 203 => 'Non-Authoritative Information',
361 204 => 'No Content',
362 205 => 'Reset Content',
363 206 => 'Partial Content',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
364 207 => 'Multi-Status',
365 226 => 'IM Used',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
366 300 => 'Multiple Choices',
367 301 => 'Moved Permanently',
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
368 302 => 'Found',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
369 303 => 'See Other',
370 304 => 'Not Modified',
371 305 => 'Use Proxy',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
372 306 => 'Reserved',
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
373 307 => 'Temporary Redirect',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
374 400 => 'Bad Request',
375 401 => 'Unauthorized',
376 402 => 'Payment Required',
377 403 => 'Forbidden',
378 404 => 'Not Found',
379 405 => 'Method Not Allowed',
380 406 => 'Not Acceptable',
381 407 => 'Proxy Authentication Required',
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
382 408 => 'Request Timeout',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
383 409 => 'Conflict',
384 410 => 'Gone',
385 411 => 'Length Required',
386 412 => 'Precondition Failed',
387 413 => 'Request Entity Too Large',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
388 414 => 'Request-URI Too Long',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
389 415 => 'Unsupported Media Type',
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
390 416 => 'Requested Range Not Satisfiable',
391 417 => 'Expectation Failed',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
392 422 => 'Unprocessable Entity',
393 423 => 'Locked',
394 424 => 'Failed Dependency',
395 426 => 'Upgrade Required',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
396 500 => 'Internal Server Error',
397 501 => 'Not Implemented',
398 502 => 'Bad Gateway',
399 503 => 'Service Unavailable',
488194ed » josh
2008-12-02 Correct status code language to follow RFC 2616
400 504 => 'Gateway Timeout',
ca6f9f9f » Eric Wong
2009-08-06 Update Rack::Utils::HTTP_STATUS_CODES hash
401 505 => 'HTTP Version Not Supported',
402 506 => 'Variant Also Negotiates',
403 507 => 'Insufficient Storage',
404 510 => 'Not Extended',
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
405 }
406
77725c7d » Dan Kubb
2008-12-20 Moved STATUS_WITH_NO_ENTITY_BODY into Rack::Utils
407 # Responses with HTTP status codes that should not have an entity body
ea814ff1 » rtomayko
2008-12-23 use Set instead of Array for STATUS_WITH_NO_ENTITY_BODY
408 STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
77725c7d » Dan Kubb
2008-12-20 Moved STATUS_WITH_NO_ENTITY_BODY into Rack::Utils
409
5c4bd17d » josh
2009-12-10 Reverse hash for looking up status codes by symbol
410 SYMBOL_TO_STATUS_CODE = HTTP_STATUS_CODES.inject({}) { |hash, (code, message)|
411 hash[message.downcase.gsub(/\s|-/, '_').to_sym] = code
412 hash
413 }
414
1ffa95c5 » josh
2009-12-20 Status code lookup utility
415 def status_code(status)
416 if status.is_a?(Symbol)
417 SYMBOL_TO_STATUS_CODE[status] || 500
418 else
419 status.to_i
420 end
421 end
422 module_function :status_code
5c4bd17d » josh
2009-12-10 Reverse hash for looking up status codes by symbol
423
376fa1e3 » chneukirchen
2007-03-01 Add RDocs
424 # A multipart form data parser, adapted from IOWA.
425 #
426 # Usually, Rack::Request#POST takes care of calling this.
d110c39b » chneukirchen
2007-03-29 Add a list of HTTP status messages
427
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
428 module Multipart
6139d297 » josh
2009-04-25 Add Utils::Multipart.build_multipart to complement Utils::Multipart.p…
429 class UploadedFile
430 # The filename, *not* including the path, of the "uploaded" file
431 attr_reader :original_filename
432
433 # The content type of the "uploaded" file
434 attr_accessor :content_type
435
436 def initialize(path, content_type = "text/plain", binary = false)
437 raise "#{path} file does not exist" unless ::File.exist?(path)
438 @content_type = content_type
439 @original_filename = ::File.basename(path)
440 @tempfile = Tempfile.new(@original_filename)
441 @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
442 @tempfile.binmode if binary
443 FileUtils.copy_file(path, @tempfile.path)
444 end
445
446 def path
447 @tempfile.path
448 end
449 alias_method :local_path, :path
450
451 def method_missing(method_name, *args, &block) #:nodoc:
452 @tempfile.__send__(method_name, *args, &block)
453 end
454 end
455
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
456 EOL = "\r\n"
6139d297 » josh
2009-04-25 Add Utils::Multipart.build_multipart to complement Utils::Multipart.p…
457 MULTIPART_BOUNDARY = "AaB03x"
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
458
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
459 def self.parse_multipart(env)
71030b9d » josh
2009-08-05 Revert "Add common HTTP strings to Rack::Const"
460 unless env['CONTENT_TYPE'] =~
6674f365 » eTM
2009-04-11 better multipart handling
461 %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
462 nil
463 else
464 boundary = "--#{$1}"
465
466 params = {}
467 buf = ""
71030b9d » josh
2009-08-05 Revert "Add common HTTP strings to Rack::Const"
468 content_length = env['CONTENT_LENGTH'].to_i
469 input = env['rack.input']
95f068e8 » FooBarWidget
2009-04-14 Remove code for handling the possibility that rack.input may not be r…
470 input.rewind
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
471
44ed4640 » raggi
2009-04-29 Fix multipart uploads on 1.9
472 boundary_size = Utils.bytesize(boundary) + EOL.size
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
473 bufsize = 16384
474
475 content_length -= boundary_size
476
e6095805 » FooBarWidget
2009-04-15 Optimize parse_multipart: put read data into a buffer in order to red…
477 read_buffer = ''
478
479 status = input.read(boundary_size, read_buffer)
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
480 raise EOFError, "bad content body" unless status == boundary + EOL
481
69b24cb7 » Jonathan del Strother
2009-01-16 Fix multipart parsing of binary content in UTF8 mode
482 rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
483
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
484 loop {
485 head = nil
486 body = ''
487 filename = content_type = name = nil
488
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
489 until head && buf =~ rx
6674f365 » eTM
2009-04-11 better multipart handling
490 if !head && i = buf.index(EOL+EOL)
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
491 head = buf.slice!(0, i+2) # First \r\n
492 buf.slice!(0, 2) # Second \r\n
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
493
a36ac970 » rubys
2010-01-20 Improve multipart parsing support for UAs that don't correctly escape
494 token = /[^\s()<>,;:\\"\/\[\]?=]+/
495 condisp = /Content-Disposition:\s*#{token}\s*/i
496 dispparm = /;\s*(#{token})=("(?:\\"|[^"])*"|#{token})*/
497
498 rfc2183 = /^#{condisp}(#{dispparm})+$/i
a1806731 » rubys
2010-01-21 Fixes next token after a filename may include a dash or other character
499 broken_quoted = /^#{condisp}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{token}=)/i
5984e3eb » josh
2010-01-20 broken_unquoted should be case insensitive
500 broken_unquoted = /^#{condisp}.*;\sfilename=(#{token})/i
a36ac970 » rubys
2010-01-20 Improve multipart parsing support for UAs that don't correctly escape
501
502 if head =~ rfc2183
503 filename = Hash[head.scan(dispparm)]['filename']
504 filename = $1 if filename and filename =~ /^"(.*)"$/
505 elsif head =~ broken_quoted
506 filename = $1
507 elsif head =~ broken_unquoted
508 filename = $1
509 end
510
511 if filename && filename !~ /\\[^\\"]/
512 filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
513 end
514
6674f365 » eTM
2009-04-11 better multipart handling
515 content_type = head[/Content-Type: (.*)#{EOL}/ni, 1]
516 name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1]
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
517
acffe8ef » chneukirchen
2010-02-07 parse_multipart: Content-Type field doesn't imply a file upload
518 if filename
11146915 » chneukirchen
2008-04-18 Open multipart tempfiles in binary mode
519 body = Tempfile.new("RackMultipart")
fe22d0fd » chneukirchen
2008-06-24 Only call binmode when possible in the multipart parser
520 body.binmode if body.respond_to?(:binmode)
11146915 » chneukirchen
2008-04-18 Open multipart tempfiles in binary mode
521 end
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
522
523 next
524 end
525
526 # Save the read body part.
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
527 if head && (boundary_size+4 < buf.size)
528 body << buf.slice!(0, buf.size - (boundary_size+4))
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
529 end
530
e6095805 » FooBarWidget
2009-04-15 Optimize parse_multipart: put read data into a buffer in order to red…
531 c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer)
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
532 raise EOFError, "bad content body" if c.nil? || c.empty?
533 buf << c
534 content_length -= c.size
535 end
536
537 # Save the rest.
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
538 if i = buf.index(rx)
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
539 body << buf.slice!(0, i)
540 buf.slice!(0, boundary_size+2)
b064d530 » chneukirchen
2007-02-28 Make multipart reading more robust
541
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
542 content_length = -1 if $1 == "--"
543 end
544
a2a26001 » josh
2009-01-20 Don't create empty tempfile if no file was selected
545 if filename == ""
546 # filename is blank which means no file has been selected
547 data = nil
548 elsif filename
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
549 body.rewind
5368e30d » josh
2009-01-15 Trim IE's full file path in multipart uploads
550
551 # Take the basename of the upload's original filename.
552 # This handles the full Windows paths given by Internet Explorer
553 # (and perhaps other broken user agents) without affecting
554 # those which give the lone filename.
a36ac970 » rubys
2010-01-20 Improve multipart parsing support for UAs that don't correctly escape
555 filename = filename.split(/[\/\\]/).last
5368e30d » josh
2009-01-15 Trim IE's full file path in multipart uploads
556
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
557 data = {:filename => filename, :type => content_type,
558 :name => name, :tempfile => body, :head => head}
6674f365 » eTM
2009-04-11 better multipart handling
559 elsif !filename && content_type
560 body.rewind
6139d297 » josh
2009-04-25 Add Utils::Multipart.build_multipart to complement Utils::Multipart.p…
561
6674f365 » eTM
2009-04-11 better multipart handling
562 # Generic multipart cases, not coming from a form
563 data = {:type => content_type,
564 :name => name, :tempfile => body, :head => head}
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
565 else
566 data = body
567 end
568
4ab6e2a6 » rob-at-thewebfellas
2009-03-13 Remove files from params when no file is selected [#37 state:resolved]
569 Utils.normalize_params(params, name, data) unless data.nil?
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
570
a9440bc7 » Derek and Matt
2009-11-16 Fixed multipart parameter parsing for when a field's body ends at the…
571 # break if we're at the end of a buffer, but not if it is the end of a field
572 break if (buf.empty? && $1 != EOL) || content_length == -1
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
573 }
574
95f068e8 » FooBarWidget
2009-04-14 Remove code for handling the possibility that rack.input may not be r…
575 input.rewind
3513f4fd » josh
2009-01-13 Add tests for multipart uploads. Also ensure multipart parser tries t…
576
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
577 params
578 end
579 end
6139d297 » josh
2009-04-25 Add Utils::Multipart.build_multipart to complement Utils::Multipart.p…
580
581 def self.build_multipart(params, first = true)
5a2eae5c » josh
2009-04-28 Utils::Multipart.build_multipart returns nil if no UploadedFiles are …
582 if first
583 unless params.is_a?(Hash)
584 raise ArgumentError, "value must be a Hash"
585 end
586
587 multipart = false
588 query = lambda { |value|
589 case value
590 when Array
591 value.each(&query)
592 when Hash
593 value.values.each(&query)
594 when UploadedFile
595 multipart = true
596 end
597 }
598 params.values.each(&query)
599 return nil unless multipart
600 end
601
6139d297 » josh
2009-04-25 Add Utils::Multipart.build_multipart to complement Utils::Multipart.p…
602 flattened_params = Hash.new
603
604 params.each do |key, value|
605 k = first ? key.to_s : "[#{key}]"
606
607 case value
608 when Array
609 value.map { |v|
610 build_multipart(v, false).each { |subkey, subvalue|
611 flattened_params["#{k}[]#{subkey}"] = subvalue
612 }
613 }
614 when Hash
615 build_multipart(value, false).each { |subkey, subvalue|
616 flattened_params[k + subkey] = subvalue
617 }
618 else
619 flattened_params[k] = value
620 end
621 end
622
623 if first
624 flattened_params.map { |name, file|
625 if file.respond_to?(:original_filename)
626 ::File.open(file.path, "rb") do |f|
627 f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
628 <<-EOF
629 --#{MULTIPART_BOUNDARY}\r
630 Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r
631 Content-Type: #{file.content_type}\r
632 Content-Length: #{::File.stat(file.path).size}\r
633 \r
634 #{f.read}\r
635 EOF
636 end
637 else
638 <<-EOF
639 --#{MULTIPART_BOUNDARY}\r
640 Content-Disposition: form-data; name="#{name}"\r
641 \r
642 #{file}\r
643 EOF
644 end
645 }.join + "--#{MULTIPART_BOUNDARY}--\r"
646 else
647 flattened_params
648 end
649 end
4fe5360a » chneukirchen
2007-02-28 Make Rack::Request read multipart form data
650 end
7ed819ad » chneukirchen
2007-02-16 Add Rack::Response and Rack::Utils
651 end
652 end
Something went wrong with that request. Please try again.