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



18
19
20
21
22
# File 'lib/merb-core/dispatch/request.rb', line 18

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



5
6
7
# File 'lib/merb-core/dispatch/request.rb', line 5

def env
  @env
end

#route_paramsObject

def env def session def route_params



5
6
7
# File 'lib/merb-core/dispatch/request.rb', line 5

def route_params
  @route_params
end

#sessionObject

def env def session def route_params



5
6
7
# File 'lib/merb-core/dispatch/request.rb', line 5

def session
  @session
end

Class Method Details

.escape(s) ⇒ Object

Parameters

s<String>

String to URL escape.

returns

String

The escaped string.



407
408
409
410
411
# File 'lib/merb-core/dispatch/request.rb', line 407

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



543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# File 'lib/merb-core/dispatch/request.rb', line 543

def normalize_params(parms, name, val=nil)
  name =~ %r([\[\]]*([^\[\]]+)\]*)
  key = $1 || ''
  after = $' || ''
  
  if after == ""
    parms[key] = val
  elsif after == "[]"
    (parms[key] ||= []) << 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, ~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"


387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'lib/merb-core/dispatch/request.rb', line 387

def params_to_query_string(value, prefix = nil)
  case value
  when Array
    value.map { |v|
      params_to_query_string(v, "#{prefix}[]")
    } * "&"
  when Hash
    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.



457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
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
# File 'lib/merb-core/dispatch/request.rb', line 457

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)
  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 = '&;') ⇒ Object

Parameters

qs<String>

The query string.

d<String>

The query string divider. Defaults to “&”.

Returns

Mash

The parsed query string.

Examples

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


434
435
436
437
438
439
# File 'lib/merb-core/dispatch/request.rb', line 434

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

.unescape(s) ⇒ Object

Parameter

s<String>

String to URL unescape.

returns

String

The unescaped string.



418
419
420
421
422
# File 'lib/merb-core/dispatch/request.rb', line 418

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



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

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

#accept_charsetObject

Returns

String

The accepted character sets.



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

def accept_charset
  @env['HTTP_ACCEPT_CHARSET']
end

#accept_encodingObject

Returns

String

The accepted encodings.



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

def accept_encoding
  @env['HTTP_ACCEPT_ENCODING']
end

#accept_languageObject

Returns

String

The accepted language.



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

def accept_language
  @env['HTTP_ACCEPT_LANGUAGE']
end

#cache_controlObject

Returns

String

HTTP cache control.



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

def cache_control
  @env['HTTP_CACHE_CONTROL']
end

#connectionObject

Returns

String

The HTTP connection.



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

def connection
  @env['HTTP_CONNECTION']
end

#content_lengthObject

Returns

Fixnum

The request content length.



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

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

#content_typeObject

Returns

String

The request content type.



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

def content_type
  @env['CONTENT_TYPE']
end

#cookiesObject

Returns

Hash

The cookies for this request.



158
159
160
# File 'lib/merb-core/dispatch/request.rb', line 158

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.



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

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

#gatewayObject

Returns

String

The gateway.



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

def gateway
  @env['GATEWAY_INTERFACE']
end

#hostObject

Returns

String

The full hostname including the port.



339
340
341
# File 'lib/merb-core/dispatch/request.rb', line 339

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

#keep_aliveObject

Returns

String

Value of HTTP_KEEP_ALIVE.



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

def keep_alive
  @env['HTTP_KEEP_ALIVE']
end

#methodObject

Returns

Symbol

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

Notes

If the method is post, then the _method param will be checked for masquerading method.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/merb-core/dispatch/request.rb', line 32

def method
  @method ||= begin
    request_method = @env['REQUEST_METHOD'].downcase.to_sym
    case request_method
    when :get, :head, :put, :delete
      request_method
    when :post
      if self.class.parse_multipart_params
        m = body_and_query_params.merge(multipart_params)['_method']
      else  
        m = body_and_query_params['_method']
      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.



141
142
143
144
145
146
147
148
149
# File 'lib/merb-core/dispatch/request.rb', line 141

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



319
320
321
322
323
# File 'lib/merb-core/dispatch/request.rb', line 319

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.



327
328
329
# File 'lib/merb-core/dispatch/request.rb', line 327

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

#portObject

Returns

Fixnum

The server port.



333
334
335
# File 'lib/merb-core/dispatch/request.rb', line 333

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

#protocolObject

Returns

String

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



197
198
199
# File 'lib/merb-core/dispatch/request.rb', line 197

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

#query_stringObject

Returns

String

The query string.



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

def query_string
  @env['QUERY_STRING']  
end

#raw_postObject

Returns

String

The raw post.



164
165
166
167
# File 'lib/merb-core/dispatch/request.rb', line 164

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

#refererObject

Returns

String

The HTTP referer.



209
210
211
# File 'lib/merb-core/dispatch/request.rb', line 209

def referer
  @env['HTTP_REFERER']
end

#remote_ipObject

Returns

String

The remote IP address.



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/merb-core/dispatch/request.rb', line 179

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.



152
153
154
# File 'lib/merb-core/dispatch/request.rb', line 152

def reset_params!
  @params = nil
end

#script_nameObject

Returns

String

The script name.



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

def script_name
  @env['SCRIPT_NAME']
end

#server_nameObject

Returns

String

The server name.



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

def server_name
  @env['SERVER_NAME']
end

#server_softwareObject

Returns

String

The server software.



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

def server_software
  @env['SERVER_SOFTWARE']
end

#ssl?Boolean

Returns

Boolean:

True if the request is an SSL request.

Returns:

  • (Boolean)


203
204
205
# File 'lib/merb-core/dispatch/request.rb', line 203

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.



350
351
352
353
# File 'lib/merb-core/dispatch/request.rb', line 350

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

#uriObject

Returns

String

The request URI.



215
216
217
# File 'lib/merb-core/dispatch/request.rb', line 215

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

#user_agentObject

Returns

String

The HTTP user agent.



221
222
223
# File 'lib/merb-core/dispatch/request.rb', line 221

def user_agent
  @env['HTTP_USER_AGENT']
end

#versionObject

Returns

String

The HTTP version



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

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)


171
172
173
# File 'lib/merb-core/dispatch/request.rb', line 171

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