tests, code split, better transfer handler
This commit is contained in:
parent
d02de92d24
commit
9375f5ff50
43
src/common/utils.cr
Normal file
43
src/common/utils.cr
Normal 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
|
@ -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
|
||||||
|
@ -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
|
data = Base64.decode message.data
|
||||||
dir = "#{Context.storage_directory}/#{user.uid}"
|
write_a_chunk user.uid.to_s, transfer_info.file_info, chunk_number, data
|
||||||
|
|
||||||
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
|
|
||||||
file.write 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"
|
||||||
|
|
||||||
|
@ -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
49
src/tests/dodb-tests.cr
Normal 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
|
Loading…
Reference in New Issue
Block a user