Class: ActionController::AbstractRequest

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::Memoizable
Defined in:
lib/action_controller/request.rb

Overview

CgiRequest and TestRequest provide concrete implementations.

Direct Known Subclasses

CgiRequest, RackRequest, TestRequest

Constant Summary collapse

HTTP_METHODS =
%w(get head put post delete options)
HTTP_METHOD_LOOKUP =
HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h }
TRUSTED_PROXIES =

Which IP addresses are “trusted proxies” that can be stripped from the right-hand-side of X-Forwarded-For

/^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#envObject (readonly)

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



24
25
26
# File 'lib/action_controller/request.rb', line 24

def env
  @env
end

Class Method Details

.clean_up_ajax_request_body!(body) ⇒ Object



596
597
598
599
# File 'lib/action_controller/request.rb', line 596

def clean_up_ajax_request_body!(body)
  body.chop! if body[-1] == 0
  body.gsub!(/&_=$/, '')
end

.extract_content_type_without_parameters(content_type_with_parameters) ⇒ Object



592
593
594
# File 'lib/action_controller/request.rb', line 592

def extract_content_type_without_parameters(content_type_with_parameters)
  $1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
end

.extract_multipart_boundary(content_type_with_parameters) ⇒ Object



584
585
586
587
588
589
590
# File 'lib/action_controller/request.rb', line 584

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

.parse_multipart_form_parameters(body, boundary, body_size, env) ⇒ Object



580
581
582
# File 'lib/action_controller/request.rb', line 580

def parse_multipart_form_parameters(body, boundary, body_size, env)
  parse_request_parameters(read_multipart(body, boundary, body_size, env))
end

.parse_query_parameters(query_string) ⇒ Object



541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/action_controller/request.rb', line 541

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

.parse_request_parameters(params) ⇒ Object



555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/action_controller/request.rb', line 555

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

.relative_url_root=(relative_url_root) ⇒ Object



12
13
14
15
16
17
# File 'lib/action_controller/request.rb', line 12

def self.relative_url_root=(relative_url_root)
  ActiveSupport::Deprecation.warn(
    "ActionController::AbstractRequest.relative_url_root= has been renamed." +
    "You can now set it with config.action_controller.relative_url_root=", caller)
  ActionController::Base.relative_url_root=relative_url_root
end

Instance Method Details

#acceptsObject

Returns the accepted MIME type for the request.



93
94
95
96
97
98
99
100
101
# File 'lib/action_controller/request.rb', line 93

def accepts
  header = @env['HTTP_ACCEPT'].to_s.strip

  if header.empty?
    [content_type, Mime::ALL].compact
  else
    Mime::Type.parse(header)
  end
end

#bodyObject

The request body is an IO input stream. If the RAW_POST_DATA environment variable is already set, wrap it in a StringIO.



417
418
419
420
421
422
423
424
# File 'lib/action_controller/request.rb', line 417

def body
  if raw_post = env['RAW_POST_DATA']
    raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
    StringIO.new(raw_post)
  else
    body_stream
  end
end

#body_streamObject

– Must be implemented in the concrete request ++



449
450
# File 'lib/action_controller/request.rb', line 449

def body_stream #:nodoc:
end

#cache_formatObject



189
190
191
# File 'lib/action_controller/request.rb', line 189

def cache_format
  parameters[:format]
end

#content_lengthObject

Returns the content length of the request as an integer.



78
79
80
# File 'lib/action_controller/request.rb', line 78

def content_length
  @env['CONTENT_LENGTH'].to_i
end

#content_typeObject

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.



87
88
89
# File 'lib/action_controller/request.rb', line 87

def content_type
  Mime::Type.lookup(content_type_without_parameters)
end

#cookiesObject

:nodoc:



452
453
# File 'lib/action_controller/request.rb', line 452

def cookies #:nodoc:
end

#delete?Boolean

Is this a DELETE request? Equivalent to request.method == :delete.

Returns:

  • (Boolean)


59
60
61
# File 'lib/action_controller/request.rb', line 59

def delete?
  request_method == :delete
end

#domain(tld_length = 1) ⇒ Object

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



317
318
319
320
321
# File 'lib/action_controller/request.rb', line 317

def domain(tld_length = 1)
  return nil unless named_host?(host)

  host.split('.').last(1 + tld_length).join('.')
end

#etag_matches?(etag) ⇒ Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/action_controller/request.rb', line 119

def etag_matches?(etag)
  if_none_match && if_none_match == etag
end

#formatObject

Returns the Mime type for the format used in the request.

GET /posts/5.xml   | request.format => Mime::XML
GET /posts/5.xhtml | request.format => Mime::HTML
GET /posts/5       | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>


144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/action_controller/request.rb', line 144

def format
  @format ||=
    if parameters[:format]
      Mime::Type.lookup_by_extension(parameters[:format])
    elsif ActionController::Base.use_accept_header
      accepts.first
    elsif xhr?
      Mime::Type.lookup_by_extension("js")
    else
      Mime::Type.lookup_by_extension("html")
    end
end

#format=(extension) ⇒ Object

Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension.

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


169
170
171
172
# File 'lib/action_controller/request.rb', line 169

def format=(extension)
  parameters[:format] = extension.to_s
  @format = Mime::Type.lookup_by_extension(parameters[:format])
end

#fresh?(response) ⇒ Boolean

Check response freshness (Last-Modified and ETag) against request If-Modified-Since and If-None-Match conditions. If both headers are supplied, both must match, or the request is not considered fresh.

Returns:

  • (Boolean)


126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/action_controller/request.rb', line 126

def fresh?(response)
  case
  when if_modified_since && if_none_match 
    not_modified?(response.last_modified) && etag_matches?(response.etag) 
  when if_modified_since 
    not_modified?(response.last_modified) 
  when if_none_match 
    etag_matches?(response.etag) 
  else 
    false 
  end 
end

#get?Boolean

Is this a GET (or HEAD) request? Equivalent to request.method == :get.

Returns:

  • (Boolean)


44
45
46
# File 'lib/action_controller/request.rb', line 44

def get?
  method == :get
end

#head?Boolean

Is this a HEAD request? Since request.method sees HEAD as :get, this method checks the actual HTTP method directly.

Returns:

  • (Boolean)


65
66
67
# File 'lib/action_controller/request.rb', line 65

def head?
  request_method == :head
end

#headersObject

Provides access to the request’s HTTP headers, for example:

request.headers["Content-Type"] # => "text/plain"


72
73
74
# File 'lib/action_controller/request.rb', line 72

def headers
  ActionController::Http::Headers.new(@env)
end

#hostObject

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



279
280
281
# File 'lib/action_controller/request.rb', line 279

def host
  raw_host_with_port.sub(/:\d+$/, '')
end

#host_with_portObject

Returns a host:port string for this request, such as “example.com” or “example.com:8080”.



286
287
288
# File 'lib/action_controller/request.rb', line 286

def host_with_port
  "#{host}#{port_string}"
end

#if_modified_sinceObject



104
105
106
107
108
# File 'lib/action_controller/request.rb', line 104

def if_modified_since
  if since = env['HTTP_IF_MODIFIED_SINCE']
    Time.rfc2822(since) rescue nil
  end
end

#if_none_matchObject



111
112
113
# File 'lib/action_controller/request.rb', line 111

def if_none_match
  env['HTTP_IF_NONE_MATCH']
end

#methodObject

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.



39
40
41
# File 'lib/action_controller/request.rb', line 39

def method
  request_method == :head ? :get : request_method
end

#not_modified?(modified_at) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/action_controller/request.rb', line 115

def not_modified?(modified_at)
  if_modified_since && modified_at && if_modified_since >= modified_at
end

#parametersObject

Returns both GET and POST parameters in a single hash.



391
392
393
# File 'lib/action_controller/request.rb', line 391

def parameters
  @parameters ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
end

#pathObject

Returns the interpreted path to requested resource after all the installation directory of this application was taken into account.



371
372
373
374
375
376
377
# File 'lib/action_controller/request.rb', line 371

def path
  path = (uri = request_uri) ? uri.split('?').first.to_s : ''

  # Cut off the path to the installation directory if given
  path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '')
  path || ''
end

#path_parametersObject

Returns a hash with the parameters used to form the path of the request. Returned hash keys are strings:

{'action' => 'my_action', 'controller' => 'my_controller'}

See symbolized_path_parameters for symbolized keys.



411
412
413
# File 'lib/action_controller/request.rb', line 411

def path_parameters
  @path_parameters ||= {}
end

#path_parameters=(parameters) ⇒ Object

:nodoc:



395
396
397
398
# File 'lib/action_controller/request.rb', line 395

def path_parameters=(parameters) #:nodoc:
  @path_parameters = parameters
  @symbolized_path_parameters = @parameters = nil
end

#portObject

Returns the port number of this request as an integer.



292
293
294
295
296
297
298
# File 'lib/action_controller/request.rb', line 292

def port
  if raw_host_with_port =~ /:(\d+)$/
    $1.to_i
  else
    standard_port
  end
end

#port_stringObject

Returns a port suffix like “:8080” if the port number of this request is not the default HTTP port 80 or HTTPS port 443.



311
312
313
# File 'lib/action_controller/request.rb', line 311

def port_string
  port == standard_port ? '' : ":#{port}"
end

#post?Boolean

Is this a POST request? Equivalent to request.method == :post.

Returns:

  • (Boolean)


49
50
51
# File 'lib/action_controller/request.rb', line 49

def post?
  request_method == :post
end

#protocolObject

Returns ‘https://’ if this is an SSL request and ‘http://’ otherwise.



259
260
261
# File 'lib/action_controller/request.rb', line 259

def protocol
  ssl? ? 'https://' : 'http://'
end

#put?Boolean

Is this a PUT request? Equivalent to request.method == :put.

Returns:

  • (Boolean)


54
55
56
# File 'lib/action_controller/request.rb', line 54

def put?
  request_method == :put
end

#query_parametersObject



436
437
438
# File 'lib/action_controller/request.rb', line 436

def query_parameters
  @query_parameters ||= self.class.parse_query_parameters(query_string)
end

#query_stringObject

Returns the query string, accounting for server idiosyncrasies.



334
335
336
337
338
339
340
# File 'lib/action_controller/request.rb', line 334

def query_string
  if uri = @env['REQUEST_URI']
    uri.split('?', 2)[1] || ''
  else
    @env['QUERY_STRING'] || ''
  end
end

#raw_host_with_portObject

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



270
271
272
273
274
275
276
# File 'lib/action_controller/request.rb', line 270

def raw_host_with_port
  if forwarded = env["HTTP_X_FORWARDED_HOST"]
    forwarded.split(/,\s?/).last
  else
    env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
  end
end

#raw_postObject

Read the request body. This is useful for web services that need to work with raw requests directly.



382
383
384
385
386
387
388
# File 'lib/action_controller/request.rb', line 382

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

#referrerObject Also known as: referer



430
431
432
# File 'lib/action_controller/request.rb', line 430

def referrer
  @env['HTTP_REFERER']
end

#remote_addrObject



426
427
428
# File 'lib/action_controller/request.rb', line 426

def remote_addr
  @env['REMOTE_ADDR']
end

#remote_ipObject

Determines 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 if REMOTE_ADDR is a proxy. HTTP_X_FORWARDED_FOR may be a comma- delimited list in the case of multiple chained proxies; the last address which is not trusted is the originating IP.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/action_controller/request.rb', line 211

def remote_ip
  remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].split(',').collect(&:strip)

  unless remote_addr_list.blank?
    not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES}
    return not_trusted_addrs.first unless not_trusted_addrs.empty?
  end
  remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')

  if @env.include? 'HTTP_CLIENT_IP'
    if remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
      # We don't know which came from the proxy, and which from the user
      raise ActionControllerError.new(<<EOM)
IP spoofing attack?!
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
EOM
    end

    return @env['HTTP_CLIENT_IP']
  end

  if remote_ips
    while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
      remote_ips.pop
    end

    return remote_ips.last.strip
  end

  @env['REMOTE_ADDR']
end

#request_methodObject

The true HTTP request method as a lowercase symbol, such as :get. UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS.



28
29
30
31
32
33
# File 'lib/action_controller/request.rb', line 28

def request_method
  method = @env['REQUEST_METHOD']
  method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank?

  HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}")
end

#request_parametersObject



440
441
442
# File 'lib/action_controller/request.rb', line 440

def request_parameters
  @request_parameters ||= parse_formatted_request_parameters
end

#request_uriObject

Returns the request URI, accounting for server idiosyncrasies. WEBrick includes the full URL. IIS leaves REQUEST_URI blank.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/action_controller/request.rb', line 345

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.
    uri = @env['PATH_INFO'].to_s

    if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
      uri = uri.sub(/#{script_filename}\//, '')
    end

    env_qs = @env['QUERY_STRING'].to_s
    uri += "?#{env_qs}" unless env_qs.empty?

    if uri.blank?
      @env.delete('REQUEST_URI')
    else
      @env['REQUEST_URI'] = uri
    end
  end
end

#reset_sessionObject

:nodoc:



462
463
# File 'lib/action_controller/request.rb', line 462

def reset_session #:nodoc:
end

#server_softwareObject

Returns the lowercase name of the HTTP server software.



246
247
248
# File 'lib/action_controller/request.rb', line 246

def server_software
  (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
end

#sessionObject

:nodoc:



455
456
# File 'lib/action_controller/request.rb', line 455

def session #:nodoc:
end

#session=(session) ⇒ Object

:nodoc:



458
459
460
# File 'lib/action_controller/request.rb', line 458

def session=(session) #:nodoc:
  @session = session
end

#ssl?Boolean

Is this an SSL request?

Returns:

  • (Boolean)


265
266
267
# File 'lib/action_controller/request.rb', line 265

def ssl?
  @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
end

#standard_portObject

Returns the standard port number for this request’s protocol.



302
303
304
305
306
307
# File 'lib/action_controller/request.rb', line 302

def standard_port
  case protocol
    when 'https://' then 443
    else 80
  end
end

#subdomains(tld_length = 1) ⇒ Object

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



327
328
329
330
331
# File 'lib/action_controller/request.rb', line 327

def subdomains(tld_length = 1)
  return [] unless named_host?(host)
  parts = host.split('.')
  parts[0..-(tld_length+2)]
end

#symbolized_path_parametersObject

The same as path_parameters with explicitly symbolized keys.



401
402
403
# File 'lib/action_controller/request.rb', line 401

def symbolized_path_parameters
  @symbolized_path_parameters ||= path_parameters.symbolize_keys
end

#template_formatObject

Returns a symbolized version of the :format parameter of the request. If no format is given it returns :jsfor Ajax requests and :html otherwise.



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/action_controller/request.rb', line 177

def template_format
  parameter_format = parameters[:format]

  if parameter_format
    parameter_format
  elsif xhr?
    :js
  else
    :html
  end
end

#urlObject

Returns the complete URL used for this request.



253
254
255
# File 'lib/action_controller/request.rb', line 253

def url
  protocol + host_with_port + request_uri
end

#xml_http_request?Boolean Also known as: xhr?

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

Returns:

  • (Boolean)


196
197
198
# File 'lib/action_controller/request.rb', line 196

def xml_http_request?
  !(@env['HTTP_X_REQUESTED_WITH'] !~ /XMLHttpRequest/i)
end