1 # -*- encoding: binary -*-
5 # the underlying middlware for for wrapping env["rack.input"],
6 # this should typically be installed before other middlewares
7 # that may wrap env["rack.input"] in the middleware chain.
8 class InputWrapper < Struct.new(:app, :path_info, :frequency, :backend,
9 :input, :pos, :seen, :content_length,
12 def initialize(app, options = {})
14 Array(options[:path_info] || nil),
15 options[:frequency] || 3,
18 # support :drb for compatibility with mongrel_upload_progress
20 backend and raise ArgumentError, ":backend and :drb are incompatible"
23 self.backend = DRbObject.new(nil, options[:drb])
24 elsif String === backend
25 # allow people to use strings in case their backend gets
26 # lazy-loaded (like an ActiveRecord model)
27 self.backend = eval(backend)
29 self.backend ||= Upr::Monitor.new
34 if path_info.empty? || path_info.include?(env["PATH_INFO"])
35 # benefit curl users...
36 /\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]
38 length = env["CONTENT_LENGTH"] and length = length.to_i
39 env["TRANSFER_ENCODING"] =~ %r{\Achunked\z}i and length = nil
40 if length.nil? || length > 0
41 req = Rack::Request.new(env)
43 # can't blindly parse params here since we don't want to read
44 # the POST body if there is one, so only parse stuff in the
46 if uid = req.GET["upload_id"]
47 return dup._call(env, uid, length)
54 def _call(env, uid, length)
55 self.upload_id = env["upr.upload_id"] = uid
56 self.mtime = self.pos = self.seen = 0
57 self.input = env["rack.input"]
58 env["rack.input"] = self
59 self.content_length = length
60 backend.start(upload_id, length)
67 if (nr = pos - seen) > 0 && mtime <= (Time.now.to_i - frequency)
68 backend.incr(upload_id, nr)
70 self.mtime = Time.now.to_i
77 # we had an unknown length and just had to read in everything to get it
78 if content_length.nil?
80 self.content_length = rv
92 _incr(rv.size) unless rv.nil?
97 rv = input.read(*args)
98 _incr(rv.size) unless rv.nil?
103 input.each do |chunk| # usually just a line