1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

use a request object to access info from env in GetIp

again, we want to hide the contents of `env` from the implementation.
Allocate a request object to access the contents of env, but save
allocations due to string literal allocations when accessing the env
hash.
This commit is contained in:
Aaron Patterson 2015-08-06 16:20:47 -07:00
parent 6716ad555a
commit 07b2ff03d0
2 changed files with 19 additions and 11 deletions

View file

@ -34,7 +34,9 @@ module ActionDispatch
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR
].freeze
ENV_METHODS.each do |env|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
@ -225,6 +227,10 @@ module ActionDispatch
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
def remote_ip=(remote_ip)
@env["action_dispatch.remote_ip".freeze] = remote_ip
end
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
# Returns the unique request id, which is based on either the X-Request-Id header that can

View file

@ -74,16 +74,17 @@ module ActionDispatch
# requests. For those requests that do need to know the IP, the
# GetIp#calculate_ip method will calculate the memoized client IP address.
def call(env)
env["action_dispatch.remote_ip"] = GetIp.new(env, check_ip, proxies)
@app.call(env)
req = ActionDispatch::Request.new env
req.remote_ip = GetIp.new(req, check_ip, proxies)
@app.call(req.env)
end
# The GetIp class exists as a way to defer processing of the request data
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
# is called, this class will calculate the value and then memoize it.
class GetIp
def initialize(env, check_ip, proxies)
@env = env
def initialize(req, check_ip, proxies)
@req = req
@check_ip = check_ip
@proxies = proxies
end
@ -108,11 +109,11 @@ module ActionDispatch
# the last address left, which was presumably set by one of those proxies.
def calculate_ip
# Set by the Rack web server, this is a single value.
remote_addr = ips_from('REMOTE_ADDR').last
remote_addr = ips_from(@req.remote_addr).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = ips_from('HTTP_CLIENT_IP').reverse
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
client_ips = ips_from(@req.client_ip).reverse
forwarded_ips = ips_from(@req.x_forwarded_for).reverse
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
# If they are both set, it means that this request passed through two
@ -123,8 +124,8 @@ module ActionDispatch
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " +
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " +
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
end
# We assume these things about the IP headers:
@ -147,8 +148,9 @@ module ActionDispatch
protected
def ips_from(header)
return [] unless header
# Split the comma-separated list into an array of strings
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
ips = header.strip.split(/[,\s]+/)
ips.select do |ip|
begin
# Only return IPs that are valid according to the IPAddr#new method