moesif_rack 1.4.18 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -1
- data/lib/moesif_rack/app_config.rb +87 -120
- data/lib/moesif_rack/client_ip.rb +61 -102
- data/lib/moesif_rack/governance_rules.rb +483 -0
- data/lib/moesif_rack/moesif_helpers.rb +57 -6
- data/lib/moesif_rack/moesif_middleware.rb +178 -140
- data/lib/moesif_rack/regex_config_helper.rb +96 -104
- data/lib/moesif_rack/update_company.rb +44 -48
- data/lib/moesif_rack/update_user.rb +44 -48
- data/moesif_capture_outgoing/httplog/adapters/net_http.rb +18 -21
- data/moesif_capture_outgoing/httplog/http_log.rb +54 -85
- data/moesif_capture_outgoing/httplog.rb +2 -2
- data/test/config_example.json +1477 -0
- data/test/govrule_example.json +20 -0
- data/test/test_governance_rules.rb +212 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed6afe67a6d8b2aa3c05b23a704d2ccfaadf9afdb078e9d66998f69fd7ed3f45
|
4
|
+
data.tar.gz: 3cb56517e37769f179aabf0f1621c0969259558a46fa8594c017f85098f77423
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91987307ffe2aaf10763dc39e0dce6acc1d91ed8f614144811b2a0d7c579b06d4f4f112cf6de10a983153aa81f45d5dac3450be5c38658af71728656d4426d5c
|
7
|
+
data.tar.gz: f6a677544fd723f0ab6842e5496586d93f22b8ddf77fc5ceb1c4fb98f770ce5443461a66f6c744f03b03a428acfa1c55c669d4f4f09841191c99842a40e614f6
|
data/README.md
CHANGED
@@ -255,8 +255,17 @@ Optional. Boolean. Default false. If true, it will print out debug messages. In
|
|
255
255
|
|
256
256
|
Optional. Boolean. Default true. If false, will not log request and response body to Moesif.
|
257
257
|
|
258
|
+
#### __`batch_size`__
|
259
|
+
Optional. int, default 200, Maximum batch size when sending to Moesif.
|
260
|
+
|
261
|
+
#### __`batch_max_time`__
|
262
|
+
Optional. int in seconds Default 2. This is the maximum wait time (approximately) before triggering flushing of the queue and sending to Moesif.
|
263
|
+
|
264
|
+
#### __`event_queue_size`__
|
265
|
+
Optional. int, Default 1000, Maximum number of events to hold in queue before sending to Moesif. In case of network issues when not able to connect/send event to Moesif, skips adding new to event to queue to prevent memory overflow.
|
266
|
+
|
258
267
|
#### __`capture_outgoing_requests`__
|
259
|
-
Optional.
|
268
|
+
Optional. Boolean, Default `false`. Set to `true` to capture all outgoing API calls from your app to third parties like Stripe, Github or to your own dependencies while using [Net::HTTP](https://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html) package. The options below is applied to outgoing API calls. When the request is outgoing, for options functions that take request and response as input arguments, the request and response objects passed in are [Request](https://www.rubydoc.info/stdlib/net/Net/HTTPRequest) request and [Response](https://www.rubydoc.info/stdlib/net/Net/HTTPResponse) response objects.
|
260
269
|
|
261
270
|
|
262
271
|
##### __`identify_user_outgoing`__
|
@@ -3,134 +3,101 @@ require 'json'
|
|
3
3
|
require 'time'
|
4
4
|
require 'zlib'
|
5
5
|
require 'stringio'
|
6
|
-
require_relative './moesif_helpers
|
7
|
-
require_relative './regex_config_helper
|
6
|
+
require_relative './moesif_helpers'
|
7
|
+
require_relative './regex_config_helper'
|
8
8
|
|
9
9
|
class AppConfig
|
10
|
+
def initialize(debug)
|
11
|
+
@debug = debug
|
12
|
+
@moesif_helpers = MoesifHelpers.new(debug)
|
13
|
+
@regex_config_helper = RegexConfigHelper.new(debug)
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def get_config(api_controller)
|
17
|
+
# Get Application Config
|
18
|
+
config_api_response = api_controller.get_app_config
|
19
|
+
@moesif_helpers.log_debug('new config downloaded')
|
20
|
+
@moesif_helpers.log_debug(config_api_response.to_s)
|
21
|
+
config_api_response
|
22
|
+
rescue MoesifApi::APIException => e
|
23
|
+
if e.response_code.between?(401, 403)
|
24
|
+
@moesif_helpers.log_debug 'Unauthorized access getting application configuration. Please check your Appplication Id.'
|
15
25
|
end
|
26
|
+
@moesif_helpers.log_debug 'Error getting application configuration, with status code:'
|
27
|
+
@moesif_helpers.log_debug e.response_code
|
28
|
+
rescue StandardError => e
|
29
|
+
@moesif_helpers.log_debug e.to_s
|
30
|
+
end
|
16
31
|
|
17
|
-
|
18
|
-
|
19
|
-
begin
|
20
|
-
config_api_response = api_controller.get_app_config()
|
21
|
-
@moesif_helpers.log_debug("new config downloaded")
|
22
|
-
@moesif_helpers.log_debug(config_api_response.to_s)
|
23
|
-
return config_api_response
|
24
|
-
rescue MoesifApi::APIException => e
|
25
|
-
if e.response_code.between?(401, 403)
|
26
|
-
@moesif_helpers.log_debug 'Unauthorized access getting application configuration. Please check your Appplication Id.'
|
27
|
-
end
|
28
|
-
@moesif_helpers.log_debug 'Error getting application configuration, with status code:'
|
29
|
-
@moesif_helpers.log_debug e.response_code
|
30
|
-
rescue => e
|
31
|
-
@moesif_helpers.log_debug e.to_s
|
32
|
-
end
|
33
|
-
rescue
|
34
|
-
end
|
32
|
+
def parse_configuration(config_api_response)
|
33
|
+
# Parse configuration object and return Etag, sample rate and last updated time
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
# Rails return gzipped compressed response body, so decompressing it and getting JSON response body
|
40
|
-
response_body = decompress_gzip_body(config_api_response)
|
41
|
-
@moesif_helpers.log_debug(response_body.to_s)
|
42
|
-
|
43
|
-
# Check if response body is not nil
|
44
|
-
if !response_body.nil? then
|
45
|
-
# Return Etag, sample rate and last updated time
|
46
|
-
return response_body, config_api_response.headers[:x_moesif_config_etag], Time.now.utc
|
47
|
-
else
|
48
|
-
@moesif_helpers.log_debug 'Response body is nil, assuming default behavior'
|
49
|
-
# Response body is nil, so assuming default behavior
|
50
|
-
return nil, nil, Time.now.utc
|
51
|
-
end
|
52
|
-
rescue => exception
|
53
|
-
@moesif_helpers.log_debug 'Error while parsing the configuration object, assuming default behavior'
|
54
|
-
@moesif_helpers.log_debug exception.to_s
|
55
|
-
# Assuming default behavior
|
56
|
-
return nil, nil, Time.now.utc
|
57
|
-
end
|
58
|
-
end
|
35
|
+
# Rails return gzipped compressed response body, so decompressing it and getting JSON response body
|
36
|
+
response_body = @moesif_helpers.decompress_gzip_body(config_api_response)
|
37
|
+
@moesif_helpers.log_debug(response_body.to_json)
|
59
38
|
|
60
|
-
|
61
|
-
|
62
|
-
begin
|
63
|
-
# Check if response body is not nil
|
64
|
-
if !config_api_response.nil? then
|
65
|
-
@moesif_helpers.log_debug("Getting sample rate for user #{user_id} company #{company_id}")
|
66
|
-
@moesif_helpers.log_debug(config_api_response.to_s)
|
67
|
-
|
68
|
-
# Get Regex Sampling rate
|
69
|
-
regex_config = config_api_response.fetch('regex_config', nil)
|
70
|
-
|
71
|
-
if !regex_config.nil? and !event_model.nil?
|
72
|
-
config_mapping = @regex_config_helper.prepare_config_mapping(event_model)
|
73
|
-
regex_sample_rate = @regex_config_helper.fetch_sample_rate_on_regex_match(regex_config, config_mapping)
|
74
|
-
if !regex_sample_rate.nil?
|
75
|
-
return regex_sample_rate
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Get user sample rate object
|
80
|
-
user_sample_rate = config_api_response.fetch('user_sample_rate', nil)
|
81
|
-
|
82
|
-
# Get company sample rate object
|
83
|
-
company_sample_rate = config_api_response.fetch('company_sample_rate', nil)
|
84
|
-
|
85
|
-
# Get sample rate for the user if exist
|
86
|
-
if !user_id.nil? && !user_sample_rate.nil? && user_sample_rate.key?(user_id)
|
87
|
-
return user_sample_rate.fetch(user_id)
|
88
|
-
end
|
89
|
-
|
90
|
-
# Get sample rate for the company if exist
|
91
|
-
if !company_id.nil? && !company_sample_rate.nil? && company_sample_rate.key?(company_id)
|
92
|
-
return company_sample_rate.fetch(company_id)
|
93
|
-
end
|
94
|
-
|
95
|
-
# Return sample rate
|
96
|
-
return config_api_response.fetch('sample_rate', 100)
|
97
|
-
else
|
98
|
-
@moesif_helpers.log_debug 'Assuming default behavior as response body is nil - '
|
99
|
-
return 100
|
100
|
-
end
|
101
|
-
rescue => exception
|
102
|
-
@moesif_helpers.log_debug 'Error while geting sampling percentage, assuming default behavior'
|
103
|
-
@moesif_helpers.log_debug exception.to_s
|
104
|
-
return 100
|
105
|
-
end
|
106
|
-
end
|
39
|
+
# Check if response body is not nil
|
40
|
+
return response_body, config_api_response.headers[:x_moesif_config_etag], Time.now.utc unless response_body.nil?
|
107
41
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
@moesif_helpers.log_debug 'Error while decompressing the response body'
|
128
|
-
@moesif_helpers.log_debug exception.to_s
|
129
|
-
return nil
|
130
|
-
end
|
131
|
-
end
|
42
|
+
# Return Etag, sample rate and last updated time
|
43
|
+
|
44
|
+
@moesif_helpers.log_debug 'Response body is nil, assuming default behavior'
|
45
|
+
# Response body is nil, so assuming default behavior
|
46
|
+
[nil, nil, Time.now.utc]
|
47
|
+
rescue StandardError => e
|
48
|
+
@moesif_helpers.log_debug 'Error while parsing the configuration object, assuming default behavior'
|
49
|
+
@moesif_helpers.log_debug e.to_s
|
50
|
+
# Assuming default behavior
|
51
|
+
[nil, nil, Time.now.utc]
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_sampling_percentage(event_model, config_api_response, user_id, company_id)
|
55
|
+
# Get sampling percentage
|
56
|
+
|
57
|
+
# Check if response body is not nil
|
58
|
+
if !config_api_response.nil?
|
59
|
+
@moesif_helpers.log_debug("Getting sample rate for user #{user_id} company #{company_id}")
|
60
|
+
@moesif_helpers.log_debug(config_api_response.to_s)
|
132
61
|
|
133
|
-
|
134
|
-
|
62
|
+
# Get Regex Sampling rate
|
63
|
+
regex_config = config_api_response.fetch('regex_config', nil)
|
64
|
+
|
65
|
+
if !regex_config.nil? and !event_model.nil?
|
66
|
+
config_mapping = @regex_config_helper.prepare_config_mapping(event_model)
|
67
|
+
regex_sample_rate = @regex_config_helper.fetch_sample_rate_on_regex_match(regex_config,
|
68
|
+
config_mapping)
|
69
|
+
return regex_sample_rate unless regex_sample_rate.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get user sample rate object
|
73
|
+
user_sample_rate = config_api_response.fetch('user_sample_rate', nil)
|
74
|
+
|
75
|
+
# Get company sample rate object
|
76
|
+
company_sample_rate = config_api_response.fetch('company_sample_rate', nil)
|
77
|
+
|
78
|
+
# Get sample rate for the user if exist
|
79
|
+
if !user_id.nil? && !user_sample_rate.nil? && user_sample_rate.key?(user_id)
|
80
|
+
return user_sample_rate.fetch(user_id)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get sample rate for the company if exist
|
84
|
+
if !company_id.nil? && !company_sample_rate.nil? && company_sample_rate.key?(company_id)
|
85
|
+
return company_sample_rate.fetch(company_id)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return sample rate
|
89
|
+
config_api_response.fetch('sample_rate', 100)
|
90
|
+
else
|
91
|
+
@moesif_helpers.log_debug 'Assuming default behavior as response body is nil - '
|
92
|
+
100
|
135
93
|
end
|
94
|
+
rescue StandardError => e
|
95
|
+
@moesif_helpers.log_debug 'Error while geting sampling percentage, assuming default behavior'
|
96
|
+
@moesif_helpers.log_debug e.to_s
|
97
|
+
100
|
98
|
+
end
|
99
|
+
|
100
|
+
def calculate_weight(sample_rate)
|
101
|
+
sample_rate == 0 ? 1 : (100 / sample_rate).floor
|
102
|
+
end
|
136
103
|
end
|
@@ -1,119 +1,78 @@
|
|
1
|
-
|
2
1
|
def is_ip?(value)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
ipv4 = /^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/
|
3
|
+
ipv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
|
4
|
+
# We use !! to convert the return value to a boolean
|
5
|
+
!!(value =~ ipv4 or value =~ ipv6)
|
7
6
|
end
|
8
7
|
|
9
8
|
def get_client_ip_from_x_forwarded_for(value)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
if splitted.length == 2
|
32
|
-
forwardedIps << splitted.first
|
33
|
-
end
|
34
|
-
end
|
35
|
-
forwardedIps << e
|
36
|
-
end
|
37
|
-
|
38
|
-
# Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650).
|
39
|
-
# Therefore taking the left-most IP address that is not unknown
|
40
|
-
# A Squid configuration directive can also set the value to "unknown" (http://www.squid-cache.org/Doc/config/forwarded_for/)
|
41
|
-
return forwardedIps.find {|e| is_ip?(e) }
|
42
|
-
end
|
43
|
-
rescue
|
44
|
-
return value.encode('utf-8')
|
9
|
+
value = value.encode('utf-8')
|
10
|
+
|
11
|
+
return nil if value.to_s.empty?
|
12
|
+
|
13
|
+
if !value.instance_of?(String)
|
14
|
+
puts('Expected a string, got - ' + value.class.to_s)
|
15
|
+
else
|
16
|
+
# x-forwarded-for may return multiple IP addresses in the format:
|
17
|
+
# "client IP, proxy 1 IP, proxy 2 IP"
|
18
|
+
# Therefore, the right-most IP address is the IP address of the most recent proxy
|
19
|
+
# and the left-most IP address is the IP address of the originating client.
|
20
|
+
# source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
|
21
|
+
# Azure Web App's also adds a port for some reason, so we'll only use the first part (the IP)
|
22
|
+
forwardedIps = []
|
23
|
+
|
24
|
+
value.gsub(/\s+/, '').split(',').each do |e|
|
25
|
+
if e.include?(':')
|
26
|
+
splitted = e.split(':')
|
27
|
+
forwardedIps << splitted.first if splitted.length == 2
|
28
|
+
end
|
29
|
+
forwardedIps << e
|
45
30
|
end
|
31
|
+
|
32
|
+
# Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650).
|
33
|
+
# Therefore taking the left-most IP address that is not unknown
|
34
|
+
# A Squid configuration directive can also set the value to "unknown" (http://www.squid-cache.org/Doc/config/forwarded_for/)
|
35
|
+
forwardedIps.find { |e| is_ip?(e) }
|
36
|
+
end
|
37
|
+
rescue StandardError
|
38
|
+
value.encode('utf-8')
|
46
39
|
end
|
47
40
|
|
48
41
|
def get_client_address(env)
|
49
|
-
|
50
|
-
|
51
|
-
if env.key?('HTTP_X_CLIENT_IP')
|
52
|
-
if is_ip?(env['HTTP_X_CLIENT_IP'])
|
53
|
-
return env['HTTP_X_CLIENT_IP']
|
54
|
-
end
|
55
|
-
end
|
42
|
+
# Standard headers used by Amazon EC2, Heroku, and others.
|
43
|
+
return env['HTTP_X_CLIENT_IP'] if env.key?('HTTP_X_CLIENT_IP') && is_ip?(env['HTTP_X_CLIENT_IP'])
|
56
44
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
45
|
+
# Load-balancers (AWS ELB) or proxies.
|
46
|
+
if env.key?('HTTP_X_FORWARDED_FOR')
|
47
|
+
xForwardedFor = get_client_ip_from_x_forwarded_for(env['HTTP_X_FORWARDED_FOR'])
|
48
|
+
return xForwardedFor if is_ip?(xForwardedFor)
|
49
|
+
end
|
64
50
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
if is_ip?(env['HTTP_CF_CONNECTING_IP'])
|
70
|
-
return env['HTTP_CF_CONNECTING_IP']
|
71
|
-
end
|
72
|
-
end
|
51
|
+
# Cloudflare.
|
52
|
+
# @see https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
|
53
|
+
# CF-Connecting-IP - applied to every request to the origin.
|
54
|
+
return env['HTTP_CF_CONNECTING_IP'] if env.key?('HTTP_CF_CONNECTING_IP') && is_ip?(env['HTTP_CF_CONNECTING_IP'])
|
73
55
|
|
74
|
-
|
75
|
-
|
76
|
-
if is_ip?(env['HTTP_TRUE_CLIENT_IP'])
|
77
|
-
return env['HTTP_TRUE_CLIENT_IP']
|
78
|
-
end
|
79
|
-
end
|
56
|
+
# Akamai and Cloudflare: True-Client-IP.
|
57
|
+
return env['HTTP_TRUE_CLIENT_IP'] if env.key?('HTTP_TRUE_CLIENT_IP') && is_ip?(env['HTTP_TRUE_CLIENT_IP'])
|
80
58
|
|
81
|
-
|
82
|
-
|
83
|
-
if is_ip?(env['HTTP_X_REAL_IP'])
|
84
|
-
return env['HTTP_X_REAL_IP']
|
85
|
-
end
|
86
|
-
end
|
59
|
+
# Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies.
|
60
|
+
return env['HTTP_X_REAL_IP'] if env.key?('HTTP_X_REAL_IP') && is_ip?(env['HTTP_X_REAL_IP'])
|
87
61
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
end
|
62
|
+
# (Rackspace LB and Riverbed's Stingray)
|
63
|
+
# http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address
|
64
|
+
# https://splash.riverbed.com/docs/DOC-1926
|
65
|
+
if env.key?('HTTP_X_CLUSTER_CLIENT_IP') && is_ip?(env['HTTP_X_CLUSTER_CLIENT_IP'])
|
66
|
+
return env['HTTP_X_CLUSTER_CLIENT_IP']
|
67
|
+
end
|
96
68
|
|
97
|
-
|
98
|
-
if is_ip?(env['HTTP_X_FORWARDED'])
|
99
|
-
return env['HTTP_X_FORWARDED']
|
100
|
-
end
|
101
|
-
end
|
69
|
+
return env['HTTP_X_FORWARDED'] if env.key?('HTTP_X_FORWARDED') && is_ip?(env['HTTP_X_FORWARDED'])
|
102
70
|
|
103
|
-
|
104
|
-
if is_ip?(env['HTTP_FORWARDED_FOR'])
|
105
|
-
return env['HTTP_FORWARDED_FOR']
|
106
|
-
end
|
107
|
-
end
|
71
|
+
return env['HTTP_FORWARDED_FOR'] if env.key?('HTTP_FORWARDED_FOR') && is_ip?(env['HTTP_FORWARDED_FOR'])
|
108
72
|
|
109
|
-
|
110
|
-
if is_ip?(env['HTTP_FORWARDED'])
|
111
|
-
return env['HTTP_FORWARDED']
|
112
|
-
end
|
113
|
-
end
|
73
|
+
return env['HTTP_FORWARDED'] if env.key?('HTTP_FORWARDED') && is_ip?(env['HTTP_FORWARDED'])
|
114
74
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
75
|
+
env['REMOTE_ADDR']
|
76
|
+
rescue StandardError
|
77
|
+
env['REMOTE_ADDR']
|
78
|
+
end
|