rack 1.2.8 → 1.3.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- data/README +9 -177
- data/Rakefile +2 -1
- data/SPEC +2 -2
- data/lib/rack.rb +2 -13
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/digest/md5.rb +6 -2
- data/lib/rack/auth/digest/params.rb +5 -7
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/backports/uri/common.rb +64 -0
- data/lib/rack/builder.rb +60 -3
- data/lib/rack/chunked.rb +29 -22
- data/lib/rack/conditionalget.rb +35 -16
- data/lib/rack/content_length.rb +3 -3
- data/lib/rack/deflater.rb +5 -2
- data/lib/rack/etag.rb +38 -10
- data/lib/rack/file.rb +76 -43
- data/lib/rack/handler.rb +13 -7
- data/lib/rack/handler/cgi.rb +0 -2
- data/lib/rack/handler/fastcgi.rb +13 -4
- data/lib/rack/handler/lsws.rb +0 -2
- data/lib/rack/handler/mongrel.rb +12 -2
- data/lib/rack/handler/scgi.rb +9 -1
- data/lib/rack/handler/thin.rb +7 -1
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/lint.rb +2 -2
- data/lib/rack/lock.rb +29 -3
- data/lib/rack/methodoverride.rb +1 -1
- data/lib/rack/mime.rb +2 -2
- data/lib/rack/mock.rb +28 -33
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +164 -0
- data/lib/rack/multipart/uploaded_file.rb +30 -0
- data/lib/rack/request.rb +55 -19
- data/lib/rack/response.rb +10 -8
- data/lib/rack/sendfile.rb +14 -18
- data/lib/rack/server.rb +55 -8
- data/lib/rack/session/abstract/id.rb +233 -22
- data/lib/rack/session/cookie.rb +99 -46
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +22 -43
- data/lib/rack/showexceptions.rb +40 -11
- data/lib/rack/showstatus.rb +9 -2
- data/lib/rack/static.rb +29 -9
- data/lib/rack/urlmap.rb +6 -1
- data/lib/rack/utils.rb +67 -326
- data/rack.gemspec +2 -3
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +3 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -1
- data/test/cgi/lighttpd.errors +412 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/text +5 -0
- data/test/multipart/webkit +32 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/spec_auth_digest.rb +20 -5
- data/test/spec_builder.rb +29 -0
- data/test/spec_cgi.rb +11 -0
- data/test/spec_chunked.rb +1 -1
- data/test/spec_commonlogger.rb +1 -1
- data/test/spec_conditionalget.rb +47 -0
- data/test/spec_content_length.rb +0 -6
- data/test/spec_content_type.rb +5 -5
- data/test/spec_deflater.rb +46 -2
- data/test/spec_etag.rb +68 -1
- data/test/spec_fastcgi.rb +11 -0
- data/test/spec_file.rb +54 -3
- data/test/spec_handler.rb +23 -5
- data/test/spec_lint.rb +2 -2
- data/test/spec_lock.rb +111 -5
- data/test/spec_methodoverride.rb +2 -2
- data/test/spec_mock.rb +3 -3
- data/test/spec_mongrel.rb +1 -2
- data/test/spec_multipart.rb +279 -0
- data/test/spec_request.rb +222 -38
- data/test/spec_response.rb +9 -3
- data/test/spec_server.rb +74 -0
- data/test/spec_session_abstract_id.rb +43 -0
- data/test/spec_session_cookie.rb +97 -15
- data/test/spec_session_memcache.rb +60 -50
- data/test/spec_session_pool.rb +63 -40
- data/test/spec_showexceptions.rb +64 -0
- data/test/spec_static.rb +23 -0
- data/test/spec_utils.rb +65 -351
- data/test/spec_webrick.rb +23 -4
- metadata +35 -15
- data/test/spec_auth.rb +0 -57
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Multipart
|
5
|
+
class Parser
|
6
|
+
BUFSIZE = 16384
|
7
|
+
|
8
|
+
def initialize(env)
|
9
|
+
@env = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
return nil unless setup_parse
|
14
|
+
|
15
|
+
fast_forward_to_first_boundary
|
16
|
+
|
17
|
+
loop do
|
18
|
+
head, filename, content_type, name, body =
|
19
|
+
get_current_head_and_filename_and_content_type_and_name_and_body
|
20
|
+
|
21
|
+
# Save the rest.
|
22
|
+
if i = @buf.index(rx)
|
23
|
+
body << @buf.slice!(0, i)
|
24
|
+
@buf.slice!(0, @boundary_size+2)
|
25
|
+
|
26
|
+
@content_length = -1 if $1 == "--"
|
27
|
+
end
|
28
|
+
|
29
|
+
filename, data = get_data(filename, body, content_type, name, head)
|
30
|
+
|
31
|
+
Utils.normalize_params(@params, name, data) unless data.nil?
|
32
|
+
|
33
|
+
# break if we're at the end of a buffer, but not if it is the end of a field
|
34
|
+
break if (@buf.empty? && $1 != EOL) || @content_length == -1
|
35
|
+
end
|
36
|
+
|
37
|
+
@io.rewind
|
38
|
+
|
39
|
+
@params
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def setup_parse
|
44
|
+
return false unless @env['CONTENT_TYPE'] =~ MULTIPART
|
45
|
+
|
46
|
+
@boundary = "--#{$1}"
|
47
|
+
|
48
|
+
@buf = ""
|
49
|
+
@params = {}
|
50
|
+
|
51
|
+
@content_length = @env['CONTENT_LENGTH'].to_i
|
52
|
+
@io = @env['rack.input']
|
53
|
+
@io.rewind
|
54
|
+
|
55
|
+
@boundary_size = Utils.bytesize(@boundary) + EOL.size
|
56
|
+
|
57
|
+
@content_length -= @boundary_size
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def full_boundary
|
62
|
+
@boundary + EOL
|
63
|
+
end
|
64
|
+
|
65
|
+
def rx
|
66
|
+
@rx ||= /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
67
|
+
end
|
68
|
+
|
69
|
+
def fast_forward_to_first_boundary
|
70
|
+
loop do
|
71
|
+
read_buffer = @io.gets
|
72
|
+
break if read_buffer == full_boundary
|
73
|
+
raise EOFError, "bad content body" if read_buffer.nil?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def get_current_head_and_filename_and_content_type_and_name_and_body
|
78
|
+
head = nil
|
79
|
+
body = ''
|
80
|
+
filename = content_type = name = nil
|
81
|
+
content = nil
|
82
|
+
|
83
|
+
until head && @buf =~ rx
|
84
|
+
if !head && i = @buf.index(EOL+EOL)
|
85
|
+
head = @buf.slice!(0, i+2) # First \r\n
|
86
|
+
|
87
|
+
@buf.slice!(0, 2) # Second \r\n
|
88
|
+
|
89
|
+
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
90
|
+
name = head[MULTIPART_CONTENT_DISPOSITION, 1] || head[MULTIPART_CONTENT_ID, 1]
|
91
|
+
|
92
|
+
filename = get_filename(head)
|
93
|
+
|
94
|
+
if filename
|
95
|
+
body = Tempfile.new("RackMultipart")
|
96
|
+
body.binmode if body.respond_to?(:binmode)
|
97
|
+
end
|
98
|
+
|
99
|
+
next
|
100
|
+
end
|
101
|
+
|
102
|
+
# Save the read body part.
|
103
|
+
if head && (@boundary_size+4 < @buf.size)
|
104
|
+
body << @buf.slice!(0, @buf.size - (@boundary_size+4))
|
105
|
+
end
|
106
|
+
|
107
|
+
content = @io.read(BUFSIZE < @content_length ? BUFSIZE : @content_length)
|
108
|
+
raise EOFError, "bad content body" if content.nil? || content.empty?
|
109
|
+
|
110
|
+
@buf << content
|
111
|
+
@content_length -= content.size
|
112
|
+
end
|
113
|
+
|
114
|
+
[head, filename, content_type, name, body]
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_filename(head)
|
118
|
+
filename = nil
|
119
|
+
if head =~ RFC2183
|
120
|
+
filename = Hash[head.scan(DISPPARM)]['filename']
|
121
|
+
filename = $1 if filename and filename =~ /^"(.*)"$/
|
122
|
+
elsif head =~ BROKEN_QUOTED
|
123
|
+
filename = $1
|
124
|
+
elsif head =~ BROKEN_UNQUOTED
|
125
|
+
filename = $1
|
126
|
+
end
|
127
|
+
|
128
|
+
if filename && filename !~ /\\[^\\"]/
|
129
|
+
filename = Utils.unescape(filename).gsub(/\\(.)/, '\1')
|
130
|
+
end
|
131
|
+
filename
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_data(filename, body, content_type, name, head)
|
135
|
+
data = nil
|
136
|
+
if filename == ""
|
137
|
+
# filename is blank which means no file has been selected
|
138
|
+
return data
|
139
|
+
elsif filename
|
140
|
+
body.rewind
|
141
|
+
|
142
|
+
# Take the basename of the upload's original filename.
|
143
|
+
# This handles the full Windows paths given by Internet Explorer
|
144
|
+
# (and perhaps other broken user agents) without affecting
|
145
|
+
# those which give the lone filename.
|
146
|
+
filename = filename.split(/[\/\\]/).last
|
147
|
+
|
148
|
+
data = {:filename => filename, :type => content_type,
|
149
|
+
:name => name, :tempfile => body, :head => head}
|
150
|
+
elsif !filename && content_type && body.is_a?(IO)
|
151
|
+
body.rewind
|
152
|
+
|
153
|
+
# Generic multipart cases, not coming from a form
|
154
|
+
data = {:type => content_type,
|
155
|
+
:name => name, :tempfile => body, :head => head}
|
156
|
+
else
|
157
|
+
data = body
|
158
|
+
end
|
159
|
+
|
160
|
+
[filename, data]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rack
|
2
|
+
module Multipart
|
3
|
+
class UploadedFile
|
4
|
+
# The filename, *not* including the path, of the "uploaded" file
|
5
|
+
attr_reader :original_filename
|
6
|
+
|
7
|
+
# The content type of the "uploaded" file
|
8
|
+
attr_accessor :content_type
|
9
|
+
|
10
|
+
def initialize(path, content_type = "text/plain", binary = false)
|
11
|
+
raise "#{path} file does not exist" unless ::File.exist?(path)
|
12
|
+
@content_type = content_type
|
13
|
+
@original_filename = ::File.basename(path)
|
14
|
+
@tempfile = Tempfile.new(@original_filename)
|
15
|
+
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
|
16
|
+
@tempfile.binmode if binary
|
17
|
+
FileUtils.copy_file(path, @tempfile.path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def path
|
21
|
+
@tempfile.path
|
22
|
+
end
|
23
|
+
alias_method :local_path, :path
|
24
|
+
|
25
|
+
def method_missing(method_name, *args, &block) #:nodoc:
|
26
|
+
@tempfile.__send__(method_name, *args, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/rack/request.rb
CHANGED
@@ -22,14 +22,17 @@ module Rack
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def body; @env["rack.input"] end
|
25
|
-
def scheme; @env["rack.url_scheme"] end
|
26
25
|
def script_name; @env["SCRIPT_NAME"].to_s end
|
27
26
|
def path_info; @env["PATH_INFO"].to_s end
|
28
|
-
def port; @env["SERVER_PORT"].to_i end
|
29
27
|
def request_method; @env["REQUEST_METHOD"] end
|
30
28
|
def query_string; @env["QUERY_STRING"].to_s end
|
31
29
|
def content_length; @env['CONTENT_LENGTH'] end
|
32
|
-
|
30
|
+
|
31
|
+
def content_type
|
32
|
+
content_type = @env['CONTENT_TYPE']
|
33
|
+
content_type.nil? || content_type.empty? ? nil : content_type
|
34
|
+
end
|
35
|
+
|
33
36
|
def session; @env['rack.session'] ||= {} end
|
34
37
|
def session_options; @env['rack.session.options'] ||= {} end
|
35
38
|
def logger; @env['rack.logger'] end
|
@@ -51,9 +54,9 @@ module Rack
|
|
51
54
|
# { 'charset' => 'utf-8' }
|
52
55
|
def media_type_params
|
53
56
|
return {} if content_type.nil?
|
54
|
-
content_type.split(/\s*[;,]\s*/)[1..-1].
|
57
|
+
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
|
55
58
|
collect { |s| s.split('=', 2) }.
|
56
|
-
|
59
|
+
map { |k,v| [k.downcase, v] }.flatten]
|
57
60
|
end
|
58
61
|
|
59
62
|
# The character set of the request body if a "charset" media type
|
@@ -64,6 +67,22 @@ module Rack
|
|
64
67
|
media_type_params['charset']
|
65
68
|
end
|
66
69
|
|
70
|
+
def scheme
|
71
|
+
if @env['HTTPS'] == 'on'
|
72
|
+
'https'
|
73
|
+
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
|
74
|
+
'https'
|
75
|
+
elsif @env['HTTP_X_FORWARDED_PROTO']
|
76
|
+
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
77
|
+
else
|
78
|
+
@env["rack.url_scheme"]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def ssl?
|
83
|
+
scheme == 'https'
|
84
|
+
end
|
85
|
+
|
67
86
|
def host_with_port
|
68
87
|
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
|
69
88
|
forwarded.split(/,\s?/).last
|
@@ -72,6 +91,20 @@ module Rack
|
|
72
91
|
end
|
73
92
|
end
|
74
93
|
|
94
|
+
def port
|
95
|
+
if port = host_with_port.split(/:/)[1]
|
96
|
+
port.to_i
|
97
|
+
elsif port = @env['HTTP_X_FORWARDED_PORT']
|
98
|
+
port.to_i
|
99
|
+
elsif ssl?
|
100
|
+
443
|
101
|
+
elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
|
102
|
+
80
|
103
|
+
else
|
104
|
+
@env["SERVER_PORT"].to_i
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
75
108
|
def host
|
76
109
|
# Remove port number.
|
77
110
|
host_with_port.to_s.gsub(/:\d+\z/, '')
|
@@ -84,6 +117,7 @@ module Rack
|
|
84
117
|
def get?; request_method == "GET" end
|
85
118
|
def head?; request_method == "HEAD" end
|
86
119
|
def options?; request_method == "OPTIONS" end
|
120
|
+
def patch?; request_method == "PATCH" end
|
87
121
|
def post?; request_method == "POST" end
|
88
122
|
def put?; request_method == "PUT" end
|
89
123
|
def trace?; request_method == "TRACE" end
|
@@ -149,7 +183,8 @@ module Rack
|
|
149
183
|
form_vars = @env["rack.input"].read
|
150
184
|
|
151
185
|
# Fix for Safari Ajax postings that always append \0
|
152
|
-
form_vars.sub!(/\0\z/, '')
|
186
|
+
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
187
|
+
form_vars.slice!(-1) if form_vars[-1] == ?\0
|
153
188
|
|
154
189
|
@env["rack.request.form_vars"] = form_vars
|
155
190
|
@env["rack.request.form_hash"] = parse_query(form_vars)
|
@@ -164,8 +199,8 @@ module Rack
|
|
164
199
|
|
165
200
|
# The union of GET and POST data.
|
166
201
|
def params
|
167
|
-
self.GET.
|
168
|
-
rescue EOFError
|
202
|
+
@params ||= self.GET.merge(self.POST)
|
203
|
+
rescue EOFError
|
169
204
|
self.GET
|
170
205
|
end
|
171
206
|
|
@@ -184,9 +219,9 @@ module Rack
|
|
184
219
|
keys.map{|key| params[key] }
|
185
220
|
end
|
186
221
|
|
187
|
-
# the referer of the client
|
222
|
+
# the referer of the client
|
188
223
|
def referer
|
189
|
-
@env['HTTP_REFERER']
|
224
|
+
@env['HTTP_REFERER']
|
190
225
|
end
|
191
226
|
alias referrer referer
|
192
227
|
|
@@ -207,10 +242,9 @@ module Rack
|
|
207
242
|
# precede those with less specific. Ordering with respect to other
|
208
243
|
# attributes (e.g., Domain) is unspecified.
|
209
244
|
@env["rack.request.cookie_hash"] =
|
210
|
-
Utils.parse_query(@env["rack.request.cookie_string"], ';,').
|
211
|
-
|
212
|
-
|
213
|
-
}
|
245
|
+
Hash[*Utils.parse_query(@env["rack.request.cookie_string"], ';,').map {|k,v|
|
246
|
+
[k, Array === v ? v.first : v]
|
247
|
+
}.flatten]
|
214
248
|
end
|
215
249
|
end
|
216
250
|
|
@@ -218,8 +252,7 @@ module Rack
|
|
218
252
|
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
|
219
253
|
end
|
220
254
|
|
221
|
-
|
222
|
-
def url
|
255
|
+
def base_url
|
223
256
|
url = scheme + "://"
|
224
257
|
url << host
|
225
258
|
|
@@ -228,11 +261,14 @@ module Rack
|
|
228
261
|
url << ":#{port}"
|
229
262
|
end
|
230
263
|
|
231
|
-
url << fullpath
|
232
|
-
|
233
264
|
url
|
234
265
|
end
|
235
266
|
|
267
|
+
# Tries to return a remake of the original request URL as a string.
|
268
|
+
def url
|
269
|
+
base_url + fullpath
|
270
|
+
end
|
271
|
+
|
236
272
|
def path
|
237
273
|
script_name + path_info
|
238
274
|
end
|
@@ -267,7 +303,7 @@ module Rack
|
|
267
303
|
end
|
268
304
|
|
269
305
|
def parse_multipart(env)
|
270
|
-
|
306
|
+
Rack::Multipart.parse_multipart(env)
|
271
307
|
end
|
272
308
|
end
|
273
309
|
end
|
data/lib/rack/response.rb
CHANGED
@@ -21,12 +21,13 @@ module Rack
|
|
21
21
|
|
22
22
|
def initialize(body=[], status=200, header={}, &block)
|
23
23
|
@status = status.to_i
|
24
|
-
@header = Utils::HeaderHash.new(
|
25
|
-
merge(header)
|
24
|
+
@header = Utils::HeaderHash.new("Content-Type" => "text/html").
|
25
|
+
merge(header)
|
26
26
|
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
27
|
+
@chunked = "chunked" == @header['Transfer-Encoding']
|
28
|
+
@writer = lambda { |x| @body << x }
|
29
|
+
@block = nil
|
30
|
+
@length = 0
|
30
31
|
|
31
32
|
@body = []
|
32
33
|
|
@@ -72,12 +73,14 @@ module Rack
|
|
72
73
|
|
73
74
|
if [204, 304].include?(status.to_i)
|
74
75
|
header.delete "Content-Type"
|
76
|
+
header.delete "Content-Length"
|
75
77
|
[status.to_i, header, []]
|
76
78
|
else
|
77
79
|
[status.to_i, header, self]
|
78
80
|
end
|
79
81
|
end
|
80
82
|
alias to_a finish # For *response
|
83
|
+
alias to_ary finish # For implicit-splat on Ruby 1.9.2
|
81
84
|
|
82
85
|
def each(&callback)
|
83
86
|
@body.each(&callback)
|
@@ -91,10 +94,10 @@ module Rack
|
|
91
94
|
#
|
92
95
|
def write(str)
|
93
96
|
s = str.to_s
|
94
|
-
@length += Rack::Utils.bytesize(s)
|
97
|
+
@length += Rack::Utils.bytesize(s) unless @chunked
|
95
98
|
@writer.call s
|
96
99
|
|
97
|
-
header["Content-Length"] = @length.to_s
|
100
|
+
header["Content-Length"] = @length.to_s unless @chunked
|
98
101
|
str
|
99
102
|
end
|
100
103
|
|
@@ -122,7 +125,6 @@ module Rack
|
|
122
125
|
def not_found?; @status == 404; end
|
123
126
|
|
124
127
|
def redirect?; [301, 302, 303, 307].include? @status; end
|
125
|
-
def empty?; [201, 204, 304].include? @status; end
|
126
128
|
|
127
129
|
# Headers
|
128
130
|
attr_reader :headers, :original_headers
|
data/lib/rack/sendfile.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
require 'rack/file'
|
2
2
|
|
3
3
|
module Rack
|
4
|
-
class File #:nodoc:
|
5
|
-
unless instance_methods(false).include?('to_path')
|
6
|
-
alias :to_path :path
|
7
|
-
end
|
8
|
-
end
|
9
4
|
|
10
5
|
# = Sendfile
|
11
6
|
#
|
@@ -52,9 +47,10 @@ module Rack
|
|
52
47
|
# }
|
53
48
|
#
|
54
49
|
# Note that the X-Sendfile-Type header must be set exactly as shown above. The
|
55
|
-
# X-Accel-Mapping header should specify the
|
56
|
-
# equals sign (=), followed name of the
|
57
|
-
# to. The middleware performs a simple substitution on the
|
50
|
+
# X-Accel-Mapping header should specify the by the location on the file system,
|
51
|
+
# followed by an equals sign (=), followed name of the private URL pattern
|
52
|
+
# that it maps to. The middleware performs a simple substitution on the
|
53
|
+
# resulting path.
|
58
54
|
#
|
59
55
|
# See Also: http://wiki.codemongers.com/NginxXSendfile
|
60
56
|
#
|
@@ -128,17 +124,17 @@ module Rack
|
|
128
124
|
end
|
129
125
|
|
130
126
|
private
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
127
|
+
def variation(env)
|
128
|
+
@variation ||
|
129
|
+
env['sendfile.type'] ||
|
130
|
+
env['HTTP_X_SENDFILE_TYPE']
|
131
|
+
end
|
136
132
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
133
|
+
def map_accel_path(env, file)
|
134
|
+
if mapping = env['HTTP_X_ACCEL_MAPPING']
|
135
|
+
internal, external = mapping.split('=', 2).map{ |p| p.strip }
|
136
|
+
file.sub(/^#{internal}/i, external)
|
142
137
|
end
|
138
|
+
end
|
143
139
|
end
|
144
140
|
end
|