Changeset 167

Show
Ignore:
Timestamp:
01/23/07 23:12:44 (2 years ago)
Author:
e.@brainspl.at
Message:

Added feature for chunked encoding for streaming. Thanks to mathieul@gmail.com for the idea. I implemented it in a thread safe way so multiple streams can be rendered at once. Very cool stuff. closes #12

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Rakefile

    r158 r167  
    1616 
    1717NAME = "merb" 
    18 VERS = "0.1.0
     18VERS = "0.1.1
    1919CLEAN.include ['**/.*.sw?', '*.gem', '.config'] 
    2020RDOC_OPTS = ['--quiet', '--title', "Merb Documentation", 
  • trunk/lib/merb.rb

    r160 r167  
    1313 
    1414module Merb 
    15   VERSION='0.1.0' unless defined?VERSION 
     15  VERSION='0.1.1' unless defined?VERSION 
    1616  class Server 
    1717    class << self 
  • trunk/lib/merb/merb_controller.rb

    r151 r167  
    3131    # file uploads by writing a tempfile and passing a reference  
    3232    # in params. 
    33     def initialize(req, env, args, method=(env['REQUEST_METHOD']||'GET')) 
     33    def initialize(req, env, args, res, method=(env['REQUEST_METHOD']||'GET')) 
    3434      env = ::MerbHash[env.to_hash] 
    35       @status, @method, @env, @headers, @root = 200, method.downcase.to_sym, env,  
     35      @status, @method, @response, @env, @headers, @root = 200, method.downcase.to_sym, res, env,  
    3636          {'Content-Type' =>'text/html'}, env['SCRIPT_NAME'].sub(/\/$/,'') 
    3737      cookies = query_parse(env['HTTP_COOKIE'], ';,') 
     
    150150      @session 
    151151    end 
     152     
     153    # accessor for @response. Please use response and  
     154    # never @response directly. 
     155    def response 
     156      @response 
     157    end 
     158     
    152159     
    153160    # shared_accessor sets up a class instance variable that can 
  • trunk/lib/merb/merb_handler.rb

    r152 r167  
     1class Mongrel::HttpResponse 
     2  NO_CLOSE_STATUS_FORMAT = "HTTP/1.1 %d %s\r\n".freeze 
     3  def send_status_no_connection_close(content_length=@body.length) 
     4    if not @status_sent 
     5      @header['Content-Length'] = content_length unless @status == 304 
     6      write(NO_CLOSE_STATUS_FORMAT % [@status, Mongrel::HTTP_STATUS_CODES[@status]]) 
     7      @status_sent = true 
     8    end 
     9  end 
     10end   
     11 
     12 
     13 
    114class MerbHandler < Mongrel::HttpHandler 
    215  @@file_only_methods = ["GET","HEAD"] 
     
    5568        # multiple file uploads can be done at once. 
    5669        controller = nil 
    57         controller, action = handle(request
     70        controller, action = handle(request, response
    5871        MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}\nParsing HTTP Input took: #{Time.now - start} seconds") 
    5972         
     
    6477        # at this point because those processes are thread safe. This  
    6578        # gives us the best trade off for multi threaded performance  
    66         # of thread safe, and a lock around calls to your controller actions. 
     79        # of thread safe code paths, and the smallest lock possible 
     80        # around calls to your controller actions. 
    6781        @guard.synchronize { 
    6882          controller.dispatch(action) 
     
    114128          controller.body.close 
    115129        end 
     130      elsif Proc === controller.body 
     131        controller.body.call 
    116132      else 
    117133        MERB_LOGGER.info("Response status: #{response.status}\nComplete Request took: #{Time.now - start} seconds\n\n") 
     
    130146  # returns a 2 element tuple of: 
    131147  # [controller, action] 
    132   def handle(request
     148  def handle(request, response
    133149    path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/') 
    134150    path = path[0..-2] if (path[-1] == ?/) && path.size > 1 
    135151    route = Merb::RouteMatcher.new.route_request(path) 
    136     [ instantiate_controller(route[:controller], request.body, request.params, route),  
     152    [ instantiate_controller(route[:controller], request.body, request.params, route, response),  
    137153      route[:action] ] 
    138154  end 
     
    142158  # into a new object passing in the request and response. 
    143159  # this is where your Merb::Controller is instantiated. 
    144   def instantiate_controller(controller_name, req, env, params)  
     160  def instantiate_controller(controller_name, req, env, params, res)  
    145161    if !File.exist?(DIST_ROOT+"/app/controllers/#{controller_name.snake_case}.rb") 
    146162      raise Merb::MissingControllerFile 
     
    148164    begin 
    149165      controller_name.import 
    150       return Object.const_get( controller_name.camel_case ).new(req, env, params
     166      return Object.const_get( controller_name.camel_case ).new(req, env, params, res
    151167    rescue RuntimeError 
    152168      warn "Error getting instance of '#{controller_name.camel_case}': #{$!}" 
  • trunk/lib/merb/mixins/render_mixin.rb

    r142 r167  
    130130        return xml.target! 
    131131      }  
     132    end 
     133     
     134    # render using chunked encoding 
     135    # def stream 
     136    #   prefix = '<p>' 
     137    #   suffix = "</p>\r\n" 
     138    #   render_chunked do 
     139    #     IO.popen("cat /tmp/test.log") do |io| 
     140    #       done = false 
     141    #       until done == true do 
     142    #         sleep 0.3 
     143    #         line = io.gets.chomp 
     144    #         if line == 'EOF' 
     145    #           done = true 
     146    #         else 
     147    #           send_chunk(prefix + line + suffix) 
     148    #         end 
     149    #       end 
     150    #     end 
     151    #   end 
     152    # end 
     153    def render_chunked(&blk) 
     154      headers['Transfer-Encoding'] = 'chunked' 
     155      Proc.new { 
     156        response.send_status_no_connection_close(0) 
     157        response.send_header 
     158        blk.call 
     159        response.write("0\r\n\r\n") 
     160      } 
     161    end 
     162     
     163    def send_chunk(data) 
     164      response.write('%x' % data.size + "\r\n") 
     165      response.write(data + "\r\n") 
    132166    end 
    133167