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


605
606
607
608
# File 'lib/action_controller/request.rb', line 605

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


601
602
603
# File 'lib/action_controller/request.rb', line 601

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


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

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


589
590
591
# File 'lib/action_controller/request.rb', line 589

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


550
551
552
553
554
555
556
557
558
559
560
561
562
# File 'lib/action_controller/request.rb', line 550

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


564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/action_controller/request.rb', line 564

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.


426
427
428
429
430
431
432
433
# File 'lib/action_controller/request.rb', line 426

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 ++


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

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:


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

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


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

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.


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

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.


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

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.


420
421
422
# File 'lib/action_controller/request.rb', line 420

def path_parameters
  @path_parameters ||= {}
end

#path_parameters=(parameters) ⇒ Object

:nodoc:


404
405
406
407
# File 'lib/action_controller/request.rb', line 404

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.


320
321
322
# File 'lib/action_controller/request.rb', line 320

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


445
446
447
# File 'lib/action_controller/request.rb', line 445

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

#query_stringObject

Returns the query string, accounting for server idiosyncrasies.


343
344
345
346
347
348
349
# File 'lib/action_controller/request.rb', line 343

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.


391
392
393
394
395
396
397
# File 'lib/action_controller/request.rb', line 391

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


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

def referrer
  @env['HTTP_REFERER']
end

#relative_url_rootObject

Returns the value of ActionController::Base.relative_url_root. This method is deprecated as the value is an application wide setting, not something which changes per request.


312
313
314
315
316
# File 'lib/action_controller/request.rb', line 312

def relative_url_root
  ActiveSupport::Deprecation.warn(
    "relative_url_root is now set application-wide, use ActionController::Base.relative_url_root instead.", caller)
  ActionController::Base.relative_url_root
end

#remote_addrObject


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

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


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

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.


354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/action_controller/request.rb', line 354

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:


471
472
# File 'lib/action_controller/request.rb', line 471

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:


464
465
# File 'lib/action_controller/request.rb', line 464

def session #:nodoc:
end

#session=(session) ⇒ Object

:nodoc:


467
468
469
# File 'lib/action_controller/request.rb', line 467

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


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

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.


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

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