Class: Rack::Cloudflare::Headers

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/cloudflare/headers.rb

Constant Summary collapse

NAMES =
%w[
  HTTP_CF_IPCOUNTRY
  HTTP_CF_CONNECTING_IP
  HTTP_CF_RAY
  HTTP_CF_VISITOR
].freeze
STANDARD =
%w[
  HTTP_X_FORWARDED_FOR
  HTTP_X_FORWARDED_PROTO
  REMOTE_ADDR
].freeze
ALL =
(NAMES + STANDARD).freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(headers) ⇒ Headers

Returns a new instance of Headers.



39
40
41
# File 'lib/rack/cloudflare/headers.rb', line 39

def initialize(headers)
  @headers = headers
end

Class Attribute Details

.backupObject

Returns the value of attribute backup.



28
29
30
# File 'lib/rack/cloudflare/headers.rb', line 28

def backup
  @backup
end

.original_forwarded_forObject

Returns the value of attribute original_forwarded_for.



28
29
30
# File 'lib/rack/cloudflare/headers.rb', line 28

def original_forwarded_for
  @original_forwarded_for
end

.original_remote_addrObject

Returns the value of attribute original_remote_addr.



28
29
30
# File 'lib/rack/cloudflare/headers.rb', line 28

def original_remote_addr
  @original_remote_addr
end

Class Method Details

.trusted?(headers) ⇒ Boolean

Returns:

  • (Boolean)


30
31
32
# File 'lib/rack/cloudflare/headers.rb', line 30

def trusted?(headers)
  Headers.new(headers).trusted?
end

Instance Method Details

#backup_headersObject



84
85
86
87
88
89
90
91
# File 'lib/rack/cloudflare/headers.rb', line 84

def backup_headers
  return {} unless Headers.backup

  {}.tap do |headers|
    headers[Headers.original_remote_addr]   = @headers[REMOTE_ADDR]
    headers[Headers.original_forwarded_for] = @headers[HTTP_X_FORWARDED_FOR]
  end
end

#connecting_ipObject

“CF-Connecting-IP: A.B.C.D”



49
50
51
# File 'lib/rack/cloudflare/headers.rb', line 49

def connecting_ip
  @connecting_ip ||= IPs.parse(@headers[HTTP_CF_CONNECTING_IP]).first
end

#forwarded_forObject

“X-Forwarded-For: A.B.C.D” “X-Forwarded-For: A.B.C.D



55
56
57
# File 'lib/rack/cloudflare/headers.rb', line 55

def forwarded_for
  @forwarded_for ||= IPs.parse(@headers[HTTP_X_FORWARDED_FOR])
end

#forwarded_protoObject

“X-Forwarded-Proto: https”



60
61
62
# File 'lib/rack/cloudflare/headers.rb', line 60

def forwarded_proto
  @headers[HTTP_X_FORWARDED_PROTO]
end

#has?(header) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/rack/cloudflare/headers.rb', line 126

def has?(header)
  @headers.key?(header)
end

#ip_countryObject

“Cf-Ipcountry: US”



44
45
46
# File 'lib/rack/cloudflare/headers.rb', line 44

def ip_country
  @headers.fetch(HTTP_CF_IPCOUNTRY, 'XX')
end

#rayObject

“Cf-Ray: 230b030023ae2822-SJC”



65
66
67
# File 'lib/rack/cloudflare/headers.rb', line 65

def ray
  @headers[HTTP_CF_RAY]
end

#remote_addrObject



75
76
77
# File 'lib/rack/cloudflare/headers.rb', line 75

def remote_addr
  @remote_addr ||= IPs.parse(@headers[REMOTE_ADDR]).first
end

#rewriteObject



122
123
124
# File 'lib/rack/cloudflare/headers.rb', line 122

def rewrite
  @headers.merge(rewritten_headers)
end

#rewritten_headersObject



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rack/cloudflare/headers.rb', line 93

def rewritten_headers
  # Only rewrites headers if it's a Cloudflare request
  return {} unless trusted?

  {}.tap do |headers|
    headers.merge! backup_headers

    # Overwrite the original remote IP based on
    # Cloudflare's specified original remote IP
    headers[REMOTE_ADDR] = connecting_ip.to_s if connecting_ip

    # Add HTTP_X_FORWARDED_FOR if it wasn't present.
    # Cloudflare will already have modified the header if
    # it was present in the original request.
    # See: https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
    headers[HTTP_X_FORWARDED_FOR] = "#{connecting_ip}, #{remote_addr}" if forwarded_for.none?
  end
end

#rewritten_target_headersObject



118
119
120
# File 'lib/rack/cloudflare/headers.rb', line 118

def rewritten_target_headers
  target_headers.merge(rewritten_headers)
end

#target_headersObject



114
115
116
# File 'lib/rack/cloudflare/headers.rb', line 114

def target_headers
  @headers.select { |k, _| ALL.include? k }
end

#trusted?Boolean

Indicates if the headers passed through Cloudflare

Returns:

  • (Boolean)


80
81
82
# File 'lib/rack/cloudflare/headers.rb', line 80

def trusted?
  IPs.list.any? { |range| range.include? remote_addr }
end

#visitorObject

“Cf-Visitor: { "scheme":"https"}”



70
71
72
73
# File 'lib/rack/cloudflare/headers.rb', line 70

def visitor
  return unless has?(HTTP_CF_VISITOR)
  ::JSON.parse @headers[HTTP_CF_VISITOR]
end