# frozen_string_literal: truerequire"stringio"require"active_support/inflector"require"action_dispatch/http/headers"require"action_controller/metal/exceptions"require"rack/request"require"action_dispatch/http/cache"require"action_dispatch/http/mime_negotiation"require"action_dispatch/http/parameters"require"action_dispatch/http/filter_parameters"require"action_dispatch/http/upload"require"action_dispatch/http/url"require"active_support/core_ext/array/conversions"moduleActionDispatchclassRequestincludeRack::Request::HelpersincludeActionDispatch::Http::Cache::RequestincludeActionDispatch::Http::MimeNegotiationincludeActionDispatch::Http::ParametersincludeActionDispatch::Http::FilterParametersincludeActionDispatch::Http::URLincludeActionDispatch::ContentSecurityPolicy::RequestincludeActionDispatch::PermissionsPolicy::RequestincludeRack::Request::Envautoload:Session,"action_dispatch/request/session"autoload:Utils,"action_dispatch/request/utils"LOCALHOST=Regexp.union[/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,/^::1$/,/^0:0:0:0:0:0:0:1(%.*)?$/]ENV_METHODS=%w[ AUTH_TYPE GATEWAY_INTERFACE
PATH_TRANSLATED REMOTE_HOST
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
SERVER_NAME SERVER_PROTOCOL
ORIGINAL_SCRIPT_NAME
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
HTTP_X_FORWARDED_FOR HTTP_ORIGIN HTTP_VERSION
HTTP_X_CSRF_TOKEN HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
SERVER_ADDR
].freeze# TODO: Remove SERVER_ADDR when we remove support to Rack 2.1.# See https://github.com/rack/rack/commit/c173b188d81ee437b588c1e046a1c9f031dea550ENV_METHODS.eachdo|env|class_eval<<-METHOD,__FILE__,__LINE__+1
# frozen_string_literal: true
def #{env.delete_prefix("HTTP_").downcase} # def accept_charset
get_header "#{env}" # get_header "HTTP_ACCEPT_CHARSET"
end # end
METHODenddefself.emptynew({})enddefinitialize(env)super@method=nil@request_method=nil@remote_ip=nil@original_fullpath=nil@fullpath=nil@ip=nilenddefcommit_cookie_jar!# :nodoc:endPASS_NOT_FOUND=Class.new{# :nodoc:defself.action(_);self;enddefself.call(_);[404,{"X-Cascade"=>"pass"},[]];enddefself.action_encoding_template(action);false;end}defcontroller_classparams=path_parametersparams[:action]||="index"controller_class_for(params[:controller])enddefcontroller_class_for(name)ifnamecontroller_param=name.underscoreconst_name=controller_param.camelize<<"Controller"beginActiveSupport::Dependencies.constantize(const_name)rescueNameError=>erroriferror.missing_name==const_name||const_name.start_with?("#{error.missing_name}::")raiseMissingController.new(error.message,error.name)elseraiseendendelsePASS_NOT_FOUNDendend# Returns true if the request has a header matching the given key parameter.## request.key? :ip_spoofing_check # => truedefkey?(key)has_header?keyend# List of HTTP request methods from the following RFCs:# Hypertext Transfer Protocol -- HTTP/1.1 (https://www.ietf.org/rfc/rfc2616.txt)# HTTP Extensions for Distributed Authoring -- WEBDAV (https://www.ietf.org/rfc/rfc2518.txt)# Versioning Extensions to WebDAV (https://www.ietf.org/rfc/rfc3253.txt)# Ordered Collections Protocol (WebDAV) (https://www.ietf.org/rfc/rfc3648.txt)# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (https://www.ietf.org/rfc/rfc3744.txt)# Web Distributed Authoring and Versioning (WebDAV) SEARCH (https://www.ietf.org/rfc/rfc5323.txt)# Calendar Extensions to WebDAV (https://www.ietf.org/rfc/rfc4791.txt)# PATCH Method for HTTP (https://www.ietf.org/rfc/rfc5789.txt)RFC2616=%w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)RFC2518=%w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)RFC3253=%w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)RFC3648=%w(ORDERPATCH)RFC3744=%w(ACL)RFC5323=%w(SEARCH)RFC4791=%w(MKCALENDAR)RFC5789=%w(PATCH)HTTP_METHODS=RFC2616+RFC2518+RFC3253+RFC3648+RFC3744+RFC5323+RFC4791+RFC5789HTTP_METHOD_LOOKUP={}# Populate the HTTP method lookup cache.HTTP_METHODS.each{|method|HTTP_METHOD_LOOKUP[method]=method.underscore.to_sym}aliasraw_request_methodrequest_method# :nodoc:# Returns the HTTP \method that the application should see.# In the case where the \method was overridden by a middleware# (for instance, if a HEAD request was converted to a GET,# or if a _method parameter was used to determine the \method# the application should use), this \method returns the overridden# value, not the original.defrequest_method@request_method||=check_method(super)enddefroutes# :nodoc:get_header("action_dispatch.routes")enddefroutes=(routes)# :nodoc:set_header("action_dispatch.routes",routes)enddefengine_script_name(_routes)# :nodoc:get_header(_routes.env_key)enddefengine_script_name=(name)# :nodoc:set_header(routes.env_key,name.dup)enddefrequest_method=(request_method)#:nodoc:ifcheck_method(request_method)@request_method=set_header("REQUEST_METHOD",request_method)endenddefcontroller_instance# :nodoc:get_header("action_controller.instance")enddefcontroller_instance=(controller)# :nodoc:set_header("action_controller.instance",controller)enddefhttp_auth_saltget_header"action_dispatch.http_auth_salt"enddefshow_exceptions?# :nodoc:# We're treating `nil` as "unset", and we want the default setting to be# `true`. This logic should be extracted to `env_config` and calculated# once.!(get_header("action_dispatch.show_exceptions")==false)end# Returns a symbol form of the #request_method.defrequest_method_symbolHTTP_METHOD_LOOKUP[request_method]end# Returns the original value of the environment's REQUEST_METHOD,# even if it was overridden by middleware. See #request_method for# more information.defmethod@method||=check_method(get_header("rack.methodoverride.original_method")||get_header("REQUEST_METHOD"))end# Returns a symbol form of the #method.defmethod_symbolHTTP_METHOD_LOOKUP[method]end# Provides access to the request's HTTP headers, for example:## request.headers["Content-Type"] # => "text/plain"defheaders@headers||=Http::Headers.new(self)end# Early Hints is an HTTP/2 status code that indicates hints to help a client start# making preparations for processing the final response.## If the env contains +rack.early_hints+ then the server accepts HTTP2 push for Link headers.## The +send_early_hints+ method accepts a hash of links as follows:## send_early_hints("Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload")## If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the# Early Hints headers are included by default if supported.defsend_early_hints(links)returnunlessenv["rack.early_hints"]env["rack.early_hints"].call(links)end# Returns a +String+ with the last requested path including their params.## # get '/foo'# request.original_fullpath # => '/foo'## # get '/foo?bar'# request.original_fullpath # => '/foo?bar'deforiginal_fullpath@original_fullpath||=(get_header("ORIGINAL_FULLPATH")||fullpath)end# Returns the +String+ full path including params of the last URL requested.## # get "/articles"# request.fullpath # => "/articles"## # get "/articles?page=2"# request.fullpath # => "/articles?page=2"deffullpath@fullpath||=superend# Returns the original request URL as a +String+.## # get "/articles?page=2"# request.original_url # => "http://www.example.com/articles?page=2"deforiginal_urlbase_url+original_fullpathend# The +String+ MIME type of the request.## # get "/articles"# request.media_type # => "application/x-www-form-urlencoded"defmedia_typecontent_mime_type.to_send# Returns the content length of the request as an integer.defcontent_lengthsuper.to_iend# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"# (case-insensitive), which may need to be manually added depending on the# choice of JavaScript libraries and frameworks.defxml_http_request?/XMLHttpRequest/i.match?(get_header("HTTP_X_REQUESTED_WITH"))endalias:xhr?:xml_http_request?# Returns the IP address of client as a +String+.defip@ip||=superend# Returns the IP address of client as a +String+,# usually set by the RemoteIp middleware.defremote_ip@remote_ip||=(get_header("action_dispatch.remote_ip")||ip).to_senddefremote_ip=(remote_ip)@remote_ip=nilset_header"action_dispatch.remote_ip",remote_ipendACTION_DISPATCH_REQUEST_ID="action_dispatch.request_id"# :nodoc:# Returns the unique request id, which is based on either the X-Request-Id header that can# be generated by a firewall, load balancer, or web server or by the RequestId middleware# (which sets the action_dispatch.request_id environment variable).## This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.# This relies on the Rack variable set by the ActionDispatch::RequestId middleware.defrequest_idget_headerACTION_DISPATCH_REQUEST_IDenddefrequest_id=(id)# :nodoc:set_headerACTION_DISPATCH_REQUEST_ID,idendalias_method:uuid,:request_id# Returns the lowercase name of the HTTP server software.defserver_software(get_header("SERVER_SOFTWARE")&&/^([a-zA-Z]+)/=~get_header("SERVER_SOFTWARE"))?$1.downcase:nilend# Read the request \body. This is useful for web services that need to# work with raw requests directly.defraw_postunlesshas_header?"RAW_POST_DATA"raw_post_body=bodyset_header("RAW_POST_DATA",raw_post_body.read(content_length))raw_post_body.rewindifraw_post_body.respond_to?(:rewind)endget_header"RAW_POST_DATA"end# The request body is an IO input stream. If the RAW_POST_DATA environment# variable is already set, wrap it in a StringIO.defbodyifraw_post=get_header("RAW_POST_DATA")raw_post=(+raw_post).force_encoding(Encoding::BINARY)StringIO.new(raw_post)elsebody_streamendend# Determine whether the request body contains form-data by checking# the request Content-Type for one of the media-types:# "application/x-www-form-urlencoded" or "multipart/form-data". The# list of form-data media types can be modified through the# +FORM_DATA_MEDIA_TYPES+ array.## A request body is not assumed to contain form-data when no# Content-Type header is provided and the request_method is POST.defform_data?FORM_DATA_MEDIA_TYPES.include?(media_type)enddefbody_stream#:nodoc:get_header("rack.input")end# TODO This should be broken apart into AD::Request::Session and probably# be included by the session middleware.defreset_sessionifsession&&session.respond_to?(:destroy)session.destroyelseself.session={}endenddefsession=(session)#:nodoc:Session.setself,sessionenddefsession_options=(options)Session::Options.setself,optionsend# Override Rack's GET method to support indifferent access.defGETfetch_header("action_dispatch.request.query_parameters")do|k|rack_query_params=super||{}controller=path_parameters[:controller]action=path_parameters[:action]rack_query_params=Request::Utils.set_binary_encoding(self,rack_query_params,controller,action)# Check for non UTF-8 parameter values, which would cause errors laterRequest::Utils.check_param_encoding(rack_query_params)set_headerk,Request::Utils.normalize_encode_params(rack_query_params)endrescueRack::Utils::ParameterTypeError,Rack::Utils::InvalidParameterError=>eraiseActionController::BadRequest.new("Invalid query parameters: #{e.message}")endalias:query_parameters:GET# Override Rack's POST method to support indifferent access.defPOSTfetch_header("action_dispatch.request.request_parameters")dopr=parse_formatted_parameters(params_parsers)do|params|super||{}endpr=Request::Utils.set_binary_encoding(self,pr,path_parameters[:controller],path_parameters[:action])Request::Utils.check_param_encoding(pr)self.request_parameters=Request::Utils.normalize_encode_params(pr)endrescueRack::Utils::ParameterTypeError,Rack::Utils::InvalidParameterError=>eraiseActionController::BadRequest.new("Invalid request parameters: #{e.message}")endalias:request_parameters:POST# Returns the authorization header regardless of whether it was specified directly or through one of the# proxy alternatives.defauthorizationget_header("HTTP_AUTHORIZATION")||get_header("X-HTTP_AUTHORIZATION")||get_header("X_HTTP_AUTHORIZATION")||get_header("REDIRECT_X_HTTP_AUTHORIZATION")end# True if the request came from localhost, 127.0.0.1, or ::1.deflocal?LOCALHOST.match?(remote_addr)&&LOCALHOST.match?(remote_ip)enddefrequest_parameters=(params)raiseifparams.nil?set_header("action_dispatch.request.request_parameters",params)enddefloggerget_header("action_dispatch.logger")enddefcommit_flashenddefssl?super||scheme=="wss"enddefinspect# :nodoc:"#<#{self.class.name}#{method}#{original_url.dump} for #{remote_ip}>"endprivatedefcheck_method(name)HTTP_METHOD_LOOKUP[name]||raise(ActionController::UnknownHttpMethod,"#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")nameendendendActiveSupport.run_load_hooks:action_dispatch_request,ActionDispatch::Request