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", "checksum", ]
- SUPPORTED_CHECKSUM_ALGORITHMS =
%w[sha1 sha256 sha384 sha512 md5 crc32]
- RESUMABLE_CONTENT_TYPE =
"application/offset+octet-stream"- HOOKS =
%i[before_create after_create after_finish after_terminate]
Instance Method Summary collapse
- #created!(location) ⇒ Object
- #error!(status, message) ⇒ Object
- #expiration_interval ⇒ Object
- #expiration_time ⇒ Object
-
#get_input(info) ⇒ Object
Wraps the Rack input (request body) into a Tus::Input object, applying a size limit if one exists.
- #handle_cors! ⇒ Object
-
#handle_range_request!(length) ⇒ Object
Handles partial responses requested in the “Range” header.
- #max_size ⇒ Object
- #no_content! ⇒ Object
- #redirect_download ⇒ Object
- #storage ⇒ Object
- #validate_content_length!(size, info) ⇒ Object
- #validate_content_type! ⇒ Object
-
#validate_partial_uploads!(part_uids) ⇒ Object
Validates that each partial upload exists and is marked as one, and at the same time calculates the sum of part lengths.
- #validate_tus_resumable! ⇒ Object
- #validate_upload_checksum!(input) ⇒ Object
- #validate_upload_concat! ⇒ Object
- #validate_upload_finished!(info) ⇒ Object
- #validate_upload_length! ⇒ Object
- #validate_upload_metadata! ⇒ Object
- #validate_upload_offset!(info) ⇒ Object
Instance Method Details
#created!(location) ⇒ Object
411 412 413 414 415 |
# File 'lib/tus/server.rb', line 411 def created!(location) response.status = 201 response.headers["Location"] = location request.halt end |
#error!(status, message) ⇒ Object
417 418 419 420 421 422 |
# File 'lib/tus/server.rb', line 417 def error!(status, ) response.status = status response.write() unless request.head? response.headers["Content-Type"] = "text/plain" request.halt end |
#expiration_interval ⇒ Object
436 437 438 |
# File 'lib/tus/server.rb', line 436 def expiration_interval opts[:expiration_interval] end |
#expiration_time ⇒ Object
432 433 434 |
# File 'lib/tus/server.rb', line 432 def expiration_time opts[:expiration_time] end |
#get_input(info) ⇒ Object
Wraps the Rack input (request body) into a Tus::Input object, applying a size limit if one exists.
221 222 223 224 225 226 227 |
# File 'lib/tus/server.rb', line 221 def get_input(info) offset = info.offset total = info.length || max_size limit = total - offset if total Tus::Input.new(request.body, limit: limit) end |
#handle_cors! ⇒ Object
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/tus/server.rb', line 390 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, Upload-Defer-Length, Upload-Concat" 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, Upload-Defer-Length, Upload-Concat" end end |
#handle_range_request!(length) ⇒ Object
Handles partial responses requested in the “Range” header. Implementation is mostly copied from Rack::File.
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/tus/server.rb', line 348 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 # we support ranged requests response.headers["Accept-Ranges"] = "bytes" 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 range response.headers["Content-Range"] = "bytes */#{length}" error!(416, "Byte range unsatisfiable") else range = ranges[0] response.status = 206 response.headers["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{length}" end response.headers["Content-Length"] = range.size.to_s range end |
#max_size ⇒ Object
428 429 430 |
# File 'lib/tus/server.rb', line 428 def max_size opts[:max_size] end |
#no_content! ⇒ Object
406 407 408 409 |
# File 'lib/tus/server.rb', line 406 def no_content! response.status = 204 request.halt end |
#redirect_download ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'lib/tus/server.rb', line 377 def redirect_download value = opts[:redirect_download] if opts[:download_url] value ||= opts[:download_url] warn "[TUS-RUBY-SERVER DEPRECATION] The :download_url option has been renamed to :redirect_download." end value = storage.method(:file_url) if value == true value end |
#storage ⇒ Object
424 425 426 |
# File 'lib/tus/server.rb', line 424 def storage opts[:storage] || Tus::Storage::Filesystem.new("data") end |
#validate_content_length!(size, info) ⇒ Object
266 267 268 269 270 271 272 273 |
# File 'lib/tus/server.rb', line 266 def validate_content_length!(size, info) if info.length error!(403, "Cannot modify completed upload") if info.offset == info.length error!(413, "Size of this chunk surpasses Upload-Length") if info.offset + size > info.length elsif max_size error!(413, "Size of this chunk surpasses Tus-Max-Size") if info.offset + size > max_size end end |
#validate_content_type! ⇒ Object
229 230 231 |
# File 'lib/tus/server.rb', line 229 def validate_content_type! error!(415, "Invalid Content-Type header") if request.content_type != RESUMABLE_CONTENT_TYPE end |
#validate_partial_uploads!(part_uids) ⇒ Object
Validates that each partial upload exists and is marked as one, and at the same time calculates the sum of part lengths.
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/tus/server.rb', line 308 def validate_partial_uploads!(part_uids) length = 0 part_uids.each do |part_uid| begin part_info = storage.read_info(part_uid) rescue Tus::NotFound error!(400, "Partial upload not found") end part_info = Tus::Info.new(part_info) error!(400, "Upload is not partial") unless part_info.partial? unless part_info.length == part_info.offset error!(400, "Partial upload is not finished") end length += part_info.length end if max_size && length > max_size error!(400, "The sum of partial upload lengths exceeds Tus-Max-Size") end length end |
#validate_tus_resumable! ⇒ Object
233 234 235 236 237 238 239 240 |
# File 'lib/tus/server.rb', line 233 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
336 337 338 339 340 341 342 343 344 |
# File 'lib/tus/server.rb', line 336 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, "Upload-Checksum value doesn't match generated checksum") if generated_checksum != checksum end |
#validate_upload_concat! ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/tus/server.rb', line 293 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 !~ /#{request.script_name}\/\w+$/ end end end |
#validate_upload_finished!(info) ⇒ Object
275 276 277 |
# File 'lib/tus/server.rb', line 275 def validate_upload_finished!(info) error!(403, "Cannot download unfinished upload") unless info.length == info.offset end |
#validate_upload_length! ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/tus/server.rb', line 242 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
279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/tus/server.rb', line 279 def = request.headers["Upload-Metadata"] .split(",").each do |string| key, value = string.split(" ", 2) error!(400, "Invalid Upload-Metadata header") if key.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!(info) ⇒ Object
254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/tus/server.rb', line 254 def validate_upload_offset!(info) 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 != info.offset error!(409, "Upload-Offset header doesn't match current offset") end end |