From d6aa9f35fb53f410d44e3b88c27ce167e85054a2 Mon Sep 17 00:00:00 2001
From: Philippe PITTOLI
Date: Fri, 20 Dec 2019 17:20:08 +0100
Subject: [PATCH] record users, handling user status, new message format
---
src/client.cr | 36 ++++++++++++++------
src/common.cr | 55 +++++++++++++++++++++++++++---
src/json_tests.cr | 5 ++-
src/main.cr | 87 +++++++++++++++++++++++++++++++++++------------
4 files changed, 145 insertions(+), 38 deletions(-)
diff --git a/src/client.cr b/src/client.cr
index b9e3321..7e0052d 100644
--- a/src/client.cr
+++ b/src/client.cr
@@ -4,13 +4,22 @@ require "json"
require "./common.cr"
+# TODO
+# For now, this example only upload files.
+# In a near future, we should be able to download files, too.
+
service_name = "filestorage"
files_and_directories_to_transfer = Array(String).new
+# This is the requests we will send to the server
+requets = Array(Requets).new
+
OptionParser.parse do |parser|
- parser.on "-s service-name", "--service-name service-name", "Service name." do |name|
+ parser.on "-s service-name",
+ "--service-name service-name",
+ "Service name." do |name|
service_name = name
end
@@ -24,19 +33,16 @@ OptionParser.parse do |parser|
end
end
-client = IPC::Client.new service_name
-
#
# Get informations about files to transfer
+# For now, we only want to upload files, so we create an UploadRequest
#
files_info = Array(FileInfo).new
puts "files and directories to transfer"
files_and_directories_to_transfer.each do |f|
- puts "- #{f}"
-
if File.directory? f
# TODO
puts "Directories not supported, for now"
@@ -45,11 +51,11 @@ files_and_directories_to_transfer.each do |f|
files_info << FileInfo.new file
end
else
- if File.exists? f
+ if ! File.exists? f
puts "#{f} does not exist"
- elsif File.file? f
+ elsif ! File.file? f
puts "#{f} is neither a directory or a file"
- elsif File.readable? f
+ elsif ! File.readable? f
puts "#{f} is not readable"
end
end
@@ -57,10 +63,16 @@ end
pp! files_info
-exit 0
+requests << UploadRequest.new files_info
#
-# Create the authentication message, including files info
+# Connection to the service
+#
+
+client = IPC::Client.new service_name
+
+#
+# Sending the authentication message, including files info
#
token = Token.new 1002, "karchnu"
@@ -69,6 +81,10 @@ authentication_message = AuthenticationMessage.new token, files_info
client.send(1.to_u8, authentication_message.to_json)
+#
+# Receiving a response
+#
+
m = client.read
# puts "message received: #{m.to_s}"
# puts "message received payload: #{String.new m.payload}"
diff --git a/src/common.cr b/src/common.cr
index 92a76b7..2e810c9 100644
--- a/src/common.cr
+++ b/src/common.cr
@@ -1,5 +1,16 @@
require "uuid"
+enum MessageType
+ Error
+ AuthenticationMessage
+ Response
+ Transfer
+end
+
+# For now, upload and download are sequentials.
+# In a future version, we will be able to send
+# arbitrary parts of each file.
+
class Token
JSON.mapping({
uid: Int32,
@@ -10,28 +21,62 @@ class Token
end
end
+# Who knows, maybe someday we will be on UDP, too.
+#class SHA256
+# JSON.mapping({
+# chunk: Slice(UInt8)
+# })
+#end
+
+
+# A file has a name, a size and tags.
class FileInfo
JSON.mapping({
name: String,
- size: UInt32,
+ size: UInt64,
+ # list of SHA256, if we are on UDP
+ # chunks: Array(SHA256),
tags: Array(String)?
})
+ # debugging constructor
def initialize(@name, @size, @tags = nil)
+ # If on UDP
+ # @chunks = Array(SHA256).new
+ # arbitrary values here
end
def initialize(file : File, @tags = nil)
- @name = file.basename
+ @name = File.basename file.path
@size = file.size
end
end
+class Request
+end
+
+class UploadRequest < Request
+ property files_to_upload : Array(FileInfo)
+
+ def initialize(@files_to_upload)
+ end
+end
+
+
+# WIP
+class DownloadRequest < Request
+ property names : Array(String)?,
+ property tags : Array(String)?
+
+ def initialize(@names = nil, @tags = nil)
+ end
+end
+
class AuthenticationMessage
JSON.mapping({
mid: String,
token: Token,
- files: Array(FileInfo),
- tags: Array(String)?
+ requests: Array(Requests)
})
def initialize(@token, @files, @tags = nil)
@@ -50,7 +95,7 @@ class Response
end
end
-class Transfer
+class TransferMessage
JSON.mapping({
mid: String,
chunk: String,
diff --git a/src/json_tests.cr b/src/json_tests.cr
index 0056f15..9060746 100644
--- a/src/json_tests.cr
+++ b/src/json_tests.cr
@@ -2,13 +2,16 @@ require "json"
require "./common.cr"
+files_info = Array(FileInfo).new
+files_info << FileInfo.new "file.txt", 4123.to_u64, %w(important truc machin)
token = Token.new 1002, "karchnu"
-authentication_message = AuthenticationMessage.new token
+authentication_message = AuthenticationMessage.new token, files_info
# TODO, TEST, DEBUG, XXX, FIXME
pp! authentication_message.to_json
+
am_from_json = AuthenticationMessage.from_json authentication_message.to_json
pp! am_from_json
diff --git a/src/main.cr b/src/main.cr
index 12c4f99..d6f24a6 100644
--- a/src/main.cr
+++ b/src/main.cr
@@ -30,33 +30,50 @@ OptionParser.parse do |parser|
end
-# keep track of connected users
+# keep track of connected users and their requests
+# TODO: requests should be handled concurrently
class User
+ property uid : Int32
property token : Token
+ property requests : Array(Request)
+
def initialize(@token)
+ @uid = token.uid
end
end
# list of connected users
-# fd => User
-connected_users = Hash(Int32, User).new
-
+# fd => uid
+connected_users = Hash(Int32, Int32).new
+users_status = Hash(Int32, User).new
+
service = IPC::SwitchingService.new service_name
+def receiving_files(user : User, event : IPC::Event::Message)
+end
+
+# Could be the reception of a file or a file request
+def request_handling(user : User, event : IPC::Event::Message)
+ puts "request handling"
+
+ #
+ # Here we get requests from the message received
+ #
+
+end
+
service.loop do |event|
case event
when IPC::Event::Timer
puts "#{CORANGE}IPC::Event::Timer#{CRESET}"
- # puts "Disconnected client is: #{client_name}"
-
when IPC::Event::Connection
puts "#{CBLUE}IPC::Event::Connection: #{event.connection.fd}#{CRESET}"
when IPC::Event::Disconnection
puts "#{CBLUE}IPC::Event::Disconnection: #{event.connection.fd}#{CRESET}"
- connected_users.select! do |fd, user|
+ connected_users.select! do |fd, uid|
fd != event.connection.fd
end
@@ -72,31 +89,57 @@ service.loop do |event|
puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}"
# 1. test if the client is already authenticated
- if user = connected_users[event.connection.fd]?
+ if userid = connected_users[event.connection.fd]?
puts "User is connected: #{user.token.login}"
+ request_handling users_status[userid], event
else
puts "User is not currently connected"
- authentication_message = AuthenticationMessage.from_json(String.new event.message.payload)
+ # The first message sent to the server has to be the AuthenticationMessage.
+ # Users sent their token (JWT) to authenticate themselves.
+ # The token contains the user id, its login and a few other parameters.
+ # (see the authd documentation).
+ authentication_message =
+ AuthenticationMessage.from_json(
+ String.new event.message.payload
+ )
- authentication_message.files.each do |file|
- puts "uploading #{file.name} - #{file.size} bytes"
+ # Is the user already recorded in users_status?
+ if users_status[authentication_message.token.uid]?
+ puts "We already knew the user #{authentication_message.token.uid}"
+ pp! users_status[authentication_message.token.uid]
+ else
+ # AuthenticationMessage includes requests.
+ new_user =
+ User.new authentication_message.token,
+ authentication_message.requests
+
+ connected_users[event.connection.fd] = new_user.uid
+
+ # record the new user in users_status
+ users_status[new_user.uid] = new_user
+
+ puts "New user is: #{new_user.token.login}"
end
- new_user = User.new authentication_message.token
- connected_users[event.connection.fd] = new_user
- puts "New user is: #{new_user.token.login}"
+ # The user is now connected.
+ user = users_status[authentication_message.token.uid]
+ # We verify the user's rights to upload files.
+ # TODO RIGHTS
+ # if user wants to upload but not allowed to: Response
+ # if user wants to get a file but not allowed to: Response
+
+ # The user is authorized to upload files.
+
+ # TODO: quotas
+ # Quotas are not defined yet.
+
+ # Sending a response.
+ # The response is "Ok" when the message is well received and authorized.
response = Response.new authentication_message.mid, "Ok"
- event.connection.send 2.to_u8, response.to_json
+ event.connection.send MessageType::Response.to_u8, response.to_json
end
-
-
- # puts "New connected client is: #{client_name}"
-
- # The first message is the connection.
- # Users sent their token (JWT) to authenticate.
- # From the token, we get the user id, its login and a few other parameters (see the authd documentation).
else
raise "Event type not supported."
end