IPC::JSON structure

dev
Karchnu 2020-10-20 19:15:50 +02:00
parent 2f649b6b15
commit 86495365d6
10 changed files with 89 additions and 169 deletions

View File

@ -10,7 +10,7 @@
# Quotas are not defined yet.
#class FileStorage::Request
# JSONIPC.request Authentication, 10 do
# IPC::JSON.message Authentication, 10 do
# property mid : String # autogenerated
# property token : Token
# property uploads : Array(UploadRequest)?
@ -70,7 +70,7 @@
#end
#
#class FileStorage::Response
# JSONIPC.request AuthenticationSuccess, 10 do
# IPC::JSON.message AuthenticationSuccess, 10 do
# property mid : String
# property responses : Array(FileStorage::Response)
# def initialize(@mid, @responses)

View File

@ -1,6 +1,6 @@
class FileStorage::Request
JSONIPC.request Download, 30 do
IPC::JSON.message Download, 30 do
property mid : String # autogenerated
property filedigest : String? # SHA256 digest of the file, used as ID
property name : String?
@ -28,7 +28,7 @@ class FileStorage::Request
end
class FileStorage::Response
JSONIPC.request Download, 30 do
IPC::JSON.message Download, 30 do
property mid : String
property nb_chunks : Int32
def initialize(@mid, @nb_chunks)

View File

@ -1,5 +1,5 @@
class FileStorage::Errors
JSONIPC.request GenericError, 200 do
IPC::JSON.message GenericError, 200 do
property mid : String
property reason : String
def initialize(@mid, @reason)
@ -7,7 +7,7 @@ class FileStorage::Errors
end
FileStorage.errors << GenericError
JSONIPC.request Authorization, 201 do
IPC::JSON.message Authorization, 201 do
property mid : String
property reason : String
def initialize(@mid, @reason = "authorization")
@ -16,7 +16,7 @@ class FileStorage::Errors
FileStorage.errors << Authorization
# When uploading a chunk already present in the DB.
JSONIPC.request ChunkAlreadyUploaded, 202 do
IPC::JSON.message ChunkAlreadyUploaded, 202 do
property mid : String
property reason = "Chunk already present"
property filedigest : String
@ -28,7 +28,7 @@ class FileStorage::Errors
FileStorage.errors << ChunkAlreadyUploaded
# You upload a chunk, but you are not the owner of the file.
JSONIPC.request ChunkUploadDenied, 203 do
IPC::JSON.message ChunkUploadDenied, 203 do
property mid : String
property reason = "This file is not yours"
property filedigest : String
@ -39,7 +39,7 @@ class FileStorage::Errors
FileStorage.errors << ChunkUploadDenied
# When uploading a file already present in the DB.
JSONIPC.request FileExists, 204 do
IPC::JSON.message FileExists, 204 do
property mid : String
property reason = "file already present"
property path : String
@ -51,7 +51,7 @@ class FileStorage::Errors
FileStorage.errors << FileExists
# When transfering a chunk for an inexistent file.
JSONIPC.request FileDoesNotExist, 205 do
IPC::JSON.message FileDoesNotExist, 205 do
property mid : String
property reason = "file does not exist"
property filedigest : String
@ -62,7 +62,7 @@ class FileStorage::Errors
FileStorage.errors << FileDoesNotExist
# When a file was already fully uploaded.
JSONIPC.request FileFullyUploaded, 206 do
IPC::JSON.message FileFullyUploaded, 206 do
property mid : String
property reason = "file already uploaded fully"
property path : String

View File

@ -4,7 +4,7 @@ require "json"
require "base64"
class FileStorage::Request
JSONIPC.request Login, 0 do
IPC::JSON.message Login, 0 do
property mid : String = ""
property token : String
@ -34,7 +34,7 @@ class FileStorage::Request
end
class FileStorage::Response
JSONIPC.request Login, 5 do
IPC::JSON.message Login, 5 do
property mid : String
def initialize(@mid)
end

View File

@ -1,5 +1,5 @@
class FileStorage::Request
JSONIPC.request PutChunk, 40 do
IPC::JSON.message PutChunk, 40 do
property mid : String # autogenerated
property filedigest : String # SHA256 digest of the entire file
# Chunk:
@ -36,7 +36,7 @@ class FileStorage::Request
end
FileStorage.requests << PutChunk
JSONIPC.request GetChunk, 41 do
IPC::JSON.message GetChunk, 41 do
property mid : String # autogenerated
property filedigest : String # SHA256 digest of the entire file
property n : Int32 # chunk number
@ -64,7 +64,7 @@ class FileStorage::Request
end
class FileStorage::Response
JSONIPC.request PutChunk, 40 do
IPC::JSON.message PutChunk, 40 do
property mid : String
property file_digest : String
property n : Int32 # chunk number
@ -72,7 +72,7 @@ class FileStorage::Response
end
end
JSONIPC.request GetChunk, 41 do
IPC::JSON.message GetChunk, 41 do
property mid : String
property file_digest : String
# Chunk:

View File

@ -1,6 +1,6 @@
class FileStorage::Request
JSONIPC.request Upload, 20 do
IPC::JSON.message Upload, 20 do
property mid : String # autogenerated
property file : FileInfo
def initialize(@file : FileInfo)
@ -26,7 +26,7 @@ class FileStorage::Request
end
class FileStorage::Response
JSONIPC.request Upload, 20 do
IPC::JSON.message Upload, 20 do
property mid : String
property path : String
def initialize(@mid, @path)
@ -34,7 +34,7 @@ class FileStorage::Response
end
FileStorage.responses << Upload
# JSONIPC.request Responses, 100 do
# IPC::JSON.message Responses, 100 do
# property mid : String
# property responses : Array(Response | Errors) # a response for each request
# property response : String

View File

@ -29,6 +29,23 @@ require "./network.cr"
require "dodb"
class IPC::JSON
def handle(filestoraged : FileStorage::Service, event : IPC::Event::Events)
raise "unknown request"
end
end
module FileStorage
class Exception < ::Exception
end
class AuthorizationException < ::Exception
end
class NotLoggedException < ::Exception
end
class AdminAuthorizationException < ::Exception
end
end
class FileStorage::Service < IPC::Server
# List of connected users (fd => uid).
@ -85,11 +102,49 @@ class FileStorage::Service < IPC::Server
@storage.user_data_per_user.update_or_create user_data.uid.to_s, user_data
end
# TODO: could be useful to send notifications.
#def send_notifications(fd : Int32, value : Int32)
# @all_connections.select(&.!=(fd)).each do |fd| ... end
# IPC::Connection.new(fd).send Response::Something.new ...
#end
def handle_request(event : IPC::Event::MessageReceived)
request = FileStorage.requests.parse_ipc_json event.message
if request.nil?
raise "unknown request type"
end
request_name = request.class.name.sub /^FileStorage::Request::/, ""
Baguette::Log.info "<< #{request_name}"
response = FileStorage::Errors::GenericError.new "#{request.id}", "generic error"
request_id = "#{request.id}"
begin
response = request.handle self, event
rescue e : AuthorizationException
Baguette::Log.error "#{request_name} authorization error"
response = FileStorage::Errors::GenericError.new request_id, "authorization error"
rescue e : AdminAuthorizationException
Baguette::Log.error "#{request_name} no admin authorization"
response = FileStorage::Errors::GenericError.new request_id, "admin authorization error"
rescue e : NotLoggedException
Baguette::Log.error "#{request_name} user not logged"
response = FileStorage::Errors::GenericError.new request_id, "user not logged"
# Do not handle generic exception case: do not provide a response.
# rescue e # Generic case
# Baguette::Log.error "#{request_name} generic error #{e}"
end
# If clients sent requests with an “id” field, it is copied
# in the responses. Allows identifying responses easily.
response.id = request.id
send event.fd, response
response_name = response.class.name.sub /^FileStorage::(Response|Errors)::/, ""
if response.responds_to?(:reason)
Baguette::Log.warning ">> #{response_name} (#{response.reason})"
else
Baguette::Log.info ">> #{response_name}"
end
end
def run
Baguette::Log.title "Starting filestoraged"
@ -128,102 +183,10 @@ class FileStorage::Service < IPC::Server
Baguette::Log.debug "IPC::Event::Message: #{event.fd}"
request_start = Time.utc
request = parse_message FileStorage.requests, event.message
if request.nil?
raise "unknown request type"
end
Baguette::Log.info "<< #{request.class.name.sub /^FileStorage::Request::/, ""}"
response = request.handle self, event
response_type = response.class.name
if response.responds_to?(:reason)
Baguette::Log.warning ">> #{response_type.sub /^FileStorage::Errors::/, ""} (#{response.reason})"
else
Baguette::Log.info ">> #{response_type.sub /^FileStorage::Response::/, ""}"
end
#################################################################
# THERE START
#################################################################
# # 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).
# # TODO: for now, the token is replaced by a hardcoded one, for debugging
#
# mtype = FileStorage::MessageType.new event.message.utype.to_i32
#
# # First, the user has to be authenticated unless we are receiving its first message.
# userid = Context.connected_users[event.fd]?
#
# # If the user is not yet connected but does not try to perform authentication.
# if ! userid && mtype != FileStorage::MessageType::Authentication
# # TODO: replace this with an Error message.
# mid = "no message id"
# response = FileStorage::Response.new mid, "Not OK", "Action on non connected user"
# do_response event, response
# end
#
# case mtype
# when .authentication?
# Baguette::Log.debug "Receiving an authentication message"
# # Test if the client is already authenticated.
# if userid
# user = Context.users_status[userid]
# raise "Authentication message while the user was already connected: this should not happen"
# else
# Baguette::Log.debug "User is not currently connected"
# hdl_authentication event
# end
#
# when .upload_request?
# Baguette::Log.debug "Upload request"
# request = FileStorage::UploadRequest.from_json(
# String.new event.message.payload
# )
# response = hdl_upload request, Context.users_status[userid]
# do_response event, response
#
# when .download_request?
# Baguette::Log.debug "Download request"
# request = FileStorage::DownloadRequest.from_json(
# String.new event.message.payload
# )
# response = hdl_download request, Context.users_status[userid]
# do_response event, response
#
# when .transfer?
# # throw an error if the user isn't recorded
# unless user = Context.users_status[userid]?
# raise "The user isn't recorded in the users_status structure"
# end
#
# transfer = FileStorage::PutChunk.from_json(
# String.new event.message.payload
# )
# response = hdl_transfer transfer, Context.users_status[userid]
#
# do_response event, response
# end
#################################################################
# FINISH
#################################################################
# If clients sent requests with an “id” field, it is copied
# in the responses. Allows identifying responses easily.
response.id = request.id
send event.fd, response
handle_request event
duration = Time.utc - request_start
Baguette::Log.debug "request took: #{duration}"
when IPC::Event::MessageSent
Baguette::Log.debug "IPC::Event::MessageSent: #{event.fd}"
else

View File

@ -1,42 +1,16 @@
require "ipc"
require "json"
class JSONIPC
include JSON::Serializable
getter type = -1
class_getter type = -1
property id : JSON::Any?
def handle(service : IPC::Server, event : IPC::Event::Events)
raise "unimplemented"
end
macro request(id, type, &block)
class {{id}} < ::JSONIPC
include JSON::Serializable
@@type = {{type}}
def type
@@type
end
{{yield}}
end
end
end
class IPC::Context
def send(fd : Int32, request : JSONIPC)
def send(fd : Int32, request : IPC::JSON)
send fd, request.type.to_u8, request.to_json
end
end
class FileStorage
class_getter requests = [] of JSONIPC.class
class_getter responses = [] of JSONIPC.class
class_getter errors = [] of JSONIPC.class
module FileStorage
class_getter requests = [] of IPC::JSON.class
class_getter responses = [] of IPC::JSON.class
class_getter errors = [] of IPC::JSON.class
end
class FileStorage::Client < IPC::Client
@ -45,19 +19,6 @@ class FileStorage::Client < IPC::Client
end
end
def parse_message(requests : Array(JSONIPC.class), message : IPC::Message) : JSONIPC?
request_type = requests.find &.type.==(message.utype)
payload = String.new message.payload
if request_type.nil?
raise "invalid request type (#{message.utype})"
end
request_type.from_json payload
end
require "../common/requests/client.cr"
require "../common/requests/login.cr"
require "../common/requests/transfer.cr"

View File

@ -10,7 +10,7 @@
# })
#end
class FileStorage
module FileStorage
# 1 MB read buffer, on-disk
def self.file_reading_buffer_size
@ -45,10 +45,6 @@ class FileStorage
end
end
class FileStorage::Exception < ::Exception
end
class FileStorage::Chunk
include JSON::Serializable

View File

@ -1,4 +1,4 @@
class FileStorage
module FileStorage
def self.message_buffer_size
def self.file_reading_buffer_size
def self.data_digest(data : Bytes)