1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# frozen_string_literal: true
module ActionDispatch
module Http
# Provides access to the request's HTTP headers from the environment.
#
# env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
# headers = ActionDispatch::Http::Headers.from_hash(env)
# headers["Content-Type"] # => "text/plain"
# headers["User-Agent"] # => "curl/7.43.0"
#
# Also note that when headers are mapped to CGI-like variables by the Rack
# server, both dashes and underscores are converted to underscores. This
# ambiguity cannot be resolved at this stage anymore. Both underscores and
# dashes have to be interpreted as if they were originally sent as dashes.
#
# # GET / HTTP/1.1
# # ...
# # User-Agent: curl/7.43.0
# # X_Custom_Header: token
#
# headers["X_Custom_Header"] # => nil
# headers["X-Custom-Header"] # => "token"
class Headers
CGI_VARIABLES = Set.new(%W[
AUTH_TYPE
CONTENT_LENGTH
CONTENT_TYPE
GATEWAY_INTERFACE
HTTPS
PATH_INFO
PATH_TRANSLATED
QUERY_STRING
REMOTE_ADDR
REMOTE_HOST
REMOTE_IDENT
REMOTE_USER
REQUEST_METHOD
SCRIPT_NAME
SERVER_NAME
SERVER_PORT
SERVER_PROTOCOL
SERVER_SOFTWARE
]).freeze
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
include Enumerable
def self.from_hash(hash)
new ActionDispatch::Request.new hash
end
def initialize(request) # :nodoc:
@req = request
end
# Returns the value for the given key mapped to @env.
def [](key)
@req.get_header env_name(key)
end
# Sets the given value for the key mapped to @env.
def []=(key, value)
@req.set_header env_name(key), value
end
# Add a value to a multivalued header like Vary or Accept-Encoding.
def add(key, value)
@req.add_header env_name(key), value
end
def key?(key)
@req.has_header? env_name(key)
end
alias :include? :key?
DEFAULT = Object.new # :nodoc:
# Returns the value for the given key mapped to @env.
#
# If the key is not found and an optional code block is not provided,
# raises a <tt>KeyError</tt> exception.
#
# If the code block is provided, then it will be run and
# its result returned.
def fetch(key, default = DEFAULT)
@req.fetch_header(env_name(key)) do
return default unless default == DEFAULT
return yield if block_given?
raise KeyError, key
end
end
def each(&block)
@req.each_header(&block)
end
# Returns a new Http::Headers instance containing the contents of
# <tt>headers_or_env</tt> and the original instance.
def merge(headers_or_env)
headers = @req.dup.headers
headers.merge!(headers_or_env)
headers
end
# Adds the contents of <tt>headers_or_env</tt> to original instance
# entries; duplicate keys are overwritten with the values from
# <tt>headers_or_env</tt>.
def merge!(headers_or_env)
headers_or_env.each do |key, value|
@req.set_header env_name(key), value
end
end
def env; @req.env.dup; end
private
# Converts an HTTP header name to an environment variable name if it is
# not contained within the headers hash.
def env_name(key)
key = key.to_s
if HTTP_HEADER.match?(key)
key = key.upcase.tr("-", "_")
key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
end
key
end
end
end
end
|