Class: Merb::Request

Inherits:
Object show all
Defined in:
lib/merb-core/dispatch/request.rb

Direct Known Subclasses

Test::RequestHelper::FakeRequest

Constant Summary collapse

METHODS =
%w{get post put delete head options}
NAME_REGEX =
/Content-Disposition:.* name="?([^\";]*)"?/ni.freeze
CONTENT_TYPE_REGEX =
/Content-Type: (.*)\r\n/ni.freeze
FILENAME_REGEX =
/Content-Disposition:.* filename="?([^\";]*)"?/ni.freeze
CRLF =
"\r\n".freeze
EOL =
CRLF

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rack_env) ⇒ Request

Initial the request object.

Parameters

http_request<~params:~[], ~body:IO>

An object like an HTTP Request.



31
32
33
34
35
# File 'lib/merb-core/dispatch/request.rb', line 31

def initialize(rack_env)
  @env  = rack_env
  @body = rack_env['rack.input']
  @route_params = {}
end

Instance Attribute Details

#envObject

def env def session def route_params



7
8
9
# File 'lib/merb-core/dispatch/request.rb', line 7

def env
  @env
end

#route_paramsObject

def env def session def route_params



7
8
9
# File 'lib/merb-core/dispatch/request.rb', line 7

def route_params
  @route_params
end

#sessionObject

def env def session def route_params



7
8
9
# File 'lib/merb-core/dispatch/request.rb', line 7

def session
  @session
end

Class Method Details

.escape(s) ⇒ Object

Parameters

s<String>

String to URL escape.

returns

String

The escaped string.



443
444
445
446
447
# File 'lib/merb-core/dispatch/request.rb', line 443

def escape(s)
  s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
    '%'+$1.unpack('H2'*$1.size).join('%').upcase
  }.tr(' ', '+')
end

.normalize_params(parms, name, val = nil) ⇒ Object

Converts a query string snippet to a hash and adds it to existing parameters.

Parameters

parms<Hash>

Parameters to add the normalized parameters to.

name<String>

The key of the parameter to normalize.

val<String>

The value of the parameter.

Returns

Hash

Normalized parameters



583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
# File 'lib/merb-core/dispatch/request.rb', line 583

def normalize_params(parms, name, val=nil)
  name =~ %r([\[\]]*([^\[\]]+)\]*)
  key = $1 || ''
  after = $' || ''
  
  if after == ""
    parms[key] = val
  elsif after == "[]"
    (parms[key] ||= []) << val
  elsif after =~ %r(^\[\])
    parms[key] ||= []
    parms[key] << normalize_params({}, after, val)
  else
    parms[key] ||= {}
    parms[key] = normalize_params(parms[key], after, val)
  end
  parms
end

.params_to_query_string(value, prefix = nil) ⇒ Object

Parameters

value<Array, Hash, Dictionary ~to_s>

The value for the query string.

prefix<~to_s>

The prefix to add to the query string keys.

Returns

String

The query string.

Alternatives

If the value is a string, the prefix will be used as the key.

Examples

params_to_query_string(10, "page")
  # => "page=10"
params_to_query_string({ :page => 10, :word => "ruby" })
  # => "page=10&word=ruby"
params_to_query_string({ :page => 10, :word => "ruby" }, "search")
  # => "search[page]=10&search[word]=ruby"
params_to_query_string([ "ice-cream", "cake" ], "shopping_list")
  # => "shopping_list[]=ice-cream&shopping_list[]=cake"


423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/merb-core/dispatch/request.rb', line 423

def params_to_query_string(value, prefix = nil)
  case value
  when Array
    value.map { |v|
      params_to_query_string(v, "#{prefix}[]")
    } * "&"
  when Hash, Dictionary
    value.map { |k, v|
      params_to_query_string(v, prefix ? "#{prefix}[#{Merb::Request.escape(k)}]" : Merb::Request.escape(k))
    } * "&"
  else
    "#{prefix}=#{Merb::Request.escape(value)}"
  end
end

.parse_multipart(request, boundary, content_length) ⇒ Object

Parameters

request<IO>

The raw request.

boundary<String>

The boundary string.

content_length<Fixnum>

The length of the content.

Raises

ControllerExceptions::MultiPartParseError

Failed to parse request.

Returns

Hash

The parsed request.



496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
# File 'lib/merb-core/dispatch/request.rb', line 496

def parse_multipart(request, boundary, content_length)
  boundary = "--#{boundary}"
  paramhsh = {}
  buf = ""
  input = request
  input.binmode if defined? input.binmode
  boundary_size = boundary.size + EOL.size
  bufsize = 16384
  content_length -= boundary_size
  status = input.read(boundary_size)
  return {} if status == nil || status.empty?
  raise ControllerExceptions::MultiPartParseError, "bad content body:\n'#{status}' should == '#{boundary + EOL}'"  unless status == boundary + EOL
  rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
  loop {
    head = nil
    body = ''
    filename = content_type = name = nil
    read_size = 0
    until head && buf =~ rx
      i = buf.index("\r\n\r\n")
      if( i == nil && read_size == 0 && content_length == 0 )
        content_length = -1
        break
      end
      if !head && i
        head = buf.slice!(0, i+2) # First \r\n
        buf.slice!(0, 2)          # Second \r\n
        filename = head[FILENAME_REGEX, 1]
        content_type = head[CONTENT_TYPE_REGEX, 1]
        name = head[NAME_REGEX, 1]
      
        if filename && !filename.empty?
          body = Tempfile.new(:Merb)
          body.binmode if defined? body.binmode
        end
        next
      end
    
      # Save the read body part.
      if head && (boundary_size+4 < buf.size)
        body << buf.slice!(0, buf.size - (boundary_size+4))
      end
    
      read_size = bufsize < content_length ? bufsize : content_length
      if( read_size > 0 )
        c = input.read(read_size)
        raise ControllerExceptions::MultiPartParseError, "bad content body"  if c.nil? || c.empty?
        buf << c
        content_length -= c.size
      end
    end
  
    # Save the rest.
    if i = buf.index(rx)
      body << buf.slice!(0, i)
      buf.slice!(0, boundary_size+2)
    
      content_length = -1  if $1 == "--"
    end
  
    if filename && !filename.empty?   
      body.rewind
      data = { 
        :filename => File.basename(filename),  
        :content_type => content_type,  
        :tempfile => body, 
        :size => File.size(body.path) 
      }
    else
      data = body
    end
    paramhsh = normalize_params(paramhsh,name,data)
    break  if buf.empty? || content_length == -1
  }
  paramhsh
end

.query_parse(qs, d = '&;', preserve_order = false) ⇒ Object

Parameters

qs<String>

The query string.

d<String>

The query string divider. Defaults to “&”.

preserve_order<Boolean>

Preserve order of args. Defaults to false.

Returns

Mash

The parsed query string (Dictionary if preserve_order is set).

Examples

query_parse("bar=nik&post[body]=heya")
  # => { :bar => "nik", :post => { :body => "heya" } }


471
472
473
474
475
476
477
478
# File 'lib/merb-core/dispatch/request.rb', line 471

def query_parse(qs, d = '&;', preserve_order = false)
  qh = preserve_order ? Dictionary.new : {}
  (qs||'').split(/[#{d}] */n).inject(qh) { |h,p| 
    key, value = unescape(p).split('=',2)
    normalize_params(h, key, value)
  }
  preserve_order ? qh : qh.to_mash
end

.unescape(s) ⇒ Object

Parameter

s<String>

String to URL unescape.

returns

String

The unescaped string.



454
455
456
457
458
# File 'lib/merb-core/dispatch/request.rb', line 454

def unescape(s)
  s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
    [$1.delete('%')].pack('H*')
  }
end

Instance Method Details

#acceptObject

Returns

String

The accepted response types. Defaults to “/”.



323
324
325
# File 'lib/merb-core/dispatch/request.rb', line 323

def accept
  @env['HTTP_ACCEPT'].blank? ? "*/*" : @env['HTTP_ACCEPT']
end

#accept_charsetObject

Returns

String

The accepted character sets.



305
306
307
# File 'lib/merb-core/dispatch/request.rb', line 305

def accept_charset
  @env['HTTP_ACCEPT_CHARSET']
end

#accept_encodingObject

Returns

String

The accepted encodings.



269
270
271
# File 'lib/merb-core/dispatch/request.rb', line 269

def accept_encoding
  @env['HTTP_ACCEPT_ENCODING']
end

#accept_languageObject

Returns

String

The accepted language.



287
288
289
# File 'lib/merb-core/dispatch/request.rb', line 287

def accept_language
  @env['HTTP_ACCEPT_LANGUAGE']
end

#cache_controlObject

Returns

String

HTTP cache control.



281
282
283
# File 'lib/merb-core/dispatch/request.rb', line 281

def cache_control
  @env['HTTP_CACHE_CONTROL']
end

#connectionObject

Returns

String

The HTTP connection.



329
330
331
# File 'lib/merb-core/dispatch/request.rb', line 329

def connection
  @env['HTTP_CONNECTION']
end

#content_lengthObject

Returns

Fixnum

The request content length.



347
348
349
# File 'lib/merb-core/dispatch/request.rb', line 347

def content_length
  @content_length ||= @env[Merb::Const::CONTENT_LENGTH].to_i
end

#content_typeObject

Returns

String

The request content type.



341
342
343
# File 'lib/merb-core/dispatch/request.rb', line 341

def content_type
  @env['CONTENT_TYPE']
end

#cookiesObject

Returns

Hash

The cookies for this request.



188
189
190
# File 'lib/merb-core/dispatch/request.rb', line 188

def cookies
  @cookies ||= self.class.query_parse(@env[Merb::Const::HTTP_COOKIE], ';,')
end

#domain(tld_length = 1) ⇒ Object

Parameters

tld_length<Fixnum>

Number of domains levels to inlclude in the top level domain. Defaults to 1.

Returns

String

The full domain name without the port number.



398
399
400
# File 'lib/merb-core/dispatch/request.rb', line 398

def domain(tld_length = 1)
  host.split('.').last(1 + tld_length).join('.').sub(/:\d+$/,'')
end

#full_uriObject

Returns

String

The full URI, including protocol and host



245
246
247
# File 'lib/merb-core/dispatch/request.rb', line 245

def full_uri
  protocol + host + uri
end

#gatewayObject

Returns

String

The gateway.



317
318
319
# File 'lib/merb-core/dispatch/request.rb', line 317

def gateway
  @env['GATEWAY_INTERFACE']
end

#hostObject

Returns

String

The full hostname including the port.



375
376
377
# File 'lib/merb-core/dispatch/request.rb', line 375

def host
  @env['HTTP_X_FORWARDED_HOST'] || @env['HTTP_HOST'] 
end

#keep_aliveObject

Returns

String

Value of HTTP_KEEP_ALIVE.



299
300
301
# File 'lib/merb-core/dispatch/request.rb', line 299

def keep_alive
  @env['HTTP_KEEP_ALIVE']
end

#messageObject



172
173
174
175
176
177
178
179
# File 'lib/merb-core/dispatch/request.rb', line 172

def message
  return {} unless params[:_message]
  begin
    Marshal.load(Merb::Request.unescape(params[:_message]).unpack("m").first)
  rescue ArgumentError
    {}
  end
end

#methodObject

Returns

Symbol

The name of the request method, e.g. :get.

Notes

If the method is post, then the blocks specified in http_method_overrides will be checked for the masquerading method. The block will get the controller yielded to it. The first matching workaround wins. To disable this behavior, set http_method_overrides = []



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/merb-core/dispatch/request.rb', line 47

def method
  @method ||= begin
    request_method = @env['REQUEST_METHOD'].downcase.to_sym
    case request_method
    when :get, :head, :put, :delete, :options
      request_method
    when :post
      m = nil
      self.class.http_method_overrides.each do |o|
        m ||= o.call(self); break if m
      end
      m.downcase! if m
      METHODS.include?(m) ? m.to_sym : :post
    else
      raise "Unknown REQUEST_METHOD: #{@env['REQUEST_METHOD']}"
    end
  end
end

#paramsObject

Returns

Hash

All request parameters.

Notes

The order of precedence for the params is XML, JSON, multipart, body and request string.



162
163
164
165
166
167
168
169
170
# File 'lib/merb-core/dispatch/request.rb', line 162

def params
  @params ||= begin
    h = body_and_query_params.merge(route_params)      
    h.merge!(multipart_params) if self.class.parse_multipart_params && multipart_params
    h.merge!(json_params) if self.class.parse_json_params && json_params
    h.merge!(xml_params) if self.class.parse_xml_params && xml_params
    h
  end
end

#pathObject

Returns

String

The URI without the query string. Strips trailing “/” and reduces duplicate “/” to a single “/”.



355
356
357
358
359
# File 'lib/merb-core/dispatch/request.rb', line 355

def path
  path = (uri.empty? ? '/' : uri.split('?').first).squeeze("/")
  path = path[0..-2] if (path[-1] == ?/) && path.size > 1
  path
end

#path_infoObject

Returns

String

The path info.



363
364
365
# File 'lib/merb-core/dispatch/request.rb', line 363

def path_info
  @path_info ||= self.class.unescape(@env['PATH_INFO'])
end

#portObject

Returns

Fixnum

The server port.



369
370
371
# File 'lib/merb-core/dispatch/request.rb', line 369

def port
  @env['SERVER_PORT'].to_i
end

#protocolObject

Returns

String

The protocol, i.e. either “https://” or “http://” depending on the HTTPS header.



227
228
229
# File 'lib/merb-core/dispatch/request.rb', line 227

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

#query_stringObject

Returns

String

The query string.



335
336
337
# File 'lib/merb-core/dispatch/request.rb', line 335

def query_string
  @env['QUERY_STRING']  
end

#raw_postObject

Returns

String

The raw post.



194
195
196
197
# File 'lib/merb-core/dispatch/request.rb', line 194

def raw_post
  @body.rewind if @body.respond_to?(:rewind)
  @raw_post ||= @body.read
end

#refererObject

Returns

String

The HTTP referer.



239
240
241
# File 'lib/merb-core/dispatch/request.rb', line 239

def referer
  @env['HTTP_REFERER']
end

#remote_ipObject

Returns

String

The remote IP address.



209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/merb-core/dispatch/request.rb', line 209

def remote_ip
  return @env['HTTP_CLIENT_IP'] if @env.include?('HTTP_CLIENT_IP')

  if @env.include?(Merb::Const::HTTP_X_FORWARDED_FOR) then
    remote_ips = @env[Merb::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
      ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
    end

    return remote_ips.first.strip unless remote_ips.empty?
  end

  return @env[Merb::Const::REMOTE_ADDR]
end

#reset_params!Object

Resets the params to a nil value.



182
183
184
# File 'lib/merb-core/dispatch/request.rb', line 182

def reset_params!
  @params = nil
end

#script_nameObject

Returns

String

The script name.



275
276
277
# File 'lib/merb-core/dispatch/request.rb', line 275

def script_name
  @env['SCRIPT_NAME']
end

#server_nameObject

Returns

String

The server name.



263
264
265
# File 'lib/merb-core/dispatch/request.rb', line 263

def server_name
  @env['SERVER_NAME']
end

#server_softwareObject

Returns

String

The server software.



293
294
295
# File 'lib/merb-core/dispatch/request.rb', line 293

def server_software
  @env['SERVER_SOFTWARE']
end

#ssl?Boolean

Returns

Boolean:

True if the request is an SSL request.

Returns:

  • (Boolean)


233
234
235
# File 'lib/merb-core/dispatch/request.rb', line 233

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

#subdomains(tld_length = 1) ⇒ Object

Parameters

tld_length<Fixnum>

Number of domains levels to inlclude in the top level domain. Defaults to 1.

Returns

Array

All the subdomain parts of the host.



386
387
388
389
# File 'lib/merb-core/dispatch/request.rb', line 386

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

#uriObject

Returns

String

The request URI.



251
252
253
# File 'lib/merb-core/dispatch/request.rb', line 251

def uri
  @env['REQUEST_PATH'] || @env['REQUEST_URI']
end

#user_agentObject

Returns

String

The HTTP user agent.



257
258
259
# File 'lib/merb-core/dispatch/request.rb', line 257

def user_agent
  @env['HTTP_USER_AGENT']
end

#versionObject

Returns

String

The HTTP version



311
312
313
# File 'lib/merb-core/dispatch/request.rb', line 311

def version
  @env['HTTP_VERSION']
end

#xml_http_request?Boolean Also known as: xhr?, ajax?

Returns

Boolean

If the request is an XML HTTP request.

Returns:

  • (Boolean)


201
202
203
# File 'lib/merb-core/dispatch/request.rb', line 201

def xml_http_request?
  not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
end