diff --git a/src/client/main.cr b/src/client/main.cr index 5b51023..e975128 100644 --- a/src/client/main.cr +++ b/src/client/main.cr @@ -6,8 +6,6 @@ require "base64" require "../common/filestorage.cr" -alias FM = FileStorage::Message - # TODO # For now, this example only upload files. # In a near future, we should be able to download files, too. @@ -17,7 +15,7 @@ service_name = "filestorage" files_and_directories_to_transfer = Array(String).new # This is the requests we will send to the server -upload_requests = Array(FM::UploadRequest).new +upload_requests = Array(FileStorage::UploadRequest).new OptionParser.parse do |parser| @@ -68,7 +66,7 @@ files_and_directories_to_transfer.each do |f| end files_info.values.each do |file_info| - upload_requests << FM::UploadRequest.new file_info + upload_requests << FileStorage::UploadRequest.new file_info end # pp! upload_requests @@ -84,7 +82,7 @@ client = IPC::Client.new service_name # token = FileStorage::Token.new 1002, "karchnu" -authentication_message = FM::Authentication.new token, upload_requests +authentication_message = FileStorage::Authentication.new token, upload_requests pp! authentication_message client.send FileStorage::MessageType::Authentication.to_u8, authentication_message.to_json @@ -96,7 +94,7 @@ m = client.read # puts "message received: #{m.to_s}" # puts "message received payload: #{String.new m.payload}" -response = FM::Response.from_json(String.new m.payload) +response = FileStorage::Response.from_json(String.new m.payload) if response.mid == authentication_message.mid puts "This is a response for the authentication message" @@ -118,7 +116,7 @@ def file_transfer(client : IPC::Client, file : File, file_info : FileStorage::Fi while (size = file.read(buffer)) > 0 # transfer message = file_info, chunk count, data (will be base64'd) - transfer_message = FM::Transfer.new file_info, counter, buffer[0 ... size] + transfer_message = FileStorage::Transfer.new file_info, counter, buffer[0 ... size] client.send FileStorage::MessageType::Transfer.to_u8, transfer_message.to_json counter += 1 @@ -134,7 +132,7 @@ def file_transfer(client : IPC::Client, file : File, file_info : FileStorage::Fi raise "Message received was not expected: #{mtype}" end - response = FM::Response.from_json(String.new m.payload) + response = FileStorage::Response.from_json(String.new m.payload) if response.mid != transfer_message.mid raise "Message received has a wrong mid: #{response.mid} != #{transfer_message.mid}" diff --git a/src/common/filestorage.cr b/src/common/filestorage.cr index 8f0bf84..e442a5c 100644 --- a/src/common/filestorage.cr +++ b/src/common/filestorage.cr @@ -90,108 +90,108 @@ module FileStorage end class Message + end - alias Request = UploadRequest | DownloadRequest + alias Request = UploadRequest | DownloadRequest - class UploadRequest - JSON.mapping({ - # autogenerated - mid: String, - file: FileInfo - }) + class UploadRequest < Message + JSON.mapping({ + # autogenerated + mid: String, + file: FileInfo + }) - def initialize(@file) - @mid = UUID.random.to_s - end + def initialize(@file) + @mid = UUID.random.to_s end + end - # WIP - class DownloadRequest - JSON.mapping({ - # autogenerated - mid: String, - # SHA256 digest of the file, used as ID - uuid: String?, - name: String?, - tags: Array(String)? - }) + # WIP + class DownloadRequest < Message + JSON.mapping({ + # autogenerated + mid: String, + # SHA256 digest of the file, used as ID + uuid: String?, + name: String?, + tags: Array(String)? + }) - def initialize(@uuid = nil, @name = nil, @tags = nil) - @mid = UUID.random.to_s - end + def initialize(@uuid = nil, @name = nil, @tags = nil) + @mid = UUID.random.to_s end + end - class Authentication - JSON.mapping({ - # autogenerated - mid: String, - token: Token, - uploads: Array(UploadRequest), - downloads: Array(DownloadRequest) - }) + class Authentication < Message + JSON.mapping({ + # autogenerated + mid: String, + token: Token, + uploads: Array(UploadRequest), + downloads: Array(DownloadRequest) + }) - def initialize(@token, @uploads = Array(UploadRequest).new, @downloads = Array(DownloadRequest).new) - @mid = UUID.random.to_s - end + def initialize(@token, @uploads = Array(UploadRequest).new, @downloads = Array(DownloadRequest).new) + @mid = UUID.random.to_s end + end - class Response - JSON.mapping({ - mid: String, - response: String, - reason: String? - }) + class Response < Message + JSON.mapping({ + mid: String, + response: String, + reason: String? + }) - def initialize(@mid, @response, @reason = nil) - end + def initialize(@mid, @response, @reason = nil) end + end - class Error - JSON.mapping({ - mid: String, - # a response for each request - response: String, - reason: String? - }) + class Error < Message + JSON.mapping({ + mid: String, + # a response for each request + response: String, + reason: String? + }) - def initialize(@mid, @response, @reason = nil) - end + def initialize(@mid, @response, @reason = nil) end + end - class Responses - JSON.mapping({ - mid: String, - # a response for each request - responses: Array(Response), - response: String, - reason: String? - }) + class Responses < Message + JSON.mapping({ + mid: String, + # a response for each request + responses: Array(Response), + response: String, + reason: String? + }) - def initialize(@mid, @response, @responses, @reason = nil) - end + def initialize(@mid, @response, @responses, @reason = nil) end + end - class Transfer - JSON.mapping({ - # autogenerated - mid: String, - # SHA256 digest of the entire file - filedigest: String, - # For now, just the counter in a string - chunk: Chunk, - # base64 slice - data: String, - }) + class Transfer < Message + JSON.mapping({ + # autogenerated + mid: String, + # SHA256 digest of the entire file + filedigest: String, + # For now, just the counter in a string + chunk: Chunk, + # base64 slice + data: String, + }) - def initialize(file_info : FileInfo, count, bindata) - # count: chunk number + def initialize(file_info : FileInfo, count, bindata) + # count: chunk number - @filedigest = file_info.digest - @data = Base64.encode bindata - @chunk = FileStorage::Chunk.new count, file_info.nb_chunks, @data - @mid = UUID.random.to_s - end + @filedigest = file_info.digest + @data = Base64.encode bindata + @chunk = FileStorage::Chunk.new count, file_info.nb_chunks, @data + @mid = UUID.random.to_s end end diff --git a/src/server/context.cr b/src/server/context.cr index 111a060..50c6b28 100644 --- a/src/server/context.cr +++ b/src/server/context.cr @@ -4,9 +4,12 @@ class User property uid : Int32 property token : FileStorage::Token - property requests : Array(FileStorage::Message::Request)? + property uploads : Array(FileStorage::UploadRequest) + property downloads : Array(FileStorage::DownloadRequest) - def initialize(@token, @requests = nil) + def initialize(@token, + @uploads = Array(FileStorage::UploadRequest).new, + @downloads = Array(FileStorage::DownloadRequest).new) @uid = token.uid end end diff --git a/src/server/handlers.cr b/src/server/handlers.cr index 41dbe01..62cb455 100644 --- a/src/server/handlers.cr +++ b/src/server/handlers.cr @@ -3,63 +3,76 @@ require "dodb" require "base64" # reception of a file chunk -def hdl_transfer(message : FileStorage::Message::Transfer, +def hdl_transfer(message : FileStorage::Transfer, user : User, - event : IPC::Event::Message) : FileStorage::Message::Response + event : IPC::Event::Message) : FileStorage::Response puts "receiving a file" - transfer_message = FileStorage::Message::Transfer.from_json( - String.new event.message.payload - ) + mid = message.mid + mid ||= "no message id" - pp! transfer_message + # pp! transfer_message + + file_info = user.uploads.select do |v| + v.file.digest == message.filedigest + end.first.file + + pp! file_info + + # TODO: verify the digest + # TODO: store the file + # TODO: register the file, with its tags # puts "chunk: #{transfer_message.chunk}" # puts "data: #{Base64.decode transfer_message.data}" - FileStorage::Message::Response.new message.mid, "Ok" + FileStorage::Response.new mid, "Ok" + +rescue e + puts "Error handling transfer: #{e.message}" + FileStorage::Response.new mid.not_nil!, "Not Ok", "Unexpected error: #{e.message}" end # TODO # the client sent an upload request -def hdl_upload(request : FileStorage::Message::UploadRequest, +def hdl_upload(request : FileStorage::UploadRequest, user : User, - event : IPC::Event::Message) : FileStorage::Message::Response + event : IPC::Event::Message) : FileStorage::Response puts "hdl upload: mid=#{request.mid}" pp! request - FileStorage::Message::Response.new request.mid, "Upload OK" + FileStorage::Response.new request.mid, "Upload OK" end # TODO # the client sent a download request -def hdl_download(request : FileStorage::Message::DownloadRequest, +def hdl_download(request : FileStorage::DownloadRequest, user : User, - event : IPC::Event::Message) : FileStorage::Message::Response + event : IPC::Event::Message) : FileStorage::Response puts "hdl download: mid=#{request.mid}" pp! request - FileStorage::Message::Response.new request.mid, "Download OK" + FileStorage::Response.new request.mid, "Download OK" end # Entry point for request management # Each request should have a response. # Then, responses are sent in a single message. -def hdl_requests(requests : Array(FileStorage::Message::Request), +def hdl_requests(requests : Array(FileStorage::Request), user : User, - event : IPC::Event::Message) : Array(FileStorage::Message::Response) + event : IPC::Event::Message) : Array(FileStorage::Response) puts "hdl request" - responses = Array(FileStorage::Message::Response).new + responses = Array(FileStorage::Response).new requests.each do |request| case request - when FileStorage::Message::DownloadRequest + when FileStorage::DownloadRequest responses << hdl_download request, user, event - when FileStorage::Message::UploadRequest + when FileStorage::UploadRequest responses << hdl_upload request, user, event else raise "request not understood" @@ -78,7 +91,7 @@ end def hdl_authentication(event : IPC::Event::Message) authentication_message = - FileStorage::Message::Authentication.from_json( + FileStorage::Authentication.from_json( String.new event.message.payload ) @@ -98,7 +111,8 @@ def hdl_authentication(event : IPC::Event::Message) # AuthenticationMessage includes requests. new_user = User.new authentication_message.token, - [ authentication_message.uploads, authentication_message.downloads ].flatten + authentication_message.uploads, + authentication_message.downloads Context.connected_users[event.connection.fd] = userid @@ -127,7 +141,7 @@ def hdl_authentication(event : IPC::Event::Message) # Sending a response, containing a response for each request. # The response is "Ok" when the message is well received and authorized. - response = FileStorage::Message::Responses.new authentication_message.mid, "Ok", responses + response = FileStorage::Responses.new authentication_message.mid, "Ok", responses event.connection.send FileStorage::MessageType::Responses.to_u8, response.to_json pp! FileStorage::MessageType::Responses.to_u8 pp! response diff --git a/src/server/main-loop.cr b/src/server/main-loop.cr index 4b7aedd..645a5ff 100644 --- a/src/server/main-loop.cr +++ b/src/server/main-loop.cr @@ -42,8 +42,8 @@ Context.service.not_nil!.loop do |event| if ! userid && mtype != FileStorage::MessageType::Authentication # TODO: replace this with an Error message? mid = "no message id" - response = FileStorage::Message::Response.new mid, "Not OK", "Action on non connected user" - event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json + response = FileStorage::Response.new mid, "Not OK", "Action on non connected user" + do_response event, response end case mtype @@ -59,22 +59,20 @@ Context.service.not_nil!.loop do |event| end when .upload_request? puts "Upload request" - request = FileStorage::Message::UploadRequest.from_json( + request = FileStorage::UploadRequest.from_json( String.new event.message.payload ) response = hdl_upload request, Context.users_status[userid], event - event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json - raise "not implemented yet" + do_response event, response when .download_request? puts "Download request" - request = FileStorage::Message::DownloadRequest.from_json( + request = FileStorage::DownloadRequest.from_json( String.new event.message.payload ) response = hdl_download request, Context.users_status[userid], event - event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json - raise "not implemented yet" + do_response event, response when .response? puts "Response message" raise "not implemented yet" @@ -90,12 +88,12 @@ Context.service.not_nil!.loop do |event| raise "The user isn't recorded in the users_status structure" end - transfer = FileStorage::Message::Transfer.from_json( + transfer = FileStorage::Transfer.from_json( String.new event.message.payload ) response = hdl_transfer transfer, Context.users_status[userid], event - event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json + do_response event, response end else raise "Event type not supported." @@ -103,3 +101,19 @@ Context.service.not_nil!.loop do |event| rescue e puts "A problem occured : #{e.message}" end + +def do_response(event : IPC::Event::Message, + response : FileStorage::Message) + + case response + when FileStorage::Response + event.connection.send FileStorage::MessageType::Response.to_u8, response.to_json + when FileStorage::Responses + event.connection.send FileStorage::MessageType::Responses.to_u8, response.to_json + when FileStorage::Error + event.connection.send FileStorage::MessageType::Error.to_u8, response.to_json + else + puts "response should not happen: #{response}" + pp! response + end +end