tests, code split, better transfer handler

This commit is contained in:
Philippe PITTOLI 2020-01-30 01:27:06 +01:00
parent d02de92d24
commit 9375f5ff50
5 changed files with 145 additions and 46 deletions

43
src/common/utils.cr Normal file
View File

@ -0,0 +1,43 @@
def remove_chunk_from_db(transfer_info : TransferInfo, chunk_number : Int32)
transfer_info.chunks.delete chunk_number
Context.db_by_filedigest.update transfer_info.file_info.digest, transfer_info
end
def write_a_chunk(userid : String, file_info : FileInfo, chunk_number : Int32, data : Bytes)
# storage: Context.storage_directory/userid/fileuuid.bin
dir = "#{Context.storage_directory}/#{userid}"
FileUtils.mkdir_p dir
path = "#{dir}/#{file_info.digest}.bin"
# Create file if non existant
File.open(path, "a+") do |file|
end
# Write in it
File.open(path, "ab") do |file|
offset = chunk_number * FileStorage.message_buffer_size
file.seek(offset, IO::Seek::Set)
file.write data
end
end
### # TODO:
### # why getting the file_info here? We could check for the transfer_info right away
### # it has more info, and we'll get it later eventually
###
### file_info = nil
### begin
### file_info = user.uploads.select do |v|
### v.file.digest == message.filedigest
### end.first.file
###
### pp! file_info
### rescue e : IndexError
### puts "No recorded upload request for file #{message.filedigest}"
###
### rescue e
### puts "Unexpected error: #{e}"
### end

View File

@ -25,7 +25,7 @@ class TransferInfo
property chunks : Array(Int32) property chunks : Array(Int32)
def initialize(@owner, @file_info) def initialize(@owner, @file_info)
@chunks = [0...@file_info.nb_chunks] @chunks = (0...@file_info.nb_chunks).to_a
end end
end end
@ -43,7 +43,6 @@ class Context
def self.db_reconnect def self.db_reconnect
# In case file_info_directory changes: database reinstanciation # In case file_info_directory changes: database reinstanciation
@@db = DODB::DataBase(TransferInfo).new @@file_info_directory @@db = DODB::DataBase(TransferInfo).new @@file_info_directory
# recreate indexes, partitions and tags objects, too # recreate indexes, partitions and tags objects, too

View File

@ -2,6 +2,13 @@
require "dodb" require "dodb"
require "base64" require "base64"
require "../common/utils"
# XXX TODO FIXME: architectural questions
# wonder why I should keep the user upload and download requests
# the server can be just for uploads, delegating downloads to HTTP
# reception of a file chunk # reception of a file chunk
def hdl_transfer(message : FileStorage::Transfer, user : User) : FileStorage::Response def hdl_transfer(message : FileStorage::Transfer, user : User) : FileStorage::Response
@ -10,56 +17,25 @@ def hdl_transfer(message : FileStorage::Transfer, user : User) : FileStorage::Re
mid = message.mid mid = message.mid
mid ||= "no message id" mid ||= "no message id"
# pp! message # Get the transfer info from the db
transfer_info = Context.db_by_filedigest.get message.filedigest
file_info = nil # TODO: if we don't have the information
begin if transfer_info.nil?
file_info = user.uploads.select do |v| # TODO
v.file.digest == message.filedigest
end.first.file
pp! file_info
rescue e : IndexError
puts "No recorded upload request for file #{message.filedigest}"
rescue e
puts "Unexpected error: #{e}"
end end
# Get the transfer info from the db # TODO: verify that the chunk sent was really missing
# is the file info recorded? chunk_number = message.chunk.n - 1
by_digest = Context.db.get_index "filedigest"
transfer_info = by_digest.get message.filedigest
if
by_owner = Context.db.get_partition "owner"
pp! by_owner
# TODO: verify the digest # TODO: verify the digest
# storage: Context.storage_directory/userid/fileuuid.bin
dir = "#{Context.storage_directory}/#{user.uid}"
FileUtils.mkdir_p dir
path = "#{dir}/#{file_info.digest}.bin"
# Create file if non existant
File.open(path, "a+") do |file|
end
# Write in it
File.open(path, "ab") do |file|
# TODO: store the file
offset = (message.chunk.n - 1) * FileStorage.message_buffer_size
file.seek(offset, IO::Seek::Set)
data = Base64.decode message.data data = Base64.decode message.data
file.write data write_a_chunk user.uid.to_s, transfer_info.file_info, chunk_number, data
end
# TODO: register the file with dodb, with its tags # TODO: register the file with dodb, with its tags
Context.db << remove_chunk_from_db transfer_info, message.chunk.n
FileStorage::Response.new mid, "Ok" FileStorage::Response.new mid, "Ok"

View File

@ -2,8 +2,12 @@ require "../common/filestorage.cr"
require "ipc" require "ipc"
require "option_parser" require "option_parser"
require "../server/context.cr"
filename = "./README.md" filename = "./README.md"
tags = "readme example"
OptionParser.parse do |parser| OptionParser.parse do |parser|
parser.on "-f file-to-transfer", parser.on "-f file-to-transfer",
"--file to-transfer", "--file to-transfer",
@ -11,11 +15,39 @@ OptionParser.parse do |parser|
filename = opt filename = opt
end end
parser.on "-d database-directory",
"--db-dir directory",
"DB directory" do |opt|
Context.file_info_directory = opt
Context.db_reconnect
end
parser.on "-t tags",
"--tags tags",
"Tags, example: 'fruit bio comestible'" do |opt|
tags = opt
end
parser.unknown_args do |args| parser.unknown_args do |args|
pp! args pp! args
end end
end end
require "../server/context.cr"
pp! Context pp! Context
fileinfo : FileStorage::FileInfo? = nil
File.open(filename) do |file|
fileinfo = FileStorage::FileInfo.new file, tags.split(' ')
end
pp! fileinfo
transfer_info = TransferInfo.new 1000, fileinfo.not_nil!
Context.db << transfer_info
Context.db.each do |ti|
pp! ti
end

49
src/tests/dodb-tests.cr Normal file
View File

@ -0,0 +1,49 @@
require "dodb"
require "json"
require "../common/filestorage.cr"
# this is a copy of User and TransferInfo classes from src/server/context.cr
class User
property uid : Int32
property token : FileStorage::Token
property uploads : Array(FileStorage::UploadRequest)
property downloads : Array(FileStorage::DownloadRequest)
def initialize(@token,
@uploads = Array(FileStorage::UploadRequest).new,
@downloads = Array(FileStorage::DownloadRequest).new)
@uid = token.uid
end
end
class TransferInfo
include JSON::Serializable
property owner : Int32
property file_info : FileStorage::FileInfo
property chunks : Hash(Int32, Bool)
def initialize(@owner, @file_info)
@chunks = Hash(Int32, Bool).new
@file_info.nb_chunks.times do |n|
@chunks[n] = false
end
end
end
file_info_directory = "./file-infos"
def init_db(file_info_directory : String)
db = DODB::DataBase(TransferInfo).new file_info_directory
# search file informations by their index, owner and tags
pp! db_by_filedigest = db.new_index "filedigest", &.file_info.digest
pp! db_by_owner = db.new_partition "owner", &.owner.to_s
pp! db_by_tags = db.new_tags "tags", &.file_info.tags.not_nil!
db
end
db = init_db file_info_directory
pp! db