| Class | ActionController::AbstractRequest |
| In: |
actionpack/lib/action_controller/request.rb
|
| Parent: | Object |
CgiRequest and TestRequest provide concrete implementations.
| MULTIPART_BOUNDARY | = | %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n |
| EOL | = | "\015\012" |
| env | [R] | The hash of environment variables for this request, such as { ‘RAILS_ENV’ => ‘production’ }. |
# File actionpack/lib/action_controller/request.rb, line 434 def clean_up_ajax_request_body!(body) body.chop! if body[-1] == 0 body.gsub!(/&_=$/, '') end
# File actionpack/lib/action_controller/request.rb, line 430 def extract_content_type_without_parameters(content_type_with_parameters) $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/ end
# File actionpack/lib/action_controller/request.rb, line 422 def extract_multipart_boundary(content_type_with_parameters) if content_type_with_parameters =~ MULTIPART_BOUNDARY ['multipart/form-data', $1.dup] else extract_content_type_without_parameters(content_type_with_parameters) end end
# File actionpack/lib/action_controller/request.rb, line 441 def get_typed_value(value) case value when String value when NilClass '' when Array value.map { |v| get_typed_value(v) } else # This is an uploaded file. if value.respond_to?(:original_filename) && !value.original_filename.blank? unless value.respond_to?(:full_original_filename) class << value alias_method :full_original_filename, :original_filename # Take the basename of the upload's original filename. # This handles the full Windows paths given by Internet Explorer # (and perhaps other broken user agents) without affecting # those which give the lone filename. # The Windows regexp is adapted from Perl's File::Basename. def original_filename if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename) md.captures.first else File.basename full_original_filename end end end end # Return the same value after overriding original_filename. value # Multipart values may have content type, but no filename. elsif value.respond_to?(:read) result = value.read value.rewind result # Unknown value, neither string nor multipart. else raise "Unknown form value: #{value.inspect}" end end end
# File actionpack/lib/action_controller/request.rb, line 418 def parse_multipart_form_parameters(body, boundary, content_length, env) parse_request_parameters(read_multipart(body, boundary, content_length, env)) end
# File actionpack/lib/action_controller/request.rb, line 379 def parse_query_parameters(query_string) return {} if query_string.blank? pairs = query_string.split('&').collect do |chunk| next if chunk.empty? key, value = chunk.split('=', 2) next if key.empty? value = value.nil? ? nil : CGI.unescape(value) [ CGI.unescape(key), value ] end.compact UrlEncodedPairParser.new(pairs).result end
# File actionpack/lib/action_controller/request.rb, line 393 def parse_request_parameters(params) parser = UrlEncodedPairParser.new params = params.dup until params.empty? for key, value in params if key.blank? params.delete key elsif !key.include?('[') # much faster to test for the most common case first (GET) # and avoid the call to build_deep_hash parser.result[key] = get_typed_value(value[0]) params.delete key elsif value.is_a?(Array) parser.parse(key, get_typed_value(value.shift)) params.delete key if value.empty? else raise TypeError, "Expected array, found #{value.inspect}" end end end parser.result end
# File actionpack/lib/action_controller/request.rb, line 492 def read_multipart(body, boundary, content_length, env) params = Hash.new([]) boundary = "--" + boundary quoted_boundary = Regexp.quote(boundary, "n") buf = "" bufsize = 10 * 1024 boundary_end="" # start multipart/form-data body.binmode if defined? body.binmode boundary_size = boundary.size + EOL.size content_length -= boundary_size status = body.read(boundary_size) if nil == status raise EOFError, "no content body" elsif boundary + EOL != status raise EOFError, "bad content body" end loop do head = nil content = if 10240 < content_length Tempfile.new("CGI") else StringIO.new end content.binmode if defined? content.binmode until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf) if (not head) and /#{EOL}#{EOL}/n.match(buf) buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do head = $1.dup "" end next end if head and ( (EOL + boundary + EOL).size < buf.size ) content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" end c = if bufsize < content_length body.read(bufsize) else body.read(content_length) end if c.nil? || c.empty? raise EOFError, "bad content body" end buf.concat(c) content_length -= c.size end buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do content.print $1 if "--" == $2 content_length = -1 end boundary_end = $2.dup "" end content.rewind /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni.match(head) filename = ($1 or $2 or "") if /Mac/ni.match(env['HTTP_USER_AGENT']) and /Mozilla/ni.match(env['HTTP_USER_AGENT']) and (not /MSIE/ni.match(env['HTTP_USER_AGENT'])) filename = CGI.unescape(filename) end /Content-Type: ([^\r]*)/ni.match(head) content_type = ($1 or "") (class << content; self; end).class_eval do alias local_path path define_method(:original_filename) {filename.dup.taint} define_method(:content_type) {content_type.dup.taint} end /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head) name = $1.dup if params.has_key?(name) params[name].push(content) else params[name] = [content] end break if buf.size == 0 break if content_length == -1 end raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ params end
Returns the accepted MIME type for the request
# File actionpack/lib/action_controller/request.rb, line 72 def accepts @accepts ||= if @env['HTTP_ACCEPT'].to_s.strip.empty? [ content_type, Mime::ALL ].compact # make sure content_type being nil is not included else Mime::Type.parse(@env['HTTP_ACCEPT']) end end
The request body is an IO input stream.
# File actionpack/lib/action_controller/request.rb, line 294 def body end
# File actionpack/lib/action_controller/request.rb, line 59 def content_length @content_length ||= env['CONTENT_LENGTH'].to_i end
The MIME type of the HTTP request, such as Mime::XML.
For backward compatibility, the post format is extracted from the X-Post-Data-Format HTTP header if present.
# File actionpack/lib/action_controller/request.rb, line 67 def content_type @content_type ||= Mime::Type.lookup(content_type_without_parameters) end
Is this a DELETE request? Equivalent to request.method == :delete
# File actionpack/lib/action_controller/request.rb, line 45 def delete? method == :delete end
Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify a different tld_length, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
# File actionpack/lib/action_controller/request.rb, line 196 def domain(tld_length = 1) return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil? host.split('.').last(1 + tld_length).join('.') end
Returns the Mime type for the format used in the request. If there is no format available, the first of the accept types will be used. Examples:
GET /posts/5.xml | request.format => Mime::XML GET /posts/5.xhtml | request.format => Mime::HTML GET /posts/5 | request.format => request.accepts.first (usually Mime::HTML for browsers)
# File actionpack/lib/action_controller/request.rb, line 87 def format @format ||= parameters[:format] ? Mime::Type.lookup_by_extension(parameters[:format]) : accepts.first end
Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension. Example:
class ApplicationController < ActionController::Base
before_filter :adjust_format_for_iphone
private
def adjust_format_for_iphone
request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
end
end
# File actionpack/lib/action_controller/request.rb, line 103 def format=(extension) parameters[:format] = extension.to_s format end
Is this a GET (or HEAD) request? Equivalent to request.method == :get
# File actionpack/lib/action_controller/request.rb, line 30 def get? method == :get end
Is this a HEAD request? request.method sees HEAD as :get, so check the HTTP method directly.
# File actionpack/lib/action_controller/request.rb, line 51 def head? @env['REQUEST_METHOD'].downcase.to_sym == :head end
Returns the host for this request, such as example.com.
# File actionpack/lib/action_controller/request.rb, line 166 def host end
Returns a host:port string for this request, such as example.com or example.com:8080.
# File actionpack/lib/action_controller/request.rb, line 171 def host_with_port host + port_string end
The HTTP request method as a lowercase symbol, such as :get. Note, HEAD is returned as :get since the two are functionally equivalent from the application’s perspective.
# File actionpack/lib/action_controller/request.rb, line 18 def method @request_method ||= if @env['REQUEST_METHOD'] == 'POST' && !parameters[:_method].blank? parameters[:_method].to_s.downcase.to_sym else @env['REQUEST_METHOD'].downcase.to_sym end @request_method == :head ? :get : @request_method end
Returns both GET and POST parameters in a single hash.
# File actionpack/lib/action_controller/request.rb, line 265 def parameters @parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access end
Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
# File actionpack/lib/action_controller/request.rb, line 230 def path path = (uri = request_uri) ? uri.split('?').first.to_s : '' # Cut off the path to the installation directory if given path.sub!(%r/^#{relative_url_root}/, '') path || '' end
Returns a hash with the parameters used to form the path of the request
Example:
{:action => 'my_action', :controller => 'my_controller'}
# File actionpack/lib/action_controller/request.rb, line 284 def path_parameters @path_parameters ||= {} end
Returns the port number of this request as an integer.
# File actionpack/lib/action_controller/request.rb, line 176 def port @port_as_int ||= @env['SERVER_PORT'].to_i end
Returns a port suffix like ":8080" if the port number of this request is not the default HTTP port 80 or HTTPS port 443.
# File actionpack/lib/action_controller/request.rb, line 190 def port_string (port == standard_port) ? '' : ":#{port}" end
Is this a POST request? Equivalent to request.method == :post
# File actionpack/lib/action_controller/request.rb, line 35 def post? method == :post end
Return ‘https://’ if this is an SSL request and ‘http://’ otherwise.
# File actionpack/lib/action_controller/request.rb, line 156 def protocol ssl? ? 'https://' : 'http://' end
Is this a PUT request? Equivalent to request.method == :put
# File actionpack/lib/action_controller/request.rb, line 40 def put? method == :put end
Read the request body. This is useful for web services that need to work with raw requests directly.
# File actionpack/lib/action_controller/request.rb, line 256 def raw_post unless env.include? 'RAW_POST_DATA' env['RAW_POST_DATA'] = body.read(content_length) body.rewind if body.respond_to?(:rewind) end env['RAW_POST_DATA'] end
Returns the path minus the web server relative installation directory. This can be set with the environment variable RAILS_RELATIVE_URL_ROOT. It can be automatically extracted for Apache setups. If the server is not Apache, this method returns an empty string.
# File actionpack/lib/action_controller/request.rb, line 242 def relative_url_root @@relative_url_root ||= case when @env["RAILS_RELATIVE_URL_ROOT"] @env["RAILS_RELATIVE_URL_ROOT"] when server_software == 'apache' @env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') else '' end end
Determine originating IP address. REMOTE_ADDR is the standard but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR are set by proxies so check for these before falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma- delimited list in the case of multiple chained proxies; the first is the originating IP.
Security note: Be aware that since remote_ip will check regular HTTP headers, it can be tricked by anyone setting those manually. In other words, people can pose as whatever IP address they like to this method. That doesn’t matter if all your doing is using IP addresses for statistical or geographical information, but if you want to, for example, limit access to an administrative area by IP, you should instead use Request#remote_addr, which can’t be spoofed (but also won’t survive proxy forwards).
# File actionpack/lib/action_controller/request.rb, line 130 def remote_ip return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP' if @env.include? 'HTTP_X_FORWARDED_FOR' then remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip| ip.strip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i end return remote_ips.first.strip unless remote_ips.empty? end @env['REMOTE_ADDR'] end
Return the request URI, accounting for server idiosyncracies. WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
# File actionpack/lib/action_controller/request.rb, line 213 def request_uri if uri = @env['REQUEST_URI'] # Remove domain, which webrick puts into the request_uri. (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri else # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO. script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$}) uri = @env['PATH_INFO'] uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil? unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty? uri << '?' << env_qs end @env['REQUEST_URI'] = uri end end
Returns the lowercase name of the HTTP server software.
# File actionpack/lib/action_controller/request.rb, line 145 def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil end
Is this an SSL request?
# File actionpack/lib/action_controller/request.rb, line 161 def ssl? @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https' end
Returns the standard port number for this request’s protocol
# File actionpack/lib/action_controller/request.rb, line 181 def standard_port case protocol when 'https://' then 443 else 80 end end
Returns all the subdomains as an array, so ["dev", "www"] would be returned for "dev.www.rubyonrails.org". You can specify a different tld_length, such as 2 to catch ["www"] instead of ["www", "rubyonrails"] in "www.rubyonrails.co.uk".
# File actionpack/lib/action_controller/request.rb, line 205 def subdomains(tld_length = 1) return [] unless host parts = host.split('.') parts[0..-(tld_length+2)] end
The same as path_parameters with explicitly symbolized keys
# File actionpack/lib/action_controller/request.rb, line 275 def symbolized_path_parameters @symbolized_path_parameters ||= path_parameters.symbolize_keys end
Returns the complete URL used for this request
# File actionpack/lib/action_controller/request.rb, line 151 def url protocol + host_with_port + request_uri end
Returns true if the request’s "X-Requested-With" header contains "XMLHttpRequest". (The Prototype Javascript library sends this header with every Ajax request.)
# File actionpack/lib/action_controller/request.rb, line 111 def xml_http_request? not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil? end
The raw content type string. Use when you need parameters such as charset or boundary which aren’t included in the content_type MIME type. Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
# File actionpack/lib/action_controller/request.rb, line 320 def content_type_with_parameters content_type_from_legacy_post_data_format_header || env['CONTENT_TYPE'].to_s end
The raw content type string with its parameters stripped off.
# File actionpack/lib/action_controller/request.rb, line 326 def content_type_without_parameters @content_type_without_parameters ||= self.class.extract_content_type_without_parameters(content_type_with_parameters) end
# File actionpack/lib/action_controller/request.rb, line 331 def content_type_from_legacy_post_data_format_header if x_post_format = @env['HTTP_X_POST_DATA_FORMAT'] case x_post_format.to_s.downcase when 'yaml'; 'application/x-yaml' when 'xml'; 'application/xml' end end end
# File actionpack/lib/action_controller/request.rb, line 340 def parse_formatted_request_parameters return {} if content_length.zero? content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters) # Don't parse params for unknown requests. return {} if content_type.blank? mime_type = Mime::Type.lookup(content_type) strategy = ActionController::Base.param_parsers[mime_type] # Only multipart form parsing expects a stream. body = (strategy && strategy != :multipart_form) ? raw_post : self.body case strategy when Proc strategy.call(body) when :url_encoded_form self.class.clean_up_ajax_request_body! body self.class.parse_query_parameters(body) when :multipart_form self.class.parse_multipart_form_parameters(body, boundary, content_length, env) when :xml_simple, :xml_node body.blank? ? {} : Hash.from_xml(body).with_indifferent_access when :yaml YAML.load(body) else {} end rescue Exception => e # YAML, XML or Ruby code block errors raise { "body" => body, "content_type" => content_type_with_parameters, "content_length" => content_length, "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace } end