Class ActionController::AbstractRequest
In: actionpack/lib/action_controller/request.rb
Parent: Object

CgiRequest and TestRequest provide concrete implementations.

Methods

Constants

MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
EOL = "\015\012"

Attributes

env  [R]  The hash of environment variables for this request, such as { ‘RAILS_ENV’ => ‘production’ }.

Private Class methods

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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

Public Instance methods

Returns the accepted MIME type for the request

[Source]

# 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.

[Source]

# File actionpack/lib/action_controller/request.rb, line 294
    def body
    end

[Source]

# 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.

[Source]

# 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

[Source]

# 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".

[Source]

# 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)

[Source]

# 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

[Source]

# 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

[Source]

# 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.

[Source]

# File actionpack/lib/action_controller/request.rb, line 51
    def head?
      @env['REQUEST_METHOD'].downcase.to_sym == :head
    end

[Source]

# File actionpack/lib/action_controller/request.rb, line 55
    def headers
      @env
    end

Returns the host for this request, such as example.com.

[Source]

# 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.

[Source]

# 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.

[Source]

# 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.

[Source]

# 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

[Source]

# 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'}

[Source]

# 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.

[Source]

# 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.

[Source]

# 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

[Source]

# 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.

[Source]

# 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

[Source]

# 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.

[Source]

# 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.

[Source]

# 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).

[Source]

# 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.

[Source]

# 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.

[Source]

# 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?

[Source]

# 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

[Source]

# 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".

[Source]

# 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

[Source]

# 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

[Source]

# File actionpack/lib/action_controller/request.rb, line 151
    def url
      protocol + host_with_port + request_uri
    end
xhr?()

Alias for xml_http_request?

Returns true if the request’s "X-Requested-With" header contains "XMLHttpRequest". (The Prototype Javascript library sends this header with every Ajax request.)

[Source]

# File actionpack/lib/action_controller/request.rb, line 111
    def xml_http_request?
      not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
    end

Protected Instance methods

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.

[Source]

# 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.

[Source]

# 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

Private Instance methods

[Source]

# 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
local_path()

Alias for path

[Source]

# 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

[Validate]