module Rack::Request::Helpers
def GET
def GET if get_header(RACK_REQUEST_QUERY_STRING) == query_string get_header(RACK_REQUEST_QUERY_HASH) else query_hash = parse_query(query_string, '&') set_header(RACK_REQUEST_QUERY_STRING, query_string) set_header(RACK_REQUEST_QUERY_HASH, query_hash) end end
def POST
This method support both application/x-www-form-urlencoded and
Returns the data received in the request body.
def POST if error = get_header(RACK_REQUEST_FORM_ERROR) raise error.class, error.message, cause: error.cause end begin rack_input = get_header(RACK_INPUT) # If the form hash was already memoized: if form_hash = get_header(RACK_REQUEST_FORM_HASH) # And it was memoized from the same input: if get_header(RACK_REQUEST_FORM_INPUT).equal?(rack_input) return form_hash end end # Otherwise, figure out how to parse the input: if rack_input.nil? set_header RACK_REQUEST_FORM_INPUT, nil set_header(RACK_REQUEST_FORM_HASH, {}) elsif form_data? || parseable_data? unless set_header(RACK_REQUEST_FORM_HASH, parse_multipart) form_vars = get_header(RACK_INPUT).read # Fix for Safari Ajax postings that always append \0 # form_vars.sub!(/\0\z/, '') # performance replacement: form_vars.slice!(-1) if form_vars.end_with?("\0") set_header RACK_REQUEST_FORM_VARS, form_vars set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&') end set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT) get_header RACK_REQUEST_FORM_HASH else set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT) set_header(RACK_REQUEST_FORM_HASH, {}) end rescue => error set_header(RACK_REQUEST_FORM_ERROR, error) raise end end
def [](key)
def [](key) warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead", uplevel: 1) params[key.to_s] end
def []=(key, value)
shortcut for request.params[key] = value
def []=(key, value) warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead", uplevel: 1) params[key.to_s] = value end
def accept_encoding
def accept_encoding parse_http_accept_header(get_header("HTTP_ACCEPT_ENCODING")) end
def accept_language
def accept_language parse_http_accept_header(get_header("HTTP_ACCEPT_LANGUAGE")) end
def allowed_scheme(header)
def allowed_scheme(header) header if ALLOWED_SCHEMES.include?(header) end
def authority
In HTTP/1, this is the `host` header.
https://tools.ietf.org/html/rfc3986#section-3.2
The authority of the incoming request as defined by RFC3976.
def authority forwarded_authority || host_authority || server_authority end
def base_url
def base_url "#{scheme}://#{host_with_port}" end
def body; get_header(RACK_INPUT) end
def body; get_header(RACK_INPUT) end
def content_charset
that, per RFC2616, text/* media types that specify no explicit
parameter was given, or nil if no "charset" was specified. Note
The character set of the request body if a "charset" media type
def content_charset media_type_params['charset'] end
def content_length; get_header('CONTENT_LENGTH') end
def content_length; get_header('CONTENT_LENGTH') end
def content_type
def content_type content_type = get_header('CONTENT_TYPE') content_type.nil? || content_type.empty? ? nil : content_type end
def cookies
def cookies hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |key| set_header(key, {}) end string = get_header(HTTP_COOKIE) unless string == get_header(RACK_REQUEST_COOKIE_STRING) hash.replace Utils.parse_cookies_header(string) set_header(RACK_REQUEST_COOKIE_STRING, string) end hash end
def default_session; {}; end
def default_session; {}; end
def delete?; request_method == DELETE end
def delete?; request_method == DELETE end
def delete_param(k)
If the parameter is in both GET and POST, the POST value takes precedence since that's how #params works.
Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
def delete_param(k) post_value, get_value = self.POST.delete(k), self.GET.delete(k) post_value || get_value end
def form_data?
A request body is also assumed to contain form-data when no
+FORM_DATA_MEDIA_TYPES+ array.
list of form-data media types can be modified through the
"application/x-www-form-urlencoded" or "multipart/form-data". The
the request content-type for one of the media-types:
Determine whether the request body contains form-data by checking
def form_data? type = media_type meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD) (meth == POST && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type) end
def forwarded_authority
def forwarded_authority forwarded_priority.each do |type| case type when :forwarded if forwarded = get_http_forwarded(:host) return forwarded.last end when :x_forwarded if value = get_header(HTTP_X_FORWARDED_HOST) return wrap_ipv6(split_header(value).last) end end end nil end
def forwarded_for
def forwarded_for forwarded_priority.each do |type| case type when :forwarded if forwarded_for = get_http_forwarded(:for) return(forwarded_for.map! do |authority| split_authority(authority)[1] end) end when :x_forwarded if value = get_header(HTTP_X_FORWARDED_FOR) return(split_header(value).map do |authority| split_authority(wrap_ipv6(authority))[1] end) end end end nil end
def forwarded_port
def forwarded_port forwarded_priority.each do |type| case type when :forwarded if forwarded = get_http_forwarded(:for) return(forwarded.map do |authority| split_authority(authority)[2] end.compact) end when :x_forwarded if value = get_header(HTTP_X_FORWARDED_PORT) return split_header(value).map(&:to_i) end end end nil end
def forwarded_priority
def forwarded_priority Request.forwarded_priority end
def forwarded_scheme
def forwarded_scheme forwarded_priority.each do |type| case type when :forwarded if (forwarded_proto = get_http_forwarded(:proto)) && (scheme = allowed_scheme(forwarded_proto.last)) return scheme end when :x_forwarded x_forwarded_proto_priority.each do |x_type| if header = FORWARDED_SCHEME_HEADERS[x_type] split_header(get_header(header)).reverse_each do |scheme| if allowed_scheme(scheme) return scheme end end end end end end nil end
def fullpath
def fullpath query_string.empty? ? path : "#{path}?#{query_string}" end
def get?; request_method == GET end
def get?; request_method == GET end
def get_http_forwarded(token)
def get_http_forwarded(token) Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token) end
def head?; request_method == HEAD end
def head?; request_method == HEAD end
def host
def host split_authority(self.authority)[0] end
def host_authority
def host_authority get_header(HTTP_HOST) end
def host_with_port(authority = self.authority)
def host_with_port(authority = self.authority) host, _, port = split_authority(authority) if port == DEFAULT_PORTS[self.scheme] host else authority end end
def hostname
as +host+. In the case of IPv6 or future address formats, the square
In the case of a domain name or IPv4 address, the result is the same
Returns an address suitable for being to resolve to an address.
def hostname split_authority(self.authority)[1] end
def ip
def ip remote_addresses = split_header(get_header('REMOTE_ADDR')) external_addresses = reject_trusted_ip_addresses(remote_addresses) unless external_addresses.empty? return external_addresses.last end if (forwarded_for = self.forwarded_for) && !forwarded_for.empty? # The forwarded for addresses are ordered: client, proxy1, proxy2. # So we reject all the trusted addresses (proxy*) and return the # last client. Or if we trust everyone, we just return the first # address. return reject_trusted_ip_addresses(forwarded_for).last || forwarded_for.first end # If all the addresses are trusted, and we aren't forwarded, just return # the first remote address, which represents the source of the request. remote_addresses.first end
def link?; request_method == LINK end
def link?; request_method == LINK end
def logger; get_header(RACK_LOGGER) end
def logger; get_header(RACK_LOGGER) end
def media_type
For more information on the use of media types in HTTP, see:
"text/plain;charset=utf-8", the media-type is "text/plain".
without any media type parameters. e.g., when CONTENT_TYPE is
The media type (type/subtype) portion of the CONTENT_TYPE header
def media_type MediaType.type(content_type) end
def media_type_params
this method responds with the following Hash:
provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
an empty Hash if no CONTENT_TYPE or media-type parameters were
The media type parameters provided in CONTENT_TYPE as a Hash, or
def media_type_params MediaType.params(content_type) end
def options?; request_method == OPTIONS end
def options?; request_method == OPTIONS end
def params
The union of GET and POST data.
def params self.GET.merge(self.POST) end
def parse_http_accept_header(header)
def parse_http_accept_header(header) header.to_s.split(",").each(&:strip!).map do |part| attribute, parameters = part.split(";", 2).each(&:strip!) quality = 1.0 if parameters and /\Aq=([\d.]+)/ =~ parameters quality = $1.to_f end [attribute, quality] end end
def parse_multipart
def parse_multipart Rack::Multipart.extract_multipart(self, query_parser) end
def parse_query(qs, d = '&')
def parse_query(qs, d = '&') query_parser.parse_nested_query(qs, d) end
def parseable_data?
Determine whether the request body contains data by checking
def parseable_data? PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) end
def patch?; request_method == PATCH end
def patch?; request_method == PATCH end
def path
def path script_name + path_info end
def path_info; get_header(PATH_INFO).to_s end
def path_info; get_header(PATH_INFO).to_s end
def path_info=(s); set_header(PATH_INFO, s.to_s) end
def path_info=(s); set_header(PATH_INFO, s.to_s) end
def port
def port if authority = self.authority _, _, port = split_authority(authority) end port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port end
def post?; request_method == POST end
def post?; request_method == POST end
def put?; request_method == PUT end
def put?; request_method == PUT end
def query_parser
def query_parser Utils.default_query_parser end
def query_string; get_header(QUERY_STRING).to_s end
def query_string; get_header(QUERY_STRING).to_s end
def referer; get_header('HTTP_REFERER') end
def referer; get_header('HTTP_REFERER') end
def reject_trusted_ip_addresses(ip_addresses)
def reject_trusted_ip_addresses(ip_addresses) ip_addresses.reject { |ip| trusted_proxy?(ip) } end
def request_method; get_header(REQUEST_METHOD) end
def request_method; get_header(REQUEST_METHOD) end
def scheme
def scheme if get_header(HTTPS) == 'on' 'https' elsif get_header(HTTP_X_FORWARDED_SSL) == 'on' 'https' elsif forwarded_scheme forwarded_scheme else get_header(RACK_URL_SCHEME) end end
def script_name; get_header(SCRIPT_NAME).to_s end
def script_name; get_header(SCRIPT_NAME).to_s end
def script_name=(s); set_header(SCRIPT_NAME, s.to_s) end
def script_name=(s); set_header(SCRIPT_NAME, s.to_s) end
def server_authority
The authority as defined by the `SERVER_NAME` and `SERVER_PORT`
def server_authority host = self.server_name port = self.server_port if host if port "#{host}:#{port}" else host end end end
def server_name
def server_name get_header(SERVER_NAME) end
def server_port
def server_port get_header(SERVER_PORT) end
def session
def session fetch_header(RACK_SESSION) do |k| set_header RACK_SESSION, default_session end end
def session_options
def session_options fetch_header(RACK_SESSION_OPTIONS) do |k| set_header RACK_SESSION_OPTIONS, {} end end
def split_authority(authority)
def split_authority(authority) return [] if authority.nil? return [] unless match = AUTHORITY.match(authority) return match[:host], match[:address], match[:port]&.to_i end
def split_header(value)
def split_header(value) value ? value.strip.split(/[,\s]+/) : [] end
def ssl?
def ssl? scheme == 'https' || scheme == 'wss' end
def trace?; request_method == TRACE end
def trace?; request_method == TRACE end
def trusted_proxy?(ip)
def trusted_proxy?(ip) Rack::Request.ip_filter.call(ip) end
def unlink?; request_method == UNLINK end
def unlink?; request_method == UNLINK end
def update_param(k, v)
The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
def update_param(k, v) found = false if self.GET.has_key?(k) found = true self.GET[k] = v end if self.POST.has_key?(k) found = true self.POST[k] = v end unless found self.GET[k] = v end end
def url
def url base_url + fullpath end
def user_agent; get_header('HTTP_USER_AGENT') end
def user_agent; get_header('HTTP_USER_AGENT') end
def values_at(*keys)
def values_at(*keys) keys.map { |key| params[key] } end
def wrap_ipv6(host)
def wrap_ipv6(host) # Even thought IPv6 addresses should be wrapped in square brackets, # sometimes this is not done in various legacy/underspecified headers. # So we try to fix this situation for compatibility reasons. # Try to detect IPv6 addresses which aren't escaped yet: if !host.start_with?('[') && host.count(':') > 1 "[#{host}]" else host end end
def x_forwarded_proto_priority
def x_forwarded_proto_priority Request.x_forwarded_proto_priority end
def xhr?
def xhr? get_header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest" end