require'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::URLincludeRack::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
].freezeENV_METHODS.eachdo|env|class_eval<<-METHOD,__FILE__,__LINE__+1
def #{env.sub(/^HTTP_/n,'').downcase} # def accept_charset
get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
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'},[]];end}defcontroller_classparams=path_parametersifparams.key?(:controller)controller_param=params[:controller].underscoreparams[:action]||='index'const_name="#{controller_param.camelize}Controller"ActiveSupport::Dependencies.constantize(const_name)elsePASS_NOT_FOUNDendenddefkey?(key)has_header?keyend# List of HTTP request methods from the following RFCs:# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)# Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt)# PATCH Method for HTTP (http://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 cacheHTTP_METHODS.each{|method|HTTP_METHOD_LOOKUP[method]=method.underscore.to_sym}# 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".freeze)enddefroutes=(routes)# :nodoc:set_header("action_dispatch.routes".freeze,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'.freeze)enddefcontroller_instance=(controller)# :nodoc:set_header('action_controller.instance'.freeze,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'.freeze)==false)end# Returns a symbol form of the #request_methoddefrequest_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 #methoddefmethod_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# 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?get_header('HTTP_X_REQUESTED_WITH')=~/XMLHttpRequest/iendalias: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)set_header"action_dispatch.remote_ip".freeze,remote_ipendACTION_DISPATCH_REQUEST_ID="action_dispatch.request_id".freeze# :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.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 accessdefGETfetch_header("action_dispatch.request.query_parameters")do|k|rack_query_params=super||{}# 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 accessdefPOSTfetch_header("action_dispatch.request.request_parameters")dopr=parse_formatted_parameters(params_parsers)do|params|super||{}endself.request_parameters=Request::Utils.normalize_encode_params(pr)endrescueParamsParser::ParseError# one of the parse strategies blew upself.request_parameters=Request::Utils.normalize_encode_params(super||{})raiserescueRack::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=~remote_addr&&LOCALHOST=~remote_ipenddefrequest_parameters=(params)raiseifparams.nil?set_header("action_dispatch.request.request_parameters".freeze,params)enddefloggerget_header("action_dispatch.logger".freeze)enddefcommit_flashenddefssl?super||scheme=='wss'.freezeendprivatedefcheck_method(name)HTTP_METHOD_LOOKUP[name]||raise(ActionController::UnknownHttpMethod,"#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")nameendendend