actionpack 4.1.7 → 4.2.11
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +404 -451
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +11 -4
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +7 -1
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller/base.rb +3 -2
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +26 -26
- data/lib/action_controller/metal/conditional_get.rb +37 -12
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +1 -1
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +7 -3
- data/lib/action_controller/metal/http_authentication.rb +20 -10
- data/lib/action_controller/metal/instrumentation.rb +8 -5
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +25 -246
- data/lib/action_controller/metal/params_wrapper.rb +5 -5
- data/lib/action_controller/metal/rack_delegation.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +29 -11
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +78 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +129 -14
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/metal.rb +12 -11
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +119 -75
- data/lib/action_controller.rb +1 -1
- data/lib/action_dispatch/http/cache.rb +5 -4
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -3
- data/lib/action_dispatch/http/mime_type.rb +18 -4
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +37 -11
- data/lib/action_dispatch/http/response.rb +74 -23
- data/lib/action_dispatch/http/upload.rb +9 -8
- data/lib/action_dispatch/http/url.rb +89 -70
- data/lib/action_dispatch/journey/formatter.rb +34 -18
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +20 -28
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +4 -19
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/router/utils.rb +1 -1
- data/lib/action_dispatch/journey/router.rb +53 -77
- data/lib/action_dispatch/journey/routes.rb +4 -0
- data/lib/action_dispatch/journey/scanner.rb +5 -5
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/callbacks.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +34 -34
- data/lib/action_dispatch/middleware/debug_exceptions.rb +15 -4
- data/lib/action_dispatch/middleware/exception_wrapper.rb +50 -18
- data/lib/action_dispatch/middleware/flash.rb +13 -7
- data/lib/action_dispatch/middleware/params_parser.rb +1 -1
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/ssl.rb +1 -1
- data/lib/action_dispatch/middleware/static.rb +75 -39
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +37 -9
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
- data/lib/action_dispatch/railtie.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -12
- data/lib/action_dispatch/routing/mapper.rb +414 -283
- data/lib/action_dispatch/routing/polymorphic_routes.rb +191 -79
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +300 -173
- data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
- data/lib/action_dispatch/routing/url_for.rb +17 -5
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +28 -20
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +1 -5
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +55 -13
- data/lib/action_controller/metal/responder.rb +0 -297
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
# This middleware calculates the IP address of the remote client that is
|
3
5
|
# making the request. It does this by checking various headers that could
|
@@ -11,7 +13,7 @@ module ActionDispatch
|
|
11
13
|
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
|
12
14
|
# requires. Some Rack servers simply drop preceding headers, and only report
|
13
15
|
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
|
14
|
-
# If you are behind multiple proxy servers (like
|
16
|
+
# If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
|
15
17
|
# then you should test your Rack server to make sure your data is good.
|
16
18
|
#
|
17
19
|
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
|
@@ -28,14 +30,14 @@ module ActionDispatch
|
|
28
30
|
# guaranteed by the IP specification to be private addresses. Those will
|
29
31
|
# not be the ultimate client IP in production, and so are discarded. See
|
30
32
|
# http://en.wikipedia.org/wiki/Private_network for details.
|
31
|
-
TRUSTED_PROXIES =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
33
|
+
TRUSTED_PROXIES = [
|
34
|
+
"127.0.0.1", # localhost IPv4
|
35
|
+
"::1", # localhost IPv6
|
36
|
+
"fc00::/7", # private IPv6 range fc00::/7
|
37
|
+
"10.0.0.0/8", # private IPv4 range 10.x.x.x
|
38
|
+
"172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
39
|
+
"192.168.0.0/16", # private IPv4 range 192.168.x.x
|
40
|
+
].map { |proxy| IPAddr.new(proxy) }
|
39
41
|
|
40
42
|
attr_reader :check_ip, :proxies
|
41
43
|
|
@@ -47,24 +49,24 @@ module ActionDispatch
|
|
47
49
|
# clients (like WAP devices), or behind proxies that set headers in an
|
48
50
|
# incorrect or confusing way (like AWS ELB).
|
49
51
|
#
|
50
|
-
# The +custom_proxies+ argument can take
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
52
|
+
# The +custom_proxies+ argument can take an Array of string, IPAddr, or
|
53
|
+
# Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a
|
54
|
+
# single string, IPAddr, or Regexp object is provided, it will be used in
|
55
|
+
# addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you
|
56
|
+
# want in the middle (or at the beginning) of the X-Forwarded-For list,
|
57
|
+
# with your proxy servers after it. If your proxies aren't removed, pass
|
58
|
+
# them in via the +custom_proxies+ parameter. That way, the middleware will
|
59
|
+
# ignore those IP addresses, and return the one that you want.
|
57
60
|
def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
|
58
61
|
@app = app
|
59
62
|
@check_ip = check_ip_spoofing
|
60
|
-
@proxies =
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
63
|
+
@proxies = if custom_proxies.blank?
|
64
|
+
TRUSTED_PROXIES
|
65
|
+
elsif custom_proxies.respond_to?(:any?)
|
66
|
+
custom_proxies
|
67
|
+
else
|
68
|
+
Array(custom_proxies) + TRUSTED_PROXIES
|
69
|
+
end
|
68
70
|
end
|
69
71
|
|
70
72
|
# Since the IP address may not be needed, we store the object here
|
@@ -80,32 +82,6 @@ module ActionDispatch
|
|
80
82
|
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
|
81
83
|
# is called, this class will calculate the value and then memoize it.
|
82
84
|
class GetIp
|
83
|
-
|
84
|
-
# This constant contains a regular expression that validates every known
|
85
|
-
# form of IP v4 and v6 address, with or without abbreviations, adapted
|
86
|
-
# from {this gist}[https://gist.github.com/gazay/1289635].
|
87
|
-
VALID_IP = %r{
|
88
|
-
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
|
89
|
-
(^(
|
90
|
-
(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
|
91
|
-
(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
|
92
|
-
(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
|
93
|
-
(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
|
94
|
-
(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
|
95
|
-
(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
|
96
|
-
(([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
97
|
-
(([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
98
|
-
(([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
99
|
-
(([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
100
|
-
(([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
101
|
-
(([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
102
|
-
(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
103
|
-
([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
|
104
|
-
(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning
|
105
|
-
(([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
|
106
|
-
)$)
|
107
|
-
}x
|
108
|
-
|
109
85
|
def initialize(env, middleware)
|
110
86
|
@env = env
|
111
87
|
@check_ip = middleware.check_ip
|
@@ -118,7 +94,7 @@ module ActionDispatch
|
|
118
94
|
#
|
119
95
|
# REMOTE_ADDR will be correct if the request is made directly against the
|
120
96
|
# Ruby process, on e.g. Heroku. When the request is proxied by another
|
121
|
-
# server like HAProxy or
|
97
|
+
# server like HAProxy or NGINX, the IP address that made the original
|
122
98
|
# request will be put in an X-Forwarded-For header. If there are multiple
|
123
99
|
# proxies, that header may contain a list of IPs. Other proxy services
|
124
100
|
# set the Client-Ip header instead, so we check that too.
|
@@ -173,12 +149,22 @@ module ActionDispatch
|
|
173
149
|
def ips_from(header)
|
174
150
|
# Split the comma-separated list into an array of strings
|
175
151
|
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
176
|
-
|
177
|
-
|
152
|
+
ips.select do |ip|
|
153
|
+
begin
|
154
|
+
# Only return IPs that are valid according to the IPAddr#new method
|
155
|
+
range = IPAddr.new(ip).to_range
|
156
|
+
# we want to make sure nobody is sneaking a netmask in
|
157
|
+
range.begin == range.end
|
158
|
+
rescue ArgumentError
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
end
|
178
162
|
end
|
179
163
|
|
180
164
|
def filter_proxies(ips)
|
181
|
-
ips.reject
|
165
|
+
ips.reject do |ip|
|
166
|
+
@proxies.any? { |proxy| proxy === ip }
|
167
|
+
end
|
182
168
|
end
|
183
169
|
|
184
170
|
end
|
@@ -5,7 +5,7 @@ module ActionDispatch
|
|
5
5
|
# Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
|
6
6
|
# ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
|
7
7
|
#
|
8
|
-
# The unique request id is either based
|
8
|
+
# The unique request id is either based on the X-Request-Id header in the request, which would typically be generated
|
9
9
|
# by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
|
10
10
|
# header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
|
11
11
|
#
|
@@ -49,7 +49,7 @@ module ActionDispatch
|
|
49
49
|
# reasonably sure that your upgrade is otherwise complete. Additionally,
|
50
50
|
# you should take care to make sure you are not relying on the ability to
|
51
51
|
# decode signed cookies generated by your app in external applications or
|
52
|
-
#
|
52
|
+
# JavaScript before upgrading.
|
53
53
|
#
|
54
54
|
# Note that changing the secret key will invalidate all existing sessions!
|
55
55
|
class CookieStore < Rack::Session::Abstract::ID
|
@@ -42,6 +42,7 @@ module ActionDispatch
|
|
42
42
|
wrapper = ExceptionWrapper.new(env, exception)
|
43
43
|
status = wrapper.status_code
|
44
44
|
env["action_dispatch.exception"] = wrapper.exception
|
45
|
+
env["action_dispatch.original_path"] = env["PATH_INFO"]
|
45
46
|
env["PATH_INFO"] = "/#{status}"
|
46
47
|
response = @exceptions_app.call(env)
|
47
48
|
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
|
@@ -2,69 +2,105 @@ require 'rack/utils'
|
|
2
2
|
require 'active_support/core_ext/uri'
|
3
3
|
|
4
4
|
module ActionDispatch
|
5
|
+
# This middleware returns a file's contents from disk in the body response.
|
6
|
+
# When initialized it can accept an optional 'Cache-Control' header which
|
7
|
+
# will be set when a response containing a file's contents is delivered.
|
8
|
+
#
|
9
|
+
# This middleware will render the file specified in `env["PATH_INFO"]`
|
10
|
+
# where the base path is in the +root+ directory. For example if the +root+
|
11
|
+
# is set to `public/` then a request with `env["PATH_INFO"]` of
|
12
|
+
# `assets/application.js` will return a response with contents of a file
|
13
|
+
# located at `public/assets/application.js` if the file exists. If the file
|
14
|
+
# does not exist a 404 "File not Found" response will be returned.
|
5
15
|
class FileHandler
|
6
16
|
def initialize(root, cache_control)
|
7
17
|
@root = root.chomp('/')
|
8
18
|
@compiled_root = /^#{Regexp.escape(root)}/
|
9
|
-
headers
|
10
|
-
@file_server
|
19
|
+
headers = cache_control && { 'Cache-Control' => cache_control }
|
20
|
+
@file_server = ::Rack::File.new(@root, headers)
|
11
21
|
end
|
12
22
|
|
13
23
|
def match?(path)
|
14
|
-
path =
|
15
|
-
return false unless path
|
16
|
-
|
17
|
-
|
18
|
-
clean_path_info
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
path = URI.parser.unescape(path)
|
25
|
+
return false unless valid_path?(path)
|
26
|
+
|
27
|
+
paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"].map { |v|
|
28
|
+
Rack::Utils.clean_path_info v
|
29
|
+
}
|
30
|
+
|
31
|
+
if match = paths.detect { |p|
|
32
|
+
path = File.join(@root, p.force_encoding('UTF-8'))
|
33
|
+
begin
|
34
|
+
File.file?(path) && File.readable?(path)
|
35
|
+
rescue SystemCallError
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
}
|
40
|
+
return ::Rack::Utils.escape(match)
|
26
41
|
end
|
27
42
|
end
|
28
43
|
|
29
44
|
def call(env)
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
45
|
+
path = env['PATH_INFO']
|
46
|
+
gzip_path = gzip_file_path(path)
|
47
|
+
|
48
|
+
if gzip_path && gzip_encoding_accepted?(env)
|
49
|
+
env['PATH_INFO'] = gzip_path
|
50
|
+
status, headers, body = @file_server.call(env)
|
51
|
+
if status == 304
|
52
|
+
return [status, headers, body]
|
53
|
+
end
|
54
|
+
headers['Content-Encoding'] = 'gzip'
|
55
|
+
headers['Content-Type'] = content_type(path)
|
56
|
+
else
|
57
|
+
status, headers, body = @file_server.call(env)
|
37
58
|
end
|
38
|
-
end
|
39
59
|
|
40
|
-
|
41
|
-
URI.parser.unescape(path)
|
42
|
-
end
|
60
|
+
headers['Vary'] = 'Accept-Encoding' if gzip_path
|
43
61
|
|
44
|
-
|
45
|
-
|
62
|
+
return [status, headers, body]
|
63
|
+
ensure
|
64
|
+
env['PATH_INFO'] = path
|
46
65
|
end
|
47
66
|
|
48
67
|
private
|
68
|
+
def ext
|
69
|
+
::ActionController::Base.default_static_extension
|
70
|
+
end
|
49
71
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
parts = path_info.split PATH_SEPS
|
54
|
-
|
55
|
-
clean = []
|
72
|
+
def content_type(path)
|
73
|
+
::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
74
|
+
end
|
56
75
|
|
57
|
-
|
58
|
-
|
59
|
-
part == '..' ? clean.pop : clean << part
|
76
|
+
def gzip_encoding_accepted?(env)
|
77
|
+
env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/i
|
60
78
|
end
|
61
79
|
|
62
|
-
|
80
|
+
def gzip_file_path(path)
|
81
|
+
can_gzip_mime = content_type(path) =~ /\A(?:text\/|application\/javascript)/
|
82
|
+
gzip_path = "#{path}.gz"
|
83
|
+
if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape(gzip_path)))
|
84
|
+
gzip_path
|
85
|
+
else
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
63
89
|
|
64
|
-
|
65
|
-
|
90
|
+
def valid_path?(path)
|
91
|
+
path.valid_encoding? && !path.include?("\0")
|
92
|
+
end
|
66
93
|
end
|
67
94
|
|
95
|
+
# This middleware will attempt to return the contents of a file's body from
|
96
|
+
# disk in the response. If a file is not found on disk, the request will be
|
97
|
+
# delegated to the application stack. This middleware is commonly initialized
|
98
|
+
# to serve assets from a server's `public/` directory.
|
99
|
+
#
|
100
|
+
# This middleware verifies the path to ensure that only files
|
101
|
+
# living in the root directory can be rendered. A request cannot
|
102
|
+
# produce a directory traversal using this middleware. Only 'GET' and 'HEAD'
|
103
|
+
# requests will result in a file being returned.
|
68
104
|
class Static
|
69
105
|
def initialize(app, path, cache_control=nil)
|
70
106
|
@app = app
|
@@ -1,25 +1,27 @@
|
|
1
|
-
<%
|
2
|
-
|
3
|
-
<div class="
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
<% @source_extracts.each_with_index do |source_extract, index| %>
|
2
|
+
<% if source_extract[:code] %>
|
3
|
+
<div class="source <%="hidden" if @show_source_idx != index%>" id="frame-source-<%=index%>">
|
4
|
+
<div class="info">
|
5
|
+
Extracted source (around line <strong>#<%= source_extract[:line_number] %></strong>):
|
6
|
+
</div>
|
7
|
+
<div class="data">
|
8
|
+
<table cellpadding="0" cellspacing="0" class="lines">
|
9
|
+
<tr>
|
10
|
+
<td>
|
11
|
+
<pre class="line_numbers">
|
12
|
+
<% source_extract[:code].each_key do |line_number| %>
|
12
13
|
<span><%= line_number -%></span>
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
<% end %>
|
15
|
+
</pre>
|
16
|
+
</td>
|
16
17
|
<td width="100%">
|
17
18
|
<pre>
|
18
|
-
<%
|
19
|
+
<% source_extract[:code].each do |line, source| -%><div class="line<%= " active" if line == source_extract[:line_number] -%>"><%= source -%></div><% end -%>
|
19
20
|
</pre>
|
20
21
|
</td>
|
21
|
-
|
22
|
-
|
23
|
-
</div>
|
24
|
-
</div>
|
22
|
+
</tr>
|
23
|
+
</table>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
25
27
|
<% end %>
|
@@ -1,9 +1,4 @@
|
|
1
|
-
<%
|
2
|
-
traces = { "Application Trace" => @application_trace,
|
3
|
-
"Framework Trace" => @framework_trace,
|
4
|
-
"Full Trace" => @full_trace }
|
5
|
-
names = traces.keys
|
6
|
-
%>
|
1
|
+
<% names = @traces.keys %>
|
7
2
|
|
8
3
|
<p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p>
|
9
4
|
|
@@ -16,9 +11,42 @@
|
|
16
11
|
<a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
|
17
12
|
<% end %>
|
18
13
|
|
19
|
-
<% traces.each do |name, trace| %>
|
20
|
-
<div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name ==
|
21
|
-
<pre><code
|
14
|
+
<% @traces.each do |name, trace| %>
|
15
|
+
<div id="<%= name.gsub(/\s/, '-') %>" style="display: <%= (name == @trace_to_show) ? 'block' : 'none' %>;">
|
16
|
+
<pre><code><% trace.each do |frame| %><a class="trace-frames" data-frame-id="<%= frame[:id] %>" href="#"><%= frame[:trace] %></a><br><% end %></code></pre>
|
22
17
|
</div>
|
23
18
|
<% end %>
|
19
|
+
|
20
|
+
<script type="text/javascript">
|
21
|
+
var traceFrames = document.getElementsByClassName('trace-frames');
|
22
|
+
var selectedFrame, currentSource = document.getElementById('frame-source-0');
|
23
|
+
|
24
|
+
// Add click listeners for all stack frames
|
25
|
+
for (var i = 0; i < traceFrames.length; i++) {
|
26
|
+
traceFrames[i].addEventListener('click', function(e) {
|
27
|
+
e.preventDefault();
|
28
|
+
var target = e.target;
|
29
|
+
var frame_id = target.dataset.frameId;
|
30
|
+
|
31
|
+
if (selectedFrame) {
|
32
|
+
selectedFrame.className = selectedFrame.className.replace("selected", "");
|
33
|
+
}
|
34
|
+
|
35
|
+
target.className += " selected";
|
36
|
+
selectedFrame = target;
|
37
|
+
|
38
|
+
// Change the extracted source code
|
39
|
+
changeSourceExtract(frame_id);
|
40
|
+
});
|
41
|
+
|
42
|
+
function changeSourceExtract(frame_id) {
|
43
|
+
var el = document.getElementById('frame-source-' + frame_id);
|
44
|
+
if (currentSource && el) {
|
45
|
+
currentSource.className += " hidden";
|
46
|
+
el.className = el.className.replace(" hidden", "");
|
47
|
+
currentSource = el;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
</script>
|
24
52
|
</div>
|
@@ -1,15 +1,9 @@
|
|
1
|
-
<%
|
2
|
-
traces = { "Application Trace" => @application_trace,
|
3
|
-
"Framework Trace" => @framework_trace,
|
4
|
-
"Full Trace" => @full_trace }
|
5
|
-
%>
|
6
|
-
|
7
1
|
Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %>
|
8
2
|
|
9
|
-
<% traces.each do |name, trace| %>
|
3
|
+
<% @traces.each do |name, trace| %>
|
10
4
|
<% if trace.any? %>
|
11
5
|
<%= name %>
|
12
|
-
<%= trace.join("\n") %>
|
6
|
+
<%= trace.map { |t| t[:trace] }.join("\n") %>
|
13
7
|
|
14
8
|
<% end %>
|
15
9
|
<% end %>
|