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 132 133 134
|
# frozen_string_literal: true
# :markup: markdown
module ActionDispatch
module Http
# # Action Dispatch HTTP Headers
#
# 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
# `KeyError` 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
# `headers_or_env` and the original instance.
def merge(headers_or_env)
headers = @req.dup.headers
headers.merge!(headers_or_env)
headers
end
# Adds the contents of `headers_or_env` to original instance entries; duplicate
# keys are overwritten with the values from `headers_or_env`.
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
key.tr!("-", "_")
key.prepend("HTTP_") unless CGI_VARIABLES.include?(key)
end
key
end
end
end
end
|