lib/merb/mixins/controller_mixin.rb in merb-0.1.0 vs lib/merb/mixins/controller_mixin.rb in merb-0.2.0
- old
+ new
@@ -1,8 +1,142 @@
module Merb
module ControllerMixin
-
+ 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
+ def parse_multipart(request,boundary)
+ boundary = "--#{boundary}"
+ paramhsh = MerbHash.new
+ buf = ""
+ content_length = @env['CONTENT_LENGTH'].to_i
+ input = request
+ boundary_size = boundary.size + EOL.size
+ bufsize = 16384
+ content_length -= boundary_size
+ status = input.read(boundary_size)
+ raise EOFError, "bad content body" unless status == boundary + EOL
+ rx = /(?:#{EOL})?#{Regexp.quote(boundary)}(#{EOL}|--)/
+
+ loop {
+ head = nil
+ body = ''
+ filename = content_type = name = nil
+
+ until head && buf =~ rx
+ if !head && i = buf.index("\r\n\r\n")
+ 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
+ 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
+
+ c = input.read(bufsize < content_length ? bufsize : content_length)
+ raise EOFError, "bad content body" if c.nil? || c.empty?
+ buf << c
+ content_length -= c.size
+ 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
+ body.rewind
+ data = {:filename => filename, :type => content_type, :tempfile => body}
+ else
+ data = body
+ end
+ paramhsh = normalize_params(paramhsh,name,data)
+ break if buf.empty? || content_length == -1
+ }
+ paramhsh
+ end
+
+ def normalize_params(parms, key, val)
+ case key
+ when /(.+)\[(.+)\]\[\]$/
+ parms[$1] ||= MerbHash.new
+ parms[$1] = normalize_params(parms[$1], "#{$2}[]", val)
+ when /(.+)\[(.+)\]$/
+ parms[$1] ||= MerbHash.new
+ parms[$1] = normalize_params(parms[$1], $2, val)
+ when /(.+)\[\]$/
+ (parms[$1] ||= []) << val
+ else
+ parms[key] = val if val
+ end
+ parms
+ end
+
+ # parses a query string or the payload of a POST
+ # request into the params hash. So for example:
+ # /foo?bar=nik&post[title]=heya&post[body]=whatever
+ # parses into:
+ # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
+ def query_parse(qs, d = '&;')
+ m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
+ (qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
+ k, v=unescape(p).split('=',2)
+ h.u(k.split(/[\]\[]+/).reverse.
+ inject(v) { |x,i| MerbHash[i,x] },&m)
+ }
+ end
+
+ # render using chunked encoding
+ # def stream
+ # prefix = '<p>'
+ # suffix = "</p>\r\n"
+ # render_chunked do
+ # IO.popen("cat /tmp/test.log") do |io|
+ # done = false
+ # until done == true do
+ # sleep 0.3
+ # line = io.gets.chomp
+ # if line == 'EOF'
+ # done = true
+ # else
+ # send_chunk(prefix + line + suffix)
+ # end
+ # end
+ # end
+ # end
+ # end
+ def render_chunked(&blk)
+ headers['Transfer-Encoding'] = 'chunked'
+ Proc.new {
+ response.send_status_no_connection_close(0)
+ response.send_header
+ blk.call
+ response.write("0\r\n\r\n")
+ }
+ end
+
+ # for use within a render_chunked response
+ def send_chunk(data)
+ response.write('%x' % data.size + "\r\n")
+ response.write(data + "\r\n")
+ end
+
# redirect to another url It can be like /foo/bar
# for redirecting within your same app. Or it can
# be a fully qualified url to another site.
def redirect(url)
MERB_LOGGER.info("Redirecting to: #{url}")
@@ -29,11 +163,11 @@
# This uses nginx X-Accel-Redirect header to send
# a file directly from nginx. See the nginx wiki:
# http://wiki.codemongers.com/NginxXSendfile
def nginx_send_file(file)
- headers['X-Accel-Redirect'] = file
+ headers['X-Accel-Redirect'] = File.expand_path(file)
return
end
# Sets a cookie to be included in the response.
def set_cookie(name, value, expires)
@@ -45,30 +179,28 @@
# the past.
def delete_cookie(name)
set_cookie(name, nil, Merb::Const::COOKIE_EXPIRED_TIME)
end
- # parses a query string or the payload of a POST
- # request into the params hash. So for example:
- # /foo?bar=nik&post[title]=heya&post[body]=whatever
- # parses into:
- # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
- def query_parse(qs, d = '&;')
- m = proc {|_,o,n|o.u(n,&m)rescue([*o]<<n)}
- (qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
- k, v=unescape(p).split('=',2)
- h.u(k.split(/[\]\[]+/).reverse.
- inject(v) { |x,i| MerbHash[i,x] },&m)
- }
- end
-
# creates a random token like:
# "b9a82e011694cc13a4249731b9e83cea"
def make_token
require 'digest/md5'
Digest::MD5.hexdigest("#{inspect}#{Time.now}#{rand}")
end
+
+ def rand_uuid
+ "%04x%04x-%04x-%04x-%04x-%06x%06x" % [
+ rand(0x0010000),
+ rand(0x0010000),
+ rand(0x0010000),
+ rand(0x0010000),
+ rand(0x0010000),
+ rand(0x1000000),
+ rand(0x1000000),
+ ]
+ end
def escape_xml(obj)
obj.to_s.gsub(/[&<>"']/) { |s| Merb::Const::ESCAPE_TABLE[s] }
end
alias h escape_xml
@@ -83,6 +215,6 @@
def unescape(s)
Mongrel::HttpRequest.unescape(s)
end
end
-end
+end
\ No newline at end of file