Class: Tus::Server

Inherits:
Roda
  • Object
show all
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

Instance Method Details

#created!(location) ⇒ Object



285
286
287
288
289
# File 'lib/tus/server.rb', line 285

def created!(location)
  response.status = 201
  response.headers["Location"] = location
  request.halt
end

#error!(status, message) ⇒ Object



295
296
297
298
299
# File 'lib/tus/server.rb', line 295

def error!(status, message)
  response.status = status
  response.write(message) unless request.head?
  request.halt
end

#expiration_intervalObject



313
314
315
# File 'lib/tus/server.rb', line 313

def expiration_interval
  opts[:expiration_interval]
end

#expiration_timeObject



309
310
311
# File 'lib/tus/server.rb', line 309

def expiration_time
  opts[:expiration_time]
end

#expire_filesObject



178
179
180
181
# File 'lib/tus/server.rb', line 178

def expire_files
  expirator = Expirator.new(storage, interval: expiration_interval)
  expirator.expire_files!
end

#handle_cors!Object



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/tus/server.rb', line 264

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"
    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

#max_sizeObject



305
306
307
# File 'lib/tus/server.rb', line 305

def max_size
  opts[:max_size]
end

#no_content!Object



280
281
282
283
# File 'lib/tus/server.rb', line 280

def no_content!
  response.status = 204
  request.halt
end

#not_found!(message = "Upload not found") ⇒ Object



291
292
293
# File 'lib/tus/server.rb', line 291

def not_found!(message = "Upload not found")
  error!(404, message)
end

#storageObject



301
302
303
# File 'lib/tus/server.rb', line 301

def storage
  opts[:storage] || Tus::Storage::Filesystem.new("data")
end

#validate_content_length!(content, remaining_length) ⇒ Object



221
222
223
224
# File 'lib/tus/server.rb', line 221

def validate_content_length!(content, remaining_length)
  error!(403, "Cannot modify completed upload") if remaining_length == 0
  error!(413, "Size of this chunk surpasses Upload-Length") if content.length > remaining_length
end

#validate_content_type!Object



183
184
185
186
# File 'lib/tus/server.rb', line 183

def validate_content_type!
  content_type = request.headers["Content-Type"]
  error!(415, "Invalid Content-Type header") if content_type != RESUMABLE_CONTENT_TYPE
end

#validate_tus_resumable!Object



188
189
190
191
192
193
194
195
# File 'lib/tus/server.rb', line 188

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!(content) ⇒ Object



253
254
255
256
257
258
259
260
261
262
# File 'lib/tus/server.rb', line 253

def validate_upload_checksum!(content)
  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)

  unless Checksum.new(algorithm).match?(checksum, content)
    error!(460, "Checksum from Upload-Checksum header doesn't match generated")
  end
end

#validate_upload_concat!Object



240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/tus/server.rb', line 240

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_length!Object



197
198
199
200
201
202
203
204
205
206
207
# File 'lib/tus/server.rb', line 197

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



226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/tus/server.rb', line 226

def validate_upload_metadata!
   = 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



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/tus/server.rb', line 209

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