request.rb 7.7 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3
module ActionController
  # These methods are available in both the production and test Request objects.
  class AbstractRequest
4
    cattr_accessor :relative_url_root
5

6 7 8 9
    # Returns the hash of environment variables for this request,
    # such as { 'RAILS_ENV' => 'production' }.
    attr_reader :env

D
Initial  
David Heinemeier Hansson 已提交
10 11
    # Returns both GET and POST parameters in a single hash.
    def parameters
12
      @parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access
D
Initial  
David Heinemeier Hansson 已提交
13 14
    end

15
    # Returns the HTTP request method as a lowercase symbol (:get, for example)
D
Initial  
David Heinemeier Hansson 已提交
16
    def method
17
      @request_method ||= @env['REQUEST_METHOD'].downcase.to_sym
D
Initial  
David Heinemeier Hansson 已提交
18 19
    end

20
    # Is this a GET request?  Equivalent to request.method == :get
D
Initial  
David Heinemeier Hansson 已提交
21 22 23 24
    def get?
      method == :get
    end

25
    # Is this a POST request?  Equivalent to request.method == :post
D
Initial  
David Heinemeier Hansson 已提交
26 27 28 29
    def post?
      method == :post
    end

30
    # Is this a PUT request?  Equivalent to request.method == :put
D
Initial  
David Heinemeier Hansson 已提交
31 32 33 34
    def put?
      method == :put
    end

35
    # Is this a DELETE request?  Equivalent to request.method == :delete
D
Initial  
David Heinemeier Hansson 已提交
36 37 38 39
    def delete?
      method == :delete
    end

40
    # Is this a HEAD request?  Equivalent to request.method == :head
41 42 43
    def head?
      method == :head
    end
44

45 46
    # Determine whether the body of a HTTP call is URL-encoded (default)
    # or matches one of the registered param_parsers. 
47 48 49
    #
    # For backward compatibility, the post format is extracted from the
    # X-Post-Data-Format HTTP header if present.
50 51
    def content_type
      return @content_type if @content_type
52

53
      @content_type = @env['CONTENT_TYPE'].to_s.downcase
54

55 56
      if @env['HTTP_X_POST_DATA_FORMAT']          
        case @env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym
57 58 59 60 61 62 63
          when :yaml
            @content_type = 'application/x-yaml'
          when :xml
            @content_type = 'application/xml'
          end
      end

64
      @content_type = Mime::Type.lookup(@content_type)
65 66 67
    end

    def accepts
68 69 70 71 72
      return @accepts if @accepts
      
      @accepts = if @env['HTTP_ACCEPT'].to_s.strip.blank?
        [ content_type, Mime::ALL ]
      else
73
        Mime::Type.parse(@env['HTTP_ACCEPT'])
74
      end
75
    end
76

77 78 79
    # Returns true if the request's "X-Requested-With" header contains
    # "XMLHttpRequest". (The Prototype Javascript library sends this header with
    # every Ajax request.)
80
    def xml_http_request?
81
      not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
82 83 84
    end
    alias xhr? :xml_http_request?

D
Initial  
David Heinemeier Hansson 已提交
85 86 87 88 89 90 91
    # 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.
    def remote_ip
92
      return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
93

94 95
      if @env.include? 'HTTP_X_FORWARDED_FOR' then
        remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
96
            ip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
97 98 99
        end

        return remote_ips.first.strip unless remote_ips.empty?
D
Initial  
David Heinemeier Hansson 已提交
100
      end
101

102
      @env['REMOTE_ADDR']
D
Initial  
David Heinemeier Hansson 已提交
103 104
    end

105 106 107
    # Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify
    # a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
    def domain(tld_length = 1)
108
      return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
109

110
      host.split('.').last(1 + tld_length).join('.')
111 112 113 114 115 116
    end

    # Returns all the subdomains as an array, so ["dev", "www"] would be returned for "dev.www.rubyonrails.org".
    # You can specify a different <tt>tld_length</tt>, such as 2 to catch ["www"] instead of ["www", "rubyonrails"]
    # in "www.rubyonrails.co.uk".
    def subdomains(tld_length = 1)
117
      return [] unless host
118
      parts = host.split('.')
119
      parts[0..-(tld_length+2)]
120 121
    end

122 123 124
    # Receive the raw post data.
    # This is useful for services such as REST, XMLRPC and SOAP
    # which communicate over HTTP POST but don't use the traditional parameter format.
125
    def raw_post
126
      @env['RAW_POST_DATA']
127
    end
128

129 130
    # Returns the request URI correctly, taking into account the idiosyncracies
    # of the various servers.
D
Initial  
David Heinemeier Hansson 已提交
131
    def request_uri
132
      if uri = @env['REQUEST_URI']
133
        (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri # Remove domain, which webrick puts into the request_uri.
134
      else  # REQUEST_URI is blank under IIS - get this from PATH_INFO and SCRIPT_NAME
135 136
        script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
        uri = @env['PATH_INFO']
137
        uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
138
        unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
139 140 141
          uri << '?' << env_qs
        end
        uri
142
      end
143
    end
D
Initial  
David Heinemeier Hansson 已提交
144

145
    # Return 'https://' if this is an SSL request and 'http://' otherwise.
D
Initial  
David Heinemeier Hansson 已提交
146
    def protocol
147
      ssl? ? 'https://' : 'http://'
D
Initial  
David Heinemeier Hansson 已提交
148 149
    end

150
    # Is this an SSL request?
151
    def ssl?
152
      @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
153
    end
154

155
    # Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
D
Initial  
David Heinemeier Hansson 已提交
156
    def path
157
      path = (uri = request_uri) ? uri.split('?').first : ''
158

159
      # Cut off the path to the installation directory if given
160 161 162 163
      root = relative_url_root
      path[0, root.length] = '' if root
      path || ''
    end
164

165
    # Returns the path minus the web server relative installation directory.
166 167 168
    # 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.
169
    def relative_url_root
170 171 172 173 174 175 176 177
      @@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
D
Initial  
David Heinemeier Hansson 已提交
178 179
    end

180
    # Returns the port number of this request as an integer.
D
Initial  
David Heinemeier Hansson 已提交
181
    def port
182
      @port_as_int ||= @env['SERVER_PORT'].to_i
D
Initial  
David Heinemeier Hansson 已提交
183
    end
184

185 186 187 188 189 190 191
    # Returns the standard port number for this request's protocol
    def standard_port
      case protocol
        when 'https://' then 443
        else 80
      end
    end
D
Initial  
David Heinemeier Hansson 已提交
192

193 194
    # Returns a port suffix like ":8080" if the port number of this request
    # is not the default HTTP port 80 or HTTPS port 443.
195
    def port_string
196
      (port == standard_port) ? '' : ":#{port}"
197 198
    end

199 200
    # Returns a host:port string for this request, such as example.com or
    # example.com:8080.
D
Initial  
David Heinemeier Hansson 已提交
201
    def host_with_port
202
      host + port_string
D
Initial  
David Heinemeier Hansson 已提交
203
    end
204

205 206
    def path_parameters=(parameters)
      @path_parameters = parameters
207 208
      @symbolized_path_parameters = @parameters = nil
    end
209

210 211
    def symbolized_path_parameters
      @symbolized_path_parameters ||= path_parameters.symbolize_keys
212
    end
D
Initial  
David Heinemeier Hansson 已提交
213

214 215 216
    def path_parameters
      @path_parameters ||= {}
    end
217 218

    # Returns the lowercase name of the HTTP server software.
219
    def server_software
220
      (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
221
    end
222

D
Initial  
David Heinemeier Hansson 已提交
223 224 225
    #--
    # Must be implemented in the concrete request
    #++
226
    def query_parameters #:nodoc:
D
Initial  
David Heinemeier Hansson 已提交
227 228
    end

229
    def request_parameters #:nodoc:
D
Initial  
David Heinemeier Hansson 已提交
230 231
    end

232 233
    # Returns the host for this request, such as example.com.
    def host
D
Initial  
David Heinemeier Hansson 已提交
234 235
    end

236
    def cookies #:nodoc:
D
Initial  
David Heinemeier Hansson 已提交
237 238
    end

239
    def session #:nodoc:
D
Initial  
David Heinemeier Hansson 已提交
240 241
    end

242 243 244 245
    def session=(session) #:nodoc:
      @session = session
    end

246
    def reset_session #:nodoc:
247
    end
D
Initial  
David Heinemeier Hansson 已提交
248 249
  end
end