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