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 _finish if content_length && pos >= content_length
68 if (nr = pos - seen) > 0 && mtime <= (Time.now.to_i - frequency)
69 backend.incr(upload_id, nr)
71 self.mtime = Time.now.to_i
76 self.seen = backend.finish(upload_id).seen
77 self.content_length ||= self.seen
83 # we had an unknown length and just had to read in everything to get it
84 if content_length.nil?
98 rv.nil? ? _finish : _incr(rv.size)
103 rv = input.read(*args)
104 rv.nil? || rv.size == 0 ? _finish : _incr(rv.size)
109 input.each do |chunk| # usually just a line