1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
| | # -*- encoding: binary -*-
module Upr
# the underlying middlware for for wrapping env["rack.input"],
# this should typically be installed before other middlewares
# that may wrap env["rack.input"] in the middleware chain.
class InputWrapper < Struct.new(:app, :path_info, :frequency, :backend,
:input, :pos, :seen, :content_length,
:upload_id, :mtime)
include Params
def initialize(app, options = {})
super(app,
Array(options[:path_info] || nil),
options[:frequency] || 1,
options[:backend])
# support :drb for compatibility with mongrel_upload_progress
if options[:drb]
backend and raise ArgumentError, ":backend and :drb are incompatible"
require 'drb'
DRb.start_service
self.backend = DRbObject.new(nil, options[:drb])
elsif String === backend
# allow people to use strings in case their backend gets
# lazy-loaded (like an ActiveRecord model)
self.backend = eval(backend)
else
self.backend ||= Upr::Monitor.new
end
end
def call(env)
if path_info.empty? || path_info.include?(env["PATH_INFO"])
# benefit curl users...
/\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]
length = env["CONTENT_LENGTH"] and length = length.to_i
chunked = env["TRANSFER_ENCODING"] =~ %r{\Achunked\z}i and length = nil
if chunked || (length && length > 0)
if uid = extract_upload_id(env)
return dup._call(env, uid, length)
end
end
end
app.call(env)
end
def _call(env, uid, length)
self.upload_id = uid
self.mtime = self.pos = self.seen = 0
self.input = env["rack.input"]
env["rack.input"] = self
self.content_length = length
backend.start(upload_id, length)
app.call(env)
end
def _incr(nr)
self.pos += nr
_finish if content_length && pos >= content_length
if (nr = pos - seen) > 0 && mtime <= (Time.now.to_i - frequency)
backend.incr(upload_id, nr)
self.seen = pos
self.mtime = Time.now.to_i
end
end
def _finish
self.seen = backend.finish(upload_id).seen
self.content_length ||= self.seen
end
def size
rv = input.size
# we had an unknown length and just had to read in everything to get it
if content_length.nil?
_incr(rv - seen)
_finish
end
rv
end
def rewind
self.pos = 0
input.rewind
end
def gets
rv = input.gets
rv.nil? ? _finish : _incr(rv.size)
rv
end
def read(*args)
rv = input.read(*args)
rv.nil? || rv.size == 0 ? _finish : _incr(rv.size)
rv
end
def each(&block)
input.each do |chunk| # usually just a line
_incr(chunk.size)
yield chunk
end
_finish
end
end
end
|