1 # -*- encoding: binary -*-
5 raise LoadError, "either json or json-pure is required"
10 # JSON protocol based on Lighttpd's mod_uploadprogress
11 # http://trac.lighttpd.net/trac/wiki/Docs:ModUploadProgress
12 class JSON < Struct.new(:frequency, :backend, :upload_id)
16 # We use this in case length is nil when clients send chunked uploads
19 SLEEP_CLASS = defined?(Actor) ? Actor : Kernel
22 'Content-Type' => 'application/json',
23 'Cache-Control' => 'no-cache',
26 def initialize(options = {})
27 super(options[:frequency] || 1, options[:backend], options[:upload_id])
29 # support :drb for compatibility with mongrel_upload_progress
31 backend and raise ArgumentError, ":backend and :drb are incompatible"
34 self.backend = DRbObject.new(nil, options[:drb])
35 elsif String === backend
36 # allow people to use strings in case their backend gets
37 # lazy-loaded (like an ActiveRecord model)
38 self.backend = eval(backend)
40 raise ArgumentError, "backend MUST be specified"
43 # only for use with rails_proc
44 upload_id.nil? and self.upload_id = options[:env]
47 def rails_render_options
49 self.upload_id = extract_upload_id(env)
51 :content_type => 'application/json',
57 if status = backend.read(upload_id)
59 _json_object(:state => 'done')
60 elsif status.seen == 0
61 _json_object(:state => 'starting')
63 _error_msg("upload failed")
68 timeout = Time.now + 2
69 until status = backend.read(upload_id)
70 SLEEP_CLASS.sleep(0.1)
71 return _error_msg("couldn't get status") if Time.now > timeout
73 _json_object(:state => 'starting')
77 # Rack interface reservced for future use with streaming AJAX
79 if uid = extract_upload_id(env)
82 [ 400, RESP_HEADERS.dup, [ _error_msg("upload_id not given") ] ]
86 # Rack interface reservced for future use with streaming AJAX
88 sleeper = defined?(Actor) ? Actor : Kernel
89 timeout = Time.now + 2
91 yield _json_object(:state => 'starting') << eol
93 until status = backend.read(upload_id)
95 break if Time.now > timeout
99 yield _update_msg(status) << eol
100 break if status.done?
101 sleeper.sleep(frequency)
102 end while status = backend.read(upload_id)
103 yield _json_object(:state => 'done') << eol
105 yield _error_msg("couldn't get status") << eol
108 yield _error_msg(e.message) << eol
112 # Rack interface reservced for future use with streaming AJAX
115 _self.upload_id = uid
116 [ 200, RESP_HEADERS.dup, _self ]
120 _json_object(:state => 'error', :status => 400, :message => msg)
123 def _json_object(options)
124 # $stderr.syswrite "#{options.inspect} #{$upr.inspect}\n"
128 def _update_msg(status)
129 raise "client error" if status.error?
130 received = status.seen
131 size = status.length || INT_MAX
132 _json_object(:state => 'uploading', :size => size, :received => received)