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
22 # our default response headers, we need to set no-transform to
23 # prevent deflaters from compressing our already-small small input
24 # and also to prevent buffering/corking of the response inside
27 'Content-Type' => 'application/json',
28 'Cache-Control' => 'no-cache, no-transform',
31 def initialize(options = {})
32 super(options[:frequency] || 1, options[:backend], options[:upload_id])
34 # support :drb for compatibility with mongrel_upload_progress
36 backend and raise ArgumentError, ":backend and :drb are incompatible"
39 self.backend = DRbObject.new(nil, options[:drb])
40 elsif String === backend
41 # allow people to use strings in case their backend gets
42 # lazy-loaded (like an ActiveRecord model)
43 self.backend = eval(backend)
45 raise ArgumentError, "backend MUST be specified"
48 # only for use with rails_proc
49 upload_id.nil? and self.upload_id = options[:env]
52 def rails_render_options
54 self.upload_id = extract_upload_id(env)
55 text = if Rack::Request.new(env).GET.include?("long")
56 Proc.new { |response,output| each { |line| output.write(line) } }
60 { :content_type => 'application/json', :text => text }
64 if status = backend.read(upload_id)
66 _json_object(:state => 'done')
67 elsif status.seen == 0
68 _json_object(:state => 'starting')
70 _error_msg("upload failed")
75 timeout = Time.now + 2
76 until status = backend.read(upload_id)
77 SLEEP_CLASS.sleep(0.1)
78 return _error_msg("couldn't get status") if Time.now > timeout
80 _json_object(:state => 'starting')
84 # Rack interface reservced for future use with streaming AJAX
86 if uid = extract_upload_id(env)
89 [ 400, RESPONSE_HEADERS.dup, [ _error_msg("upload_id not given") ] ]
93 # Rack interface reservced for future use with streaming AJAX
95 sleeper = defined?(Actor) ? Actor : Kernel
96 timeout = Time.now + 2
98 yield _json_object(:state => 'starting') << eol
100 until status = backend.read(upload_id)
102 break if Time.now > timeout
106 yield _update_msg(status) << eol
107 break if status.done?
108 sleeper.sleep(frequency)
109 end while status = backend.read(upload_id)
110 yield _json_object(:state => 'done') << eol
112 yield _error_msg("couldn't get status") << eol
115 yield _error_msg(e.message) << eol
119 # Rack interface reservced for future use with streaming AJAX
122 _self.upload_id = uid
123 [ 200, RESPONSE_HEADERS.dup, _self ]
127 _json_object(:state => 'error', :status => 400, :message => msg)
130 def _json_object(options)
131 # $stderr.syswrite "#{options.inspect} #{$upr.inspect}\n"
135 def _update_msg(status)
136 raise "client error" if status.error?
137 received = status.seen
138 size = status.length || INT_MAX
139 _json_object(:state => 'uploading', :size => size, :received => received)