class Tus::Server

Constants

HOOKS
RESUMABLE_CONTENT_TYPE
SUPPORTED_CHECKSUM_ALGORITHMS
SUPPORTED_EXTENSIONS
SUPPORTED_VERSIONS

Public Instance Methods

created!(location) click to toggle source
# File lib/tus/server.rb, line 411
def created!(location)
  response.status = 201
  response.headers["Location"] = location
  request.halt
end
error!(status, message) click to toggle source
# File lib/tus/server.rb, line 417
def error!(status, message)
  response.status = status
  response.write(message) unless request.head?
  response.headers["Content-Type"] = "text/plain"
  request.halt
end
expiration_interval() click to toggle source
# File lib/tus/server.rb, line 436
def expiration_interval
  opts[:expiration_interval]
end
expiration_time() click to toggle source
# File lib/tus/server.rb, line 432
def expiration_time
  opts[:expiration_time]
end
get_input(info) click to toggle source

Wraps the Rack input (request body) into a Tus::Input object, applying a size limit if one exists.

# 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!() click to toggle source
# 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.options?
    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) click to toggle source

Handles partial responses requested in the “Range” header. Implementation is mostly copied from Rack::File.

# 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() click to toggle source
# File lib/tus/server.rb, line 428
def max_size
  opts[:max_size]
end
no_content!() click to toggle source
# File lib/tus/server.rb, line 406
def no_content!
  response.status = 204
  request.halt
end
redirect_download() click to toggle source
# 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() click to toggle source
# File lib/tus/server.rb, line 424
def storage
  opts[:storage] || Tus::Storage::Filesystem.new("data")
end
validate_content_length!(size, info) click to toggle source
# 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!() click to toggle source
# 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) click to toggle source

Validates that each partial upload exists and is marked as one, and at the same time calculates the sum of part lengths.

# 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!() click to toggle source
# 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) click to toggle source
# 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!() click to toggle source
# 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) click to toggle source
# 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!() click to toggle source
# 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!() click to toggle source
# File lib/tus/server.rb, line 279
def validate_upload_metadata!
  upload_metadata = request.headers["Upload-Metadata"]

  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) click to toggle source
# 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