Class: Tus::Server
- Inherits:
-
Roda
- Object
- Roda
- Tus::Server
- Defined in:
- lib/tus/server.rb
Constant Summary collapse
- SUPPORTED_VERSIONS =
["1.0.0"]
- SUPPORTED_EXTENSIONS =
[ "creation", "creation-defer-length", "termination", "expiration", "concatenation", "concatenation-unfinished", "checksum", ]
- SUPPORTED_CHECKSUM_ALGORITHMS =
%w[sha1 sha256 sha384 sha512 md5 crc32]
- RESUMABLE_CONTENT_TYPE =
"application/offset+octet-stream"
Instance Method Summary collapse
- #created!(location) ⇒ Object
- #error!(status, message) ⇒ Object
- #expiration_interval ⇒ Object
- #expiration_time ⇒ Object
- #handle_cors! ⇒ Object
-
#handle_range_request!(length) ⇒ Object
“Range” header handling logic copied from Rack::File.
- #max_size ⇒ Object
- #no_content! ⇒ Object
- #not_found!(message = "Upload not found") ⇒ Object
- #storage ⇒ Object
- #validate_content_length!(current_offset, length) ⇒ Object
- #validate_content_type! ⇒ Object
- #validate_tus_resumable! ⇒ Object
- #validate_upload_checksum!(input) ⇒ Object
- #validate_upload_concat! ⇒ Object
- #validate_upload_finished!(length, current_offset) ⇒ Object
- #validate_upload_length! ⇒ Object
- #validate_upload_metadata! ⇒ Object
- #validate_upload_offset!(current_offset) ⇒ Object
Instance Method Details
#created!(location) ⇒ Object
304 305 306 307 308 |
# File 'lib/tus/server.rb', line 304 def created!(location) response.status = 201 response.headers["Location"] = location request.halt end |
#error!(status, message) ⇒ Object
314 315 316 317 318 |
# File 'lib/tus/server.rb', line 314 def error!(status, ) response.status = status response.write() unless request.head? request.halt end |
#expiration_interval ⇒ Object
332 333 334 |
# File 'lib/tus/server.rb', line 332 def expiration_interval opts[:expiration_interval] end |
#expiration_time ⇒ Object
328 329 330 |
# File 'lib/tus/server.rb', line 328 def expiration_time opts[:expiration_time] end |
#handle_cors! ⇒ Object
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/tus/server.rb', line 283 def handle_cors! origin = request.headers["Origin"] return if origin.to_s == "" response.headers["Access-Control-Allow-Origin"] = origin if request. response.headers["Access-Control-Allow-Methods"] = "POST, GET, HEAD, PATCH, DELETE, OPTIONS" response.headers["Access-Control-Allow-Headers"] = "Origin, X-Requested-With, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata" response.headers["Access-Control-Max-Age"] = "86400" else response.headers["Access-Control-Expose-Headers"] = "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata" end end |
#handle_range_request!(length) ⇒ Object
“Range” header handling logic copied from Rack::File
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/tus/server.rb', line 258 def handle_range_request!(length) if Rack.release >= "2.0" ranges = Rack::Utils.get_byte_ranges(request.headers["Range"], length) else ranges = Rack::Utils.byte_ranges(request.env, length) end if ranges.nil? || ranges.length > 1 # No ranges, or multiple ranges (which we don't support): response.status = 200 range = 0..length-1 elsif ranges.empty? # Unsatisfiable. Return error, and file size: response.headers["Content-Range"] = "bytes */#{length}" error!(416, "Byte range unsatisfiable") else # Partial content: range = ranges[0] response.status = 206 response.headers["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{length}" end range end |
#max_size ⇒ Object
324 325 326 |
# File 'lib/tus/server.rb', line 324 def max_size opts[:max_size] end |
#no_content! ⇒ Object
299 300 301 302 |
# File 'lib/tus/server.rb', line 299 def no_content! response.status = 204 request.halt end |
#not_found!(message = "Upload not found") ⇒ Object
310 311 312 |
# File 'lib/tus/server.rb', line 310 def not_found!( = "Upload not found") error!(404, ) end |
#storage ⇒ Object
320 321 322 |
# File 'lib/tus/server.rb', line 320 def storage opts[:storage] || Tus::Storage::Filesystem.new("data") end |
#validate_content_length!(current_offset, length) ⇒ Object
207 208 209 210 211 212 213 214 |
# File 'lib/tus/server.rb', line 207 def validate_content_length!(current_offset, length) if length error!(403, "Cannot modify completed upload") if current_offset == length error!(413, "Size of this chunk surpasses Upload-Length") if Integer(request.content_length) + current_offset > length else error!(413, "Size of this chunk surpasses Tus-Max-Size") if Integer(request.content_length) + current_offset > max_size end end |
#validate_content_type! ⇒ Object
170 171 172 |
# File 'lib/tus/server.rb', line 170 def validate_content_type! error!(415, "Invalid Content-Type header") if request.content_type != RESUMABLE_CONTENT_TYPE end |
#validate_tus_resumable! ⇒ Object
174 175 176 177 178 179 180 181 |
# File 'lib/tus/server.rb', line 174 def validate_tus_resumable! client_version = request.headers["Tus-Resumable"] unless SUPPORTED_VERSIONS.include?(client_version) response.headers["Tus-Version"] = SUPPORTED_VERSIONS.join(",") error!(412, "Unsupported version") end end |
#validate_upload_checksum!(input) ⇒ Object
247 248 249 250 251 252 253 254 255 |
# File 'lib/tus/server.rb', line 247 def validate_upload_checksum!(input) algorithm, checksum = request.headers["Upload-Checksum"].split(" ") error!(400, "Invalid Upload-Checksum header") if algorithm.nil? || checksum.nil? error!(400, "Invalid Upload-Checksum header") unless SUPPORTED_CHECKSUM_ALGORITHMS.include?(algorithm) generated_checksum = Tus::Checksum.generate(algorithm, input) error!(460, "Checksum from Upload-Checksum header doesn't match generated") if generated_checksum != checksum end |
#validate_upload_concat! ⇒ Object
234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/tus/server.rb', line 234 def validate_upload_concat! upload_concat = request.headers["Upload-Concat"] error!(400, "Invalid Upload-Concat header") if upload_concat !~ /^(partial|final)/ if upload_concat.start_with?("final") string = upload_concat.split(";").last string.split(" ").each do |url| error!(400, "Invalid Upload-Concat header") if url !~ %r{^#{request.script_name}/\w+$} end end end |
#validate_upload_finished!(length, current_offset) ⇒ Object
216 217 218 |
# File 'lib/tus/server.rb', line 216 def validate_upload_finished!(length, current_offset) error!(403, "Cannot download unfinished upload") unless length && current_offset && length == current_offset end |
#validate_upload_length! ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/tus/server.rb', line 183 def validate_upload_length! upload_length = request.headers["Upload-Length"] error!(400, "Missing Upload-Length header") if upload_length.to_s == "" error!(400, "Invalid Upload-Length header") if upload_length =~ /\D/ error!(400, "Invalid Upload-Length header") if upload_length.to_i < 0 if max_size && upload_length.to_i > max_size error!(413, "Upload-Length header too large") end end |
#validate_upload_metadata! ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/tus/server.rb', line 220 def = request.headers["Upload-Metadata"] .split(",").each do |string| key, value = string.split(" ") error!(400, "Invalid Upload-Metadata header") if key.nil? || value.nil? error!(400, "Invalid Upload-Metadata header") if key.ord > 127 error!(400, "Invalid Upload-Metadata header") if key =~ /,| / error!(400, "Invalid Upload-Metadata header") if value =~ /[^a-zA-Z0-9+\/=]/ end end |
#validate_upload_offset!(current_offset) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/tus/server.rb', line 195 def validate_upload_offset!(current_offset) upload_offset = request.headers["Upload-Offset"] error!(400, "Missing Upload-Offset header") if upload_offset.to_s == "" error!(400, "Invalid Upload-Offset header") if upload_offset =~ /\D/ error!(400, "Invalid Upload-Offset header") if upload_offset.to_i < 0 if upload_offset.to_i != current_offset error!(409, "Upload-Offset header doesn't match current offset") end end |