1 # -*- encoding: binary -*-
5 raise LoadError, "either json or json-pure is required"
11 # JSON protocol based on Lighttpd's mod_uploadprogress
12 # http://trac.lighttpd.net/trac/wiki/Docs:ModUploadProgress
13 class JSON < Struct.new(:frequency, :backend, :upload_id)
17 # We use this in case length is nil when clients send chunked uploads
20 SLEEP_CLASS = defined?(Actor) ? Actor : Kernel
23 'Content-Type' => 'application/json',
24 'Cache-Control' => 'no-cache',
27 def initialize(options = {})
28 super(options[:frequency] || 1, options[:backend], options[:upload_id])
30 # support :drb for compatibility with mongrel_upload_progress
32 backend and raise ArgumentError, ":backend and :drb are incompatible"
35 self.backend = DRbObject.new(nil, options[:drb])
36 elsif String === backend
37 # allow people to use strings in case their backend gets
38 # lazy-loaded (like an ActiveRecord model)
39 self.backend = eval(backend)
41 raise ArgumentError, "backend MUST be specified"
44 # only for use with rails_proc
45 upload_id.nil? and self.upload_id = options[:env]
48 def rails_render_options
50 self.upload_id = extract_upload_id(env)
51 text = if Rack::Request.new(env).GET.include?("long")
52 Proc.new { |response,output| each { |line| output.write(line) } }
56 { :content_type => 'application/json', :text => text }
60 if status = backend.read(upload_id)
62 _json_object(:state => 'done')
63 elsif status.seen == 0
64 _json_object(:state => 'starting')
66 _error_msg("upload failed")
71 timeout = Time.now + 2
72 until status = backend.read(upload_id)
73 SLEEP_CLASS.sleep(0.1)
74 return _error_msg("couldn't get status") if Time.now > timeout
76 _json_object(:state => 'starting')
80 # Rack interface reservced for future use with streaming AJAX
82 if uid = extract_upload_id(env)
85 [ 400, RESP_HEADERS.dup, [ _error_msg("upload_id not given") ] ]
89 # Rack interface reservced for future use with streaming AJAX
91 sleeper = defined?(Actor) ? Actor : Kernel
92 timeout = Time.now + 2
94 yield _json_object(:state => 'starting') << eol
96 until status = backend.read(upload_id)
98 break if Time.now > timeout
102 yield _update_msg(status) << eol
103 break if status.done?
104 sleeper.sleep(frequency)
105 end while status = backend.read(upload_id)
106 yield _json_object(:state => 'done') << eol
108 yield _error_msg("couldn't get status") << eol
111 yield _error_msg(e.message) << eol
115 # Rack interface reservced for future use with streaming AJAX
118 _self.upload_id = uid
119 [ 200, RESP_HEADERS.dup, _self ]
123 _json_object(:state => 'error', :status => 400, :message => msg)
126 def _json_object(options)
127 # $stderr.syswrite "#{options.inspect} #{$upr.inspect}\n"
131 def _update_msg(status)
132 raise "client error" if status.error?
133 received = status.seen
134 size = status.length || INT_MAX
135 _json_object(:state => 'uploading', :size => size, :received => received)