IPC::JSON structure
parent
2f649b6b15
commit
86495365d6
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue