Class: Merb::Request

Constants

NameValue
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

Attributes

NameRead/write?
env RW
route_params RW
session RW

Public Class Methods


escape (s)

Parameters

s<String>:String to URL escape.

returns

String:The escaped string.
     # File lib/merb-core/dispatch/request.rb, line 407
407:       def escape(s)
408:         s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
409:           '%'+$1.unpack('H2'*$1.size).join('%').upcase
410:         }.tr(' ', '+')
411:       end

new (rack_env)

Initial the request object.

Parameters

http_request<~params:~[], ~body:IO>:An object like an HTTP Request.
    # File lib/merb-core/dispatch/request.rb, line 18
18:     def initialize(rack_env)
19:       @env  = rack_env
20:       @body = rack_env['rack.input']
21:       @route_params = {}
22:     end

normalize_params (parms, name, val=nil)

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
     # File lib/merb-core/dispatch/request.rb, line 543
543:       def normalize_params(parms, name, val=nil)
544:         name =~ %r([\[\]]*([^\[\]]+)\]*)
545:         key = $1 || ''
546:         after = $' || ''
547:         
548:         if after == ""
549:           parms[key] = val
550:         elsif after == "[]"
551:           (parms[key] ||= []) << val
552:         else
553:           parms[key] ||= {}
554:           parms[key] = normalize_params(parms[key], after, val)
555:         end
556:         parms
557:       end

params_to_query_string (value, prefix = nil)

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"
     # File lib/merb-core/dispatch/request.rb, line 387
387:       def params_to_query_string(value, prefix = nil)
388:         case value
389:         when Array
390:           value.map { |v|
391:             params_to_query_string(v, "#{prefix}[]")
392:           } * "&"
393:         when Hash
394:           value.map { |k, v|
395:             params_to_query_string(v, prefix ? "#{prefix}[#{Merb::Request.escape(k)}]" : Merb::Request.escape(k))
396:           } * "&"
397:         else
398:           "#{prefix}=#{Merb::Request.escape(value)}"
399:         end
400:       end

parse_multipart (request, boundary, content_length)

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.
     # File lib/merb-core/dispatch/request.rb, line 457
457:       def parse_multipart(request, boundary, content_length)
458:         boundary = "--#{boundary}"
459:         paramhsh = {}
460:         buf = ""
461:         input = request
462:         input.binmode if defined? input.binmode
463:         boundary_size = boundary.size + EOL.size
464:         bufsize = 16384
465:         content_length -= boundary_size
466:         status = input.read(boundary_size)
467:         raise ControllerExceptions::MultiPartParseError, "bad content body:\n'#{status}' should == '#{boundary + EOL}'"  unless status == boundary + EOL
468:         rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
469:         loop {
470:           head = nil
471:           body = ''
472:           filename = content_type = name = nil
473:           read_size = 0
474:           until head && buf =~ rx
475:             i = buf.index("\r\n\r\n")
476:             if( i == nil && read_size == 0 && content_length == 0 )
477:               content_length = -1
478:               break
479:             end
480:             if !head && i
481:               head = buf.slice!(0, i+2) # First \r\n
482:               buf.slice!(0, 2)          # Second \r\n
483:               filename = head[FILENAME_REGEX, 1]
484:               content_type = head[CONTENT_TYPE_REGEX, 1]
485:               name = head[NAME_REGEX, 1]
486:             
487:               if filename && !filename.empty?
488:                 body = Tempfile.new(:Merb)
489:                 body.binmode if defined? body.binmode
490:               end
491:               next
492:             end
493:           
494:             # Save the read body part.
495:             if head && (boundary_size+4 < buf.size)
496:               body << buf.slice!(0, buf.size - (boundary_size+4))
497:             end
498:           
499:             read_size = bufsize < content_length ? bufsize : content_length
500:             if( read_size > 0 )
501:               c = input.read(read_size)
502:               raise ControllerExceptions::MultiPartParseError, "bad content body"  if c.nil? || c.empty?
503:               buf << c
504:               content_length -= c.size
505:             end
506:           end
507:         
508:           # Save the rest.
509:           if i = buf.index(rx)
510:             body << buf.slice!(0, i)
511:             buf.slice!(0, boundary_size+2)
512:           
513:             content_length = -1  if $1 == "--"
514:           end
515:         
516:           if filename && !filename.empty?   
517:             body.rewind
518:             data = { 
519:               :filename => File.basename(filename),  
520:               :content_type => content_type,  
521:               :tempfile => body, 
522:               :size => File.size(body.path) 
523:             }
524:           else
525:             data = body
526:           end
527:           paramhsh = normalize_params(paramhsh,name,data)
528:           break  if buf.empty? || content_length == -1
529:         }
530:         paramhsh
531:       end

query_parse (qs, d = '&;')

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" } }
     # File lib/merb-core/dispatch/request.rb, line 434
434:       def query_parse(qs, d = '&;')
435:         (qs||'').split(/[#{d}] */n).inject({}) { |h,p| 
436:           key, value = unescape(p).split('=',2)
437:           normalize_params(h, key, value)
438:         }.to_mash
439:       end

unescape (s)

Parameter

s<String>:String to URL unescape.

returns

String:The unescaped string.
     # File lib/merb-core/dispatch/request.rb, line 418
418:       def unescape(s)
419:         s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
420:           [$1.delete('%')].pack('H*')
421:         }
422:       end

Public Instance Methods


accept ()

Returns

String:The accepted response types. Defaults to "*/*".
     # File lib/merb-core/dispatch/request.rb, line 287
287:     def accept
288:       @env['HTTP_ACCEPT'].blank? ? "*/*" : @env['HTTP_ACCEPT']
289:     end

accept_charset ()

Returns

String:The accepted character sets.
     # File lib/merb-core/dispatch/request.rb, line 269
269:     def accept_charset
270:       @env['HTTP_ACCEPT_CHARSET']
271:     end

accept_encoding ()

Returns

String:The accepted encodings.
     # File lib/merb-core/dispatch/request.rb, line 233
233:     def accept_encoding
234:       @env['HTTP_ACCEPT_ENCODING']
235:     end

accept_language ()

Returns

String:The accepted language.
     # File lib/merb-core/dispatch/request.rb, line 251
251:     def accept_language
252:       @env['HTTP_ACCEPT_LANGUAGE']
253:     end

ajax? ()

Alias for xml_http_request?


cache_control ()

Returns

String:HTTP cache control.
     # File lib/merb-core/dispatch/request.rb, line 245
245:     def cache_control
246:       @env['HTTP_CACHE_CONTROL']
247:     end

connection ()

Returns

String:The HTTP connection.
     # File lib/merb-core/dispatch/request.rb, line 293
293:     def connection
294:       @env['HTTP_CONNECTION']
295:     end

content_length ()

Returns

Fixnum:The request content length.
     # File lib/merb-core/dispatch/request.rb, line 311
311:     def content_length
312:       @content_length ||= @env[Merb::Const::CONTENT_LENGTH].to_i
313:     end

content_type ()

Returns

String:The request content type.
     # File lib/merb-core/dispatch/request.rb, line 305
305:     def content_type
306:       @env['CONTENT_TYPE']
307:     end

cookies ()

Returns

Hash:The cookies for this request.
     # File lib/merb-core/dispatch/request.rb, line 158
158:     def cookies
159:       @cookies ||= self.class.query_parse(@env[Merb::Const::HTTP_COOKIE], ';,')
160:     end

domain (tld_length = 1)

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.
     # File lib/merb-core/dispatch/request.rb, line 362
362:     def domain(tld_length = 1)
363:       host.split('.').last(1 + tld_length).join('.').sub(/:\d+$/,'')
364:     end

gateway ()

Returns

String:The gateway.
     # File lib/merb-core/dispatch/request.rb, line 281
281:     def gateway
282:       @env['GATEWAY_INTERFACE']
283:     end

host ()

Returns

String:The full hostname including the port.
     # File lib/merb-core/dispatch/request.rb, line 339
339:     def host
340:       @env['HTTP_X_FORWARDED_HOST'] || @env['HTTP_HOST'] 
341:     end

keep_alive ()

Returns

String:Value of HTTP_KEEP_ALIVE.
     # File lib/merb-core/dispatch/request.rb, line 263
263:     def keep_alive
264:       @env['HTTP_KEEP_ALIVE']
265:     end

method ()

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.

    # File lib/merb-core/dispatch/request.rb, line 32
32:     def method
33:       @method ||= begin
34:         request_method = @env['REQUEST_METHOD'].downcase.to_sym
35:         case request_method
36:         when :get, :head, :put, :delete
37:           request_method
38:         when :post
39:           if self.class.parse_multipart_params
40:             m = body_and_query_params.merge(multipart_params)['_method']
41:           else  
42:             m = body_and_query_params['_method']
43:           end
44:           m.downcase! if m
45:           METHODS.include?(m) ? m.to_sym : :post
46:         else
47:           raise "Unknown REQUEST_METHOD: #{@env['REQUEST_METHOD']}"
48:         end
49:       end
50:     end

params ()

Returns

Hash:All request parameters.

Notes

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

     # File lib/merb-core/dispatch/request.rb, line 141
141:     def params
142:       @params ||= begin
143:         h = body_and_query_params.merge(route_params)      
144:         h.merge!(multipart_params) if self.class.parse_multipart_params && multipart_params
145:         h.merge!(json_params) if self.class.parse_json_params && json_params
146:         h.merge!(xml_params) if self.class.parse_xml_params && xml_params
147:         h
148:       end
149:     end

path ()

Returns

String:The URI without the query string. Strips trailing "/" and reduces duplicate "/" to a single "/".
     # File lib/merb-core/dispatch/request.rb, line 319
319:     def path
320:       path = (uri.empty? ? '/' : uri.split('?').first).squeeze("/")
321:       path = path[0..-2] if (path[-1] == ?/) && path.size > 1
322:       path
323:     end

path_info ()

Returns

String:The path info.
     # File lib/merb-core/dispatch/request.rb, line 327
327:     def path_info
328:       @path_info ||= self.class.unescape(@env['PATH_INFO'])
329:     end

port ()

Returns

Fixnum:The server port.
     # File lib/merb-core/dispatch/request.rb, line 333
333:     def port
334:       @env['SERVER_PORT'].to_i
335:     end

protocol ()

Returns

String:The protocol, i.e. either "https://" or "http://" depending on the HTTPS header.
     # File lib/merb-core/dispatch/request.rb, line 197
197:     def protocol
198:       ssl? ? 'https://' : 'http://'
199:     end

query_string ()

Returns

String:The query string.
     # File lib/merb-core/dispatch/request.rb, line 299
299:     def query_string
300:       @env['QUERY_STRING']  
301:     end

raw_post ()

Returns

String:The raw post.
     # File lib/merb-core/dispatch/request.rb, line 164
164:     def raw_post
165:       @body.rewind if @body.respond_to?(:rewind)
166:       @raw_post ||= @body.read
167:     end

referer ()

Returns

String:The HTTP referer.
     # File lib/merb-core/dispatch/request.rb, line 209
209:     def referer
210:       @env['HTTP_REFERER']
211:     end

remote_ip ()

Returns

String:The remote IP address.
     # File lib/merb-core/dispatch/request.rb, line 179
179:     def remote_ip
180:       return @env['HTTP_CLIENT_IP'] if @env.include?('HTTP_CLIENT_IP')
181:     
182:       if @env.include?(Merb::Const::HTTP_X_FORWARDED_FOR) then
183:         remote_ips = @env[Merb::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
184:           ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
185:         end
186:     
187:         return remote_ips.first.strip unless remote_ips.empty?
188:       end
189:     
190:       return @env[Merb::Const::REMOTE_ADDR]
191:     end

reset_params! ()

Resets the params to a nil value.

     # File lib/merb-core/dispatch/request.rb, line 152
152:     def reset_params!
153:       @params = nil
154:     end

script_name ()

Returns

String:The script name.
     # File lib/merb-core/dispatch/request.rb, line 239
239:     def script_name
240:       @env['SCRIPT_NAME']
241:     end

server_name ()

Returns

String:The server name.
     # File lib/merb-core/dispatch/request.rb, line 227
227:     def server_name
228:       @env['SERVER_NAME']
229:     end

server_software ()

Returns

String:The server software.
     # File lib/merb-core/dispatch/request.rb, line 257
257:     def server_software
258:       @env['SERVER_SOFTWARE']
259:     end

ssl? ()

Returns

Boolean::True if the request is an SSL request.
     # File lib/merb-core/dispatch/request.rb, line 203
203:     def ssl?
204:       @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
205:     end

subdomains (tld_length = 1)

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.
     # File lib/merb-core/dispatch/request.rb, line 350
350:     def subdomains(tld_length = 1)
351:       parts = host.split('.')
352:       parts[0..-(tld_length+2)]
353:     end

uri ()

Returns

String:The request URI.
     # File lib/merb-core/dispatch/request.rb, line 215
215:     def uri
216:       @env['REQUEST_URI'] || @env['REQUEST_PATH']
217:     end

user_agent ()

Returns

String:The HTTP user agent.
     # File lib/merb-core/dispatch/request.rb, line 221
221:     def user_agent
222:       @env['HTTP_USER_AGENT']
223:     end

version ()

Returns

String:The HTTP version
     # File lib/merb-core/dispatch/request.rb, line 275
275:     def version
276:       @env['HTTP_VERSION']
277:     end

xhr? ()

Alias for xml_http_request?


xml_http_request? ()

Returns

Boolean:If the request is an XML HTTP request.
     # File lib/merb-core/dispatch/request.rb, line 171
171:     def xml_http_request?
172:       not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
173:     end