rack 1.6.13 → 2.0.6
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.
- checksums.yaml +4 -4
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +3 -3
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +70 -21
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +273 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +108 -138
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +136 -211
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +771 -605
- data/test/spec_response.rb +215 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +60 -40
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
|
@@ -6,159 +6,304 @@ module Rack
|
|
|
6
6
|
|
|
7
7
|
class Parser
|
|
8
8
|
BUFSIZE = 16384
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
io
|
|
9
|
+
TEXT_PLAIN = "text/plain"
|
|
10
|
+
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
|
11
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0".freeze, '%00'.freeze))])
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class BoundedIO # :nodoc:
|
|
15
|
+
def initialize(io, content_length)
|
|
16
|
+
@io = io
|
|
17
|
+
@content_length = content_length
|
|
18
|
+
@cursor = 0
|
|
19
|
+
end
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
def read(size)
|
|
22
|
+
return if @cursor >= @content_length
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
lambda { |filename, content_type| Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0".freeze, '%00'.freeze))]) }
|
|
22
|
-
bufsize = env['rack.multipart.buffer_size'] || BUFSIZE
|
|
24
|
+
left = @content_length - @cursor
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
str = if left < size
|
|
27
|
+
@io.read left
|
|
28
|
+
else
|
|
29
|
+
@io.read size
|
|
30
|
+
end
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
if str
|
|
33
|
+
@cursor += str.bytesize
|
|
34
|
+
else
|
|
35
|
+
# Raise an error for mismatching Content-Length and actual contents
|
|
36
|
+
raise EOFError, "bad content body"
|
|
37
|
+
end
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
@buf.force_encoding Encoding::ASCII_8BIT
|
|
39
|
+
str
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
@
|
|
35
|
-
@boundary = "--#{boundary}"
|
|
36
|
-
@io = io
|
|
37
|
-
@content_length = content_length
|
|
38
|
-
@boundary_size = Utils.bytesize(@boundary) + EOL.size
|
|
39
|
-
@env = env
|
|
40
|
-
@tempfile = tempfile
|
|
41
|
-
@bufsize = bufsize
|
|
42
|
+
def eof?; @content_length == @cursor; end
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
@
|
|
44
|
+
def rewind
|
|
45
|
+
@io.rewind
|
|
45
46
|
end
|
|
47
|
+
end
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
MultipartInfo = Struct.new :params, :tmp_files
|
|
50
|
+
EMPTY = MultipartInfo.new(nil, [])
|
|
51
|
+
|
|
52
|
+
def self.parse_boundary(content_type)
|
|
53
|
+
return unless content_type
|
|
54
|
+
data = content_type.match(MULTIPART)
|
|
55
|
+
return unless data
|
|
56
|
+
data[1]
|
|
49
57
|
end
|
|
50
58
|
|
|
51
|
-
def parse
|
|
52
|
-
|
|
59
|
+
def self.parse(io, content_length, content_type, tmpfile, bufsize, qp)
|
|
60
|
+
return EMPTY if 0 == content_length
|
|
61
|
+
|
|
62
|
+
boundary = parse_boundary content_type
|
|
63
|
+
return EMPTY unless boundary
|
|
64
|
+
|
|
65
|
+
io = BoundedIO.new(io, content_length) if content_length
|
|
66
|
+
|
|
67
|
+
parser = new(boundary, tmpfile, bufsize, qp)
|
|
68
|
+
parser.on_read io.read(bufsize), io.eof?
|
|
53
69
|
|
|
54
|
-
opened_files = 0
|
|
55
70
|
loop do
|
|
71
|
+
break if parser.state == :DONE
|
|
72
|
+
parser.on_read io.read(bufsize), io.eof?
|
|
73
|
+
end
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
io.rewind
|
|
76
|
+
parser.result
|
|
77
|
+
end
|
|
59
78
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
79
|
+
class Collector
|
|
80
|
+
class MimePart < Struct.new(:body, :head, :filename, :content_type, :name)
|
|
81
|
+
def get_data
|
|
82
|
+
data = body
|
|
83
|
+
if filename == ""
|
|
84
|
+
# filename is blank which means no file has been selected
|
|
85
|
+
return
|
|
86
|
+
elsif filename
|
|
87
|
+
body.rewind if body.respond_to?(:rewind)
|
|
88
|
+
|
|
89
|
+
# Take the basename of the upload's original filename.
|
|
90
|
+
# This handles the full Windows paths given by Internet Explorer
|
|
91
|
+
# (and perhaps other broken user agents) without affecting
|
|
92
|
+
# those which give the lone filename.
|
|
93
|
+
fn = filename.split(/[\/\\]/).last
|
|
94
|
+
|
|
95
|
+
data = {:filename => fn, :type => content_type,
|
|
96
|
+
:name => name, :tempfile => body, :head => head}
|
|
97
|
+
elsif !filename && content_type && body.is_a?(IO)
|
|
98
|
+
body.rewind
|
|
99
|
+
|
|
100
|
+
# Generic multipart cases, not coming from a form
|
|
101
|
+
data = {:type => content_type,
|
|
102
|
+
:name => name, :tempfile => body, :head => head}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
yield data
|
|
63
106
|
end
|
|
107
|
+
end
|
|
64
108
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
109
|
+
class BufferPart < MimePart
|
|
110
|
+
def file?; false; end
|
|
111
|
+
def close; end
|
|
112
|
+
end
|
|
69
113
|
|
|
70
|
-
|
|
71
|
-
end
|
|
114
|
+
class TempfilePart < MimePart
|
|
115
|
+
def file?; true; end
|
|
116
|
+
def close; body.close; end
|
|
117
|
+
end
|
|
72
118
|
|
|
73
|
-
|
|
74
|
-
tag_multipart_encoding(filename, content_type, name, data)
|
|
119
|
+
include Enumerable
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
|
|
121
|
+
def initialize tempfile
|
|
122
|
+
@tempfile = tempfile
|
|
123
|
+
@mime_parts = []
|
|
124
|
+
@open_files = 0
|
|
125
|
+
end
|
|
78
126
|
|
|
79
|
-
|
|
80
|
-
|
|
127
|
+
def each
|
|
128
|
+
@mime_parts.each { |part| yield part }
|
|
81
129
|
end
|
|
82
130
|
|
|
83
|
-
|
|
131
|
+
def on_mime_head mime_index, head, filename, content_type, name
|
|
132
|
+
if filename
|
|
133
|
+
body = @tempfile.call(filename, content_type)
|
|
134
|
+
body.binmode if body.respond_to?(:binmode)
|
|
135
|
+
klass = TempfilePart
|
|
136
|
+
@open_files += 1
|
|
137
|
+
else
|
|
138
|
+
body = String.new
|
|
139
|
+
klass = BufferPart
|
|
140
|
+
end
|
|
84
141
|
|
|
85
|
-
|
|
86
|
-
|
|
142
|
+
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
|
143
|
+
check_open_files
|
|
144
|
+
end
|
|
87
145
|
|
|
88
|
-
|
|
89
|
-
|
|
146
|
+
def on_mime_body mime_index, content
|
|
147
|
+
@mime_parts[mime_index].body << content
|
|
148
|
+
end
|
|
90
149
|
|
|
91
|
-
|
|
150
|
+
def on_mime_finish mime_index
|
|
151
|
+
end
|
|
92
152
|
|
|
93
|
-
|
|
94
|
-
loop do
|
|
95
|
-
content = @io.read(@bufsize)
|
|
96
|
-
raise EOFError, "bad content body" unless content
|
|
97
|
-
@buf << content
|
|
153
|
+
private
|
|
98
154
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
155
|
+
def check_open_files
|
|
156
|
+
if Utils.multipart_part_limit > 0
|
|
157
|
+
if @open_files >= Utils.multipart_part_limit
|
|
158
|
+
@mime_parts.each(&:close)
|
|
159
|
+
raise MultipartPartLimitError, 'Maximum file multiparts in content reached'
|
|
160
|
+
end
|
|
102
161
|
end
|
|
103
|
-
|
|
104
|
-
raise EOFError, "bad content body" if Utils.bytesize(@buf) >= @bufsize
|
|
105
162
|
end
|
|
106
163
|
end
|
|
107
164
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
165
|
+
attr_reader :state
|
|
166
|
+
|
|
167
|
+
def initialize(boundary, tempfile, bufsize, query_parser)
|
|
168
|
+
@buf = String.new
|
|
169
|
+
|
|
170
|
+
@query_parser = query_parser
|
|
171
|
+
@params = query_parser.make_params
|
|
172
|
+
@boundary = "--#{boundary}"
|
|
173
|
+
@bufsize = bufsize
|
|
174
|
+
|
|
175
|
+
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
|
176
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
|
177
|
+
@full_boundary = @boundary
|
|
178
|
+
@end_boundary = @boundary + '--'
|
|
179
|
+
@state = :FAST_FORWARD
|
|
180
|
+
@mime_index = 0
|
|
181
|
+
@collector = Collector.new tempfile
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def on_read content, eof
|
|
185
|
+
handle_empty_content!(content, eof)
|
|
186
|
+
@buf << content
|
|
187
|
+
run_parser
|
|
188
|
+
end
|
|
111
189
|
|
|
112
|
-
|
|
113
|
-
|
|
190
|
+
def result
|
|
191
|
+
@collector.each do |part|
|
|
192
|
+
part.get_data do |data|
|
|
193
|
+
tag_multipart_encoding(part.filename, part.content_type, part.name, data)
|
|
194
|
+
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
|
195
|
+
end
|
|
114
196
|
end
|
|
115
197
|
|
|
116
|
-
|
|
198
|
+
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
|
199
|
+
end
|
|
117
200
|
|
|
118
|
-
|
|
119
|
-
if !head && i = @buf.index(EOL+EOL)
|
|
120
|
-
head = @buf.slice!(0, i+2) # First \r\n
|
|
201
|
+
private
|
|
121
202
|
|
|
122
|
-
|
|
203
|
+
def run_parser
|
|
204
|
+
loop do
|
|
205
|
+
case @state
|
|
206
|
+
when :FAST_FORWARD
|
|
207
|
+
break if handle_fast_forward == :want_read
|
|
208
|
+
when :CONSUME_TOKEN
|
|
209
|
+
break if handle_consume_token == :want_read
|
|
210
|
+
when :MIME_HEAD
|
|
211
|
+
break if handle_mime_head == :want_read
|
|
212
|
+
when :MIME_BODY
|
|
213
|
+
break if handle_mime_body == :want_read
|
|
214
|
+
when :DONE
|
|
215
|
+
break
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
123
219
|
|
|
124
|
-
|
|
125
|
-
|
|
220
|
+
def handle_fast_forward
|
|
221
|
+
if consume_boundary
|
|
222
|
+
@state = :MIME_HEAD
|
|
223
|
+
else
|
|
224
|
+
raise EOFError, "bad content body" if @buf.bytesize >= @bufsize
|
|
225
|
+
:want_read
|
|
226
|
+
end
|
|
227
|
+
end
|
|
126
228
|
|
|
127
|
-
|
|
229
|
+
def handle_consume_token
|
|
230
|
+
tok = consume_boundary
|
|
231
|
+
# break if we're at the end of a buffer, but not if it is the end of a field
|
|
232
|
+
if tok == :END_BOUNDARY || (@buf.empty? && tok != :BOUNDARY)
|
|
233
|
+
@state = :DONE
|
|
234
|
+
else
|
|
235
|
+
@state = :MIME_HEAD
|
|
236
|
+
end
|
|
237
|
+
end
|
|
128
238
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
239
|
+
def handle_mime_head
|
|
240
|
+
if @buf.index(EOL + EOL)
|
|
241
|
+
i = @buf.index(EOL+EOL)
|
|
242
|
+
head = @buf.slice!(0, i+2) # First \r\n
|
|
243
|
+
@buf.slice!(0, 2) # Second \r\n
|
|
244
|
+
|
|
245
|
+
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
|
246
|
+
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
|
247
|
+
name = Rack::Auth::Digest::Params::dequote(name)
|
|
248
|
+
else
|
|
249
|
+
name = head[MULTIPART_CONTENT_ID, 1]
|
|
250
|
+
end
|
|
132
251
|
|
|
133
|
-
|
|
134
|
-
(@env['rack.tempfiles'] ||= []) << body = @tempfile.call(filename, content_type)
|
|
135
|
-
body.binmode if body.respond_to?(:binmode)
|
|
136
|
-
end
|
|
252
|
+
filename = get_filename(head)
|
|
137
253
|
|
|
138
|
-
|
|
254
|
+
if name.nil? || name.empty?
|
|
255
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
|
139
256
|
end
|
|
140
257
|
|
|
258
|
+
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
|
259
|
+
@state = :MIME_BODY
|
|
260
|
+
else
|
|
261
|
+
:want_read
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def handle_mime_body
|
|
266
|
+
if i = @buf.index(rx)
|
|
267
|
+
# Save the rest.
|
|
268
|
+
@collector.on_mime_body @mime_index, @buf.slice!(0, i)
|
|
269
|
+
@buf.slice!(0, 2) # Remove \r\n after the content
|
|
270
|
+
@state = :CONSUME_TOKEN
|
|
271
|
+
@mime_index += 1
|
|
272
|
+
else
|
|
141
273
|
# Save the read body part.
|
|
142
|
-
if
|
|
143
|
-
|
|
274
|
+
if @rx_max_size < @buf.size
|
|
275
|
+
@collector.on_mime_body @mime_index, @buf.slice!(0, @buf.size - @rx_max_size)
|
|
144
276
|
end
|
|
277
|
+
:want_read
|
|
278
|
+
end
|
|
279
|
+
end
|
|
145
280
|
|
|
146
|
-
|
|
147
|
-
raise EOFError, "bad content body" if content.nil? || content.empty?
|
|
281
|
+
def full_boundary; @full_boundary; end
|
|
148
282
|
|
|
149
|
-
|
|
150
|
-
@content_length -= content.size if @content_length
|
|
151
|
-
end
|
|
283
|
+
def rx; @rx; end
|
|
152
284
|
|
|
153
|
-
|
|
285
|
+
def consume_boundary
|
|
286
|
+
while @buf.gsub!(/\A([^\n]*(?:\n|\Z))/, '')
|
|
287
|
+
read_buffer = $1
|
|
288
|
+
case read_buffer.strip
|
|
289
|
+
when full_boundary then return :BOUNDARY
|
|
290
|
+
when @end_boundary then return :END_BOUNDARY
|
|
291
|
+
end
|
|
292
|
+
return if @buf.empty?
|
|
293
|
+
end
|
|
154
294
|
end
|
|
155
295
|
|
|
156
296
|
def get_filename(head)
|
|
157
297
|
filename = nil
|
|
158
298
|
case head
|
|
159
299
|
when RFC2183
|
|
160
|
-
|
|
161
|
-
|
|
300
|
+
params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
|
|
301
|
+
|
|
302
|
+
if filename = params['filename']
|
|
303
|
+
filename = $1 if filename =~ /^"(.*)"$/
|
|
304
|
+
elsif filename = params['filename*']
|
|
305
|
+
encoding, _, filename = filename.split("'", 3)
|
|
306
|
+
end
|
|
162
307
|
when BROKEN_QUOTED, BROKEN_UNQUOTED
|
|
163
308
|
filename = $1
|
|
164
309
|
end
|
|
@@ -169,84 +314,55 @@ module Rack
|
|
|
169
314
|
filename = Utils.unescape(filename)
|
|
170
315
|
end
|
|
171
316
|
|
|
172
|
-
|
|
317
|
+
filename.scrub!
|
|
173
318
|
|
|
174
319
|
if filename !~ /\\[^\\"]/
|
|
175
320
|
filename = filename.gsub(/\\(.)/, '\1')
|
|
176
321
|
end
|
|
177
|
-
filename
|
|
178
|
-
end
|
|
179
322
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
unless filename.valid_encoding?
|
|
183
|
-
# FIXME: this force_encoding is for Ruby 2.0 and 1.9 support.
|
|
184
|
-
# We can remove it after they are dropped
|
|
185
|
-
filename.force_encoding(Encoding::ASCII_8BIT)
|
|
186
|
-
filename.encode!(:invalid => :replace, :undef => :replace)
|
|
187
|
-
end
|
|
323
|
+
if encoding
|
|
324
|
+
filename.force_encoding ::Encoding.find(encoding)
|
|
188
325
|
end
|
|
189
326
|
|
|
190
|
-
|
|
191
|
-
|
|
327
|
+
filename
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
CHARSET = "charset"
|
|
192
331
|
|
|
193
|
-
|
|
194
|
-
|
|
332
|
+
def tag_multipart_encoding(filename, content_type, name, body)
|
|
333
|
+
name = name.to_s
|
|
334
|
+
encoding = Encoding::UTF_8
|
|
195
335
|
|
|
196
|
-
|
|
336
|
+
name.force_encoding(encoding)
|
|
197
337
|
|
|
198
|
-
|
|
338
|
+
return if filename
|
|
199
339
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
340
|
+
if content_type
|
|
341
|
+
list = content_type.split(';')
|
|
342
|
+
type_subtype = list.first
|
|
343
|
+
type_subtype.strip!
|
|
344
|
+
if TEXT_PLAIN == type_subtype
|
|
345
|
+
rest = list.drop 1
|
|
346
|
+
rest.each do |param|
|
|
347
|
+
k,v = param.split('=', 2)
|
|
348
|
+
k.strip!
|
|
349
|
+
v.strip!
|
|
350
|
+
v = v[1..-2] if v[0] == '"' && v[-1] == '"'
|
|
351
|
+
encoding = Encoding.find v if k == CHARSET
|
|
212
352
|
end
|
|
213
353
|
end
|
|
214
|
-
|
|
215
|
-
name.force_encoding encoding
|
|
216
|
-
body.force_encoding encoding
|
|
217
|
-
end
|
|
218
|
-
else
|
|
219
|
-
def scrub_filename(filename)
|
|
220
354
|
end
|
|
221
|
-
def tag_multipart_encoding(filename, content_type, name, body)
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def get_data(filename, body, content_type, name, head)
|
|
226
|
-
data = body
|
|
227
|
-
if filename == ""
|
|
228
|
-
# filename is blank which means no file has been selected
|
|
229
|
-
return
|
|
230
|
-
elsif filename
|
|
231
|
-
body.rewind if body.respond_to?(:rewind)
|
|
232
355
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
# those which give the lone filename.
|
|
237
|
-
filename = filename.split(/[\/\\]/).last
|
|
356
|
+
name.force_encoding(encoding)
|
|
357
|
+
body.force_encoding(encoding)
|
|
358
|
+
end
|
|
238
359
|
|
|
239
|
-
data = {:filename => filename, :type => content_type,
|
|
240
|
-
:name => name, :tempfile => body, :head => head}
|
|
241
|
-
elsif !filename && content_type && body.is_a?(IO)
|
|
242
|
-
body.rewind
|
|
243
360
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
361
|
+
def handle_empty_content!(content, eof)
|
|
362
|
+
if content.nil? || content.empty?
|
|
363
|
+
raise EOFError if eof
|
|
364
|
+
return true
|
|
247
365
|
end
|
|
248
|
-
|
|
249
|
-
yield data
|
|
250
366
|
end
|
|
251
367
|
end
|
|
252
368
|
end
|