added methods for Client to send and receive messages
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "tlibc/errors.h"
|
#include "tlibc/errors.h"
|
||||||
|
#include "tlibc/time.h"
|
||||||
|
#include "tlibc/magic.h"
|
||||||
|
|
||||||
/// requires tlibc and tlibtoml init
|
/// requires tlibc and tlibtoml init
|
||||||
Result(void) TcpChat_init();
|
Result(void) TcpChat_init();
|
||||||
@@ -63,6 +65,7 @@ typedef void (*LogFunction_t)(void* logger, cstr context, LogSeverity severity,
|
|||||||
#define logWarn(format, ...) log(LogSeverity_Warn, format ,##__VA_ARGS__)
|
#define logWarn(format, ...) log(LogSeverity_Warn, format ,##__VA_ARGS__)
|
||||||
#define logError(format, ...) log(LogSeverity_Error, format ,##__VA_ARGS__)
|
#define logError(format, ...) log(LogSeverity_Error, format ,##__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// Server //
|
// Server //
|
||||||
@@ -82,6 +85,7 @@ void Server_free(Server* server);
|
|||||||
|
|
||||||
Result(void) Server_run(Server* server);
|
Result(void) Server_run(Server* server);
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// Client //
|
// Client //
|
||||||
@@ -117,3 +121,15 @@ Result(void) Client_register(Client* self, i64* out_user_id);
|
|||||||
|
|
||||||
/// Authorize on connected server
|
/// Authorize on connected server
|
||||||
Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_channel_id);
|
Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_channel_id);
|
||||||
|
|
||||||
|
/// @param out_timestamp timestamp received from server
|
||||||
|
/// @return message id received from server
|
||||||
|
Result(i64) Client_sendMessage(Client* self, i64 channel_id, Array(u8) content, DateTime* out_timestamp);
|
||||||
|
|
||||||
|
/// Receive a bunch of messages from the server to a client internal buffer
|
||||||
|
/// @return number of messages received
|
||||||
|
Result(u32) Client_receiveMessageBlock(Client* self, i64 channel_id, i64 first_message_id, u32 messages_count);
|
||||||
|
|
||||||
|
/// Read message saved in client internal buffer.
|
||||||
|
/// @return number of bytes written in dst_content
|
||||||
|
Result(u32) Client_popMessage(Client* self, Array(u8) dst_content, i64* message_id, i64* sender_id, DateTime* timestamp_utc);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ void ServerConnection_close(ServerConnection* self){
|
|||||||
EncryptedSocketTCP_destroy(&self->sock);
|
EncryptedSocketTCP_destroy(&self->sock);
|
||||||
Array_u8_destroy(&self->token);
|
Array_u8_destroy(&self->token);
|
||||||
Array_u8_destroy(&self->session_key);
|
Array_u8_destroy(&self->session_key);
|
||||||
|
MessageBlock_destroy(&self->received_message_block);
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +74,8 @@ Result(ServerConnection*) ServerConnection_open(Client* client, cstr server_addr
|
|||||||
PacketType_ServerHandshake));
|
PacketType_ServerHandshake));
|
||||||
conn->session_id = server_handshake.session_id;
|
conn->session_id = server_handshake.session_id;
|
||||||
|
|
||||||
|
MessageBlock_alloc(&conn->received_message_block);
|
||||||
|
|
||||||
success = true;
|
success = true;
|
||||||
Return RESULT_VALUE(p, conn);
|
Return RESULT_VALUE(p, conn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ Result(void) Client_register(Client* self, i64* out_user_id){
|
|||||||
PacketHeader req_head, res_head;
|
PacketHeader req_head, res_head;
|
||||||
RegisterRequest req;
|
RegisterRequest req;
|
||||||
RegisterResponse res;
|
RegisterResponse res;
|
||||||
// TODO: hash token with server public key
|
|
||||||
try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
||||||
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||||
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_RegisterResponse));
|
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_RegisterResponse));
|
||||||
@@ -98,7 +97,6 @@ Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_chann
|
|||||||
PacketHeader req_head, res_head;
|
PacketHeader req_head, res_head;
|
||||||
LoginRequest req;
|
LoginRequest req;
|
||||||
LoginResponse res;
|
LoginResponse res;
|
||||||
// TODO: hash token with server public key
|
|
||||||
try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
||||||
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||||
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_LoginResponse));
|
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_LoginResponse));
|
||||||
@@ -108,3 +106,71 @@ Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_chann
|
|||||||
|
|
||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result(i64) Client_sendMessage(Client* self, i64 channel_id, Array(u8) content, DateTime* out_timestamp){
|
||||||
|
Deferral(1);
|
||||||
|
try_assert(self != NULL);
|
||||||
|
try_assert(self->conn != NULL && "didn't connect to a server yet");
|
||||||
|
try_assert(content.len >= MESSAGE_SIZE_MIN && content.len <= MESSAGE_SIZE_MAX);
|
||||||
|
|
||||||
|
PacketHeader req_head, res_head;
|
||||||
|
SendMessageRequest req;
|
||||||
|
SendMessageResponse res;
|
||||||
|
SendMessageRequest_construct(&req, &req_head, channel_id, content.len);
|
||||||
|
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||||
|
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_SendMessageResponse));
|
||||||
|
*out_timestamp = res.timestamp;
|
||||||
|
|
||||||
|
Return RESULT_VALUE(i, res.message_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(u32) Client_receiveMessageBlock(Client* self, i64 channel_id, i64 first_message_id, u32 messages_count){
|
||||||
|
Deferral(1);
|
||||||
|
try_assert(self != NULL);
|
||||||
|
try_assert(self->conn != NULL && "didn't connect to a server yet");
|
||||||
|
|
||||||
|
PacketHeader req_head, res_head;
|
||||||
|
GetMessageBlockRequest req;
|
||||||
|
GetMessageBlockResponse res;
|
||||||
|
GetMessageBlockRequest_construct(&req, &req_head, channel_id, first_message_id, messages_count);
|
||||||
|
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||||
|
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_GetMessageBlockResponse));
|
||||||
|
self->conn->received_message_block.messages_count = res.messages_count;
|
||||||
|
self->conn->received_message_block.datum.len = res.data_size;
|
||||||
|
try_void(
|
||||||
|
EncryptedSocketTCP_recv(
|
||||||
|
&self->conn->sock,
|
||||||
|
self->conn->received_message_block.datum,
|
||||||
|
SocketRecvFlag_WholeBuffer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Return RESULT_VALUE(u, res.messages_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result(u32) Client_popMessage(Client* self, Array(u8) dst_content,
|
||||||
|
i64* message_id, i64* sender_id, DateTime* timestamp_utc)
|
||||||
|
{
|
||||||
|
Deferral(1);
|
||||||
|
try_assert(self != NULL);
|
||||||
|
try_assert(self->conn != NULL && "didn't connect to a server yet");
|
||||||
|
try_assert(dst_content.len >= MESSAGE_SIZE_MAX);
|
||||||
|
|
||||||
|
MessageMeta msg_meta = {0};
|
||||||
|
try(bool read_success, u,
|
||||||
|
MessageBlock_readMessage(
|
||||||
|
&self->conn->received_message_block,
|
||||||
|
&msg_meta,
|
||||||
|
dst_content
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if(!read_success){
|
||||||
|
Return RESULT_VALUE(u, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
*message_id = msg_meta.id;
|
||||||
|
*sender_id = msg_meta.sender_id;
|
||||||
|
*timestamp_utc = msg_meta.timestamp;
|
||||||
|
|
||||||
|
Return RESULT_VALUE(u, msg_meta.data_size);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "cryptography/AES.h"
|
#include "cryptography/AES.h"
|
||||||
#include "cryptography/RSA.h"
|
#include "cryptography/RSA.h"
|
||||||
#include "network/encrypted_sockets.h"
|
#include "network/encrypted_sockets.h"
|
||||||
|
#include "network/tcp-chat-protocol/v1.h"
|
||||||
|
|
||||||
typedef struct ServerConnection ServerConnection;
|
typedef struct ServerConnection ServerConnection;
|
||||||
|
|
||||||
@@ -12,7 +13,6 @@ typedef struct Client {
|
|||||||
ServerConnection* conn;
|
ServerConnection* conn;
|
||||||
} Client;
|
} Client;
|
||||||
|
|
||||||
|
|
||||||
typedef struct ServerConnection {
|
typedef struct ServerConnection {
|
||||||
Client* client;
|
Client* client;
|
||||||
EndpointIPv4 server_end;
|
EndpointIPv4 server_end;
|
||||||
@@ -23,6 +23,7 @@ typedef struct ServerConnection {
|
|||||||
EncryptedSocketTCP sock;
|
EncryptedSocketTCP sock;
|
||||||
i64 session_id;
|
i64 session_id;
|
||||||
i64 user_id;
|
i64 user_id;
|
||||||
|
MessageBlock received_message_block;
|
||||||
} ServerConnection;
|
} ServerConnection;
|
||||||
|
|
||||||
/// @param server_addr_cstr
|
/// @param server_addr_cstr
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "RSA.h"
|
#include "RSA.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "bearssl_x509.h"
|
|
||||||
#include "bearssl_pem.h"
|
|
||||||
#include "tlibc/base64.h"
|
#include "tlibc/base64.h"
|
||||||
|
|
||||||
// https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537
|
// https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537
|
||||||
|
|||||||
@@ -142,62 +142,67 @@ void RegisterResponse_construct(RegisterResponse *ptr, PacketHeader* header,
|
|||||||
ptr->user_id = user_id;
|
ptr->user_id = user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(u32) MessageBlock_writeMessage(
|
Result(bool) MessageBlock_writeMessage(MessageBlock* block,
|
||||||
MessageMeta* msg, Array(u8) msg_content,
|
const MessageMeta* msg_meta, const Array(u8) msg_content)
|
||||||
MessageBlockMeta* block_meta, Array(u8)* block_free_part)
|
|
||||||
{
|
{
|
||||||
Deferral(1);
|
Deferral(1);
|
||||||
try_assert(msg->data_size >= MESSAGE_SIZE_MIN && msg->data_size <= MESSAGE_SIZE_MAX);
|
|
||||||
try_assert(msg->data_size <= msg_content.len);
|
|
||||||
|
|
||||||
u32 offset_increment = sizeof(MessageMeta) + msg->data_size;
|
// check msg_meta
|
||||||
if(block_free_part->len < offset_increment){
|
try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
|
||||||
Return RESULT_VALUE(u, 0);
|
try_assert(msg_meta->data_size >= MESSAGE_SIZE_MIN && msg_meta->data_size <= MESSAGE_SIZE_MAX);
|
||||||
|
try_assert(msg_meta->data_size <= msg_content.len);
|
||||||
|
try_assert(msg_meta->id > 0);
|
||||||
|
try_assert(msg_meta->sender_id > 0);
|
||||||
|
try_assert(msg_meta->timestamp.d.year > 2024);
|
||||||
|
// check block->datum.len
|
||||||
|
if(block->datum.len < block->offset + sizeof(MessageMeta) + msg_meta->data_size){
|
||||||
|
Return RESULT_VALUE(u, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(block_free_part->data, msg, sizeof(MessageMeta));
|
// write msg_meta
|
||||||
block_free_part->data += sizeof(MessageMeta);
|
memcpy(block->datum.data + block->offset, msg_meta, sizeof(MessageMeta));
|
||||||
block_free_part->len -= sizeof(MessageMeta);
|
block->offset += sizeof(MessageMeta);
|
||||||
|
|
||||||
memcpy(block_free_part->data, msg_content.data, msg->data_size);
|
// write msg_content
|
||||||
block_free_part->data += msg->data_size;
|
memcpy(block->datum.data + block->offset, msg_content.data, msg_meta->data_size);
|
||||||
block_free_part->len -= msg->data_size;
|
block->offset += msg_meta->data_size;
|
||||||
|
|
||||||
if(block_meta->message_count == 0)
|
block->messages_count++;
|
||||||
block_meta->first_message_id = msg->id;
|
Return RESULT_VALUE(u, true);
|
||||||
block_meta->message_count++;
|
|
||||||
block_meta->data_size += offset_increment;
|
|
||||||
|
|
||||||
Return RESULT_VALUE(u, offset_increment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result(u32) MessageBlock_readMessage(
|
Result(bool) MessageBlock_readMessage(MessageBlock* block,
|
||||||
Array(u8)* block_unread_part,
|
MessageMeta* msg_meta, Array(u8) msg_content)
|
||||||
MessageMeta* msg, Array(u8) msg_content)
|
|
||||||
{
|
{
|
||||||
Deferral(1);
|
Deferral(1);
|
||||||
try_assert(block_unread_part->len >= sizeof(MessageMeta) + MESSAGE_SIZE_MIN);
|
|
||||||
try_assert(msg_content.len >= MESSAGE_SIZE_MIN && msg_content.len <= MESSAGE_SIZE_MAX);
|
|
||||||
|
|
||||||
memcpy(msg, block_unread_part->data, sizeof(MessageMeta));
|
// check block
|
||||||
block_unread_part->data += sizeof(MessageMeta);
|
if(block->messages_count == 0){
|
||||||
block_unread_part->len -= sizeof(MessageMeta);
|
Return RESULT_VALUE(u, false);
|
||||||
|
|
||||||
if(msg->magic.n != MESSAGE_MAGIC.n){
|
|
||||||
Return RESULT_VALUE(u, 0);
|
|
||||||
}
|
}
|
||||||
try_assert(block_unread_part->len >= msg->data_size);
|
try_assert(block->datum.len >= block->offset + sizeof(MessageMeta) + MESSAGE_SIZE_MIN);
|
||||||
try_assert(msg->data_size >= MESSAGE_SIZE_MIN && msg->data_size <= MESSAGE_SIZE_MAX);
|
// check msg_content.len
|
||||||
try_assert(msg->id > 0);
|
try_assert(msg_content.len >= MESSAGE_SIZE_MAX);
|
||||||
try_assert(msg->sender_id > 0);
|
|
||||||
try_assert(msg->timestamp.d.year > 2024);
|
|
||||||
|
|
||||||
memcpy(msg_content.data, block_unread_part->data, msg->data_size);
|
// read msg_meta
|
||||||
block_unread_part->data += msg->data_size;
|
memcpy(msg_meta, block->datum.data + block->offset, sizeof(MessageMeta));
|
||||||
block_unread_part->len -= msg->data_size;
|
block->offset += sizeof(MessageMeta);
|
||||||
|
|
||||||
u32 offset_increment = sizeof(MessageMeta) + msg->data_size;
|
// check msg_meta
|
||||||
Return RESULT_VALUE(u, offset_increment);
|
try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
|
||||||
|
try_assert(msg_meta->data_size >= MESSAGE_SIZE_MIN && msg_meta->data_size <= MESSAGE_SIZE_MAX);
|
||||||
|
try_assert(msg_meta->data_size <= msg_content.len);
|
||||||
|
try_assert(msg_meta->id > 0);
|
||||||
|
try_assert(msg_meta->sender_id > 0);
|
||||||
|
try_assert(msg_meta->timestamp.d.year > 2024);
|
||||||
|
try_assert(block->datum.len >= block->offset + msg_meta->data_size);
|
||||||
|
|
||||||
|
// read msg_content
|
||||||
|
memcpy(msg_content.data, block->datum.data + block->offset, msg_meta->data_size);
|
||||||
|
block->offset += msg_meta->data_size;
|
||||||
|
|
||||||
|
block->messages_count--;
|
||||||
|
Return RESULT_VALUE(u, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendMessageRequest_construct(SendMessageRequest *ptr, PacketHeader *header,
|
void SendMessageRequest_construct(SendMessageRequest *ptr, PacketHeader *header,
|
||||||
@@ -219,19 +224,20 @@ void SendMessageResponse_construct(SendMessageResponse *ptr, PacketHeader *heade
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetMessageBlockRequest_construct(GetMessageBlockRequest *ptr, PacketHeader *header,
|
void GetMessageBlockRequest_construct(GetMessageBlockRequest *ptr, PacketHeader *header,
|
||||||
i64 channel_id, i64 first_message_id, u32 message_count)
|
i64 channel_id, i64 first_message_id, u32 messages_count)
|
||||||
{
|
{
|
||||||
_PacketHeader_construct(GetMessageBlockRequest);
|
_PacketHeader_construct(GetMessageBlockRequest);
|
||||||
zeroStruct(ptr);
|
zeroStruct(ptr);
|
||||||
ptr->channel_id = channel_id;
|
ptr->channel_id = channel_id;
|
||||||
ptr->first_message_id = first_message_id;
|
ptr->first_message_id = first_message_id;
|
||||||
ptr->message_count = message_count;
|
ptr->messages_count = messages_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetMessageBlockResponse_construct(GetMessageBlockResponse *ptr, PacketHeader *header,
|
void GetMessageBlockResponse_construct(GetMessageBlockResponse *ptr, PacketHeader *header,
|
||||||
MessageBlockMeta* block_meta)
|
u32 messages_count, u32 data_size)
|
||||||
{
|
{
|
||||||
_PacketHeader_construct(GetMessageBlockResponse);
|
_PacketHeader_construct(GetMessageBlockResponse);
|
||||||
zeroStruct(ptr);
|
zeroStruct(ptr);
|
||||||
memcpy(&ptr->block_meta, block_meta, sizeof(MessageBlockMeta));
|
ptr->messages_count = messages_count;
|
||||||
|
ptr->data_size = data_size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,30 +129,63 @@ typedef struct MessageMeta {
|
|||||||
i64 id;
|
i64 id;
|
||||||
i64 sender_id;
|
i64 sender_id;
|
||||||
DateTime timestamp; /* UTC */
|
DateTime timestamp; /* UTC */
|
||||||
} ALIGN_PACKET_STRUCT MessageMeta;
|
} ATTRIBUTE_ALIGNED(8) MessageMeta;
|
||||||
|
|
||||||
#define MESSAGE_MAGIC ((Magic32){ .bytes = { 'M', 's', 'g', 'S' } })
|
#define MessageMeta_construct(DATA_SIZE, MESSAGE_ID, SENDER_ID, TIMESTAMP) ((MessageMeta){ \
|
||||||
|
.magic = MESSAGE_MAGIC, \
|
||||||
|
.data_size = DATA_SIZE, \
|
||||||
|
.id = MESSAGE_ID, \
|
||||||
|
.sender_id = SENDER_ID, \
|
||||||
|
.timestamp = TIMESTAMP \
|
||||||
|
})
|
||||||
|
|
||||||
typedef struct MessageBlockMeta {
|
#define MESSAGE_MAGIC ((Magic32){ .bytes = { 'M', 's', 'g', '1' } })
|
||||||
i64 first_message_id;
|
|
||||||
u32 message_count;
|
|
||||||
u32 data_size;
|
|
||||||
} ALIGN_PACKET_STRUCT MessageBlockMeta;
|
|
||||||
|
|
||||||
/// @brief write msg_meta and msg_meta->data_size bytes from msg_content to buffer
|
typedef struct MessageBlock {
|
||||||
/// @param block_meta set to {0} if block is empty yet
|
Array(u8) datum; // sequence(MessageMeta, byte[MessageMeta.data_size])
|
||||||
/// @param block_free_part .data and .len are adjusted to point to free part
|
u32 messages_count;
|
||||||
/// @return amount of bytes written to block, may be 0 if msg_meta and msg_content don't fit
|
u32 offset;
|
||||||
Result(u32) MessageBlock_writeMessage(
|
} MessageBlock;
|
||||||
MessageMeta* msg_meta, Array(u8) msg_content,
|
|
||||||
MessageBlockMeta* block_meta, Array(u8)* block_free_part);
|
|
||||||
|
|
||||||
/// @brief read message meta and content from buffer
|
static inline void MessageBlock_construct(MessageBlock* self, Array(u8) datum, u32 messages_count){
|
||||||
/// @param block_unread_part .data and .len are adjusted to point to unread part
|
self->datum = datum;
|
||||||
/// @param msg_content .len must be >= MESSAGE_SIZE_MAX
|
self->messages_count = messages_count;
|
||||||
/// @return amount of bytes read from block, may be 0 if it doesn't start with MESSAGE_MAGIC
|
self->offset = 0;
|
||||||
Result(u32) MessageBlock_readMessage(
|
}
|
||||||
Array(u8)* block_unread_part,
|
|
||||||
|
static inline void MessageBlock_alloc(MessageBlock* self){
|
||||||
|
self->datum = Array_u8_alloc(MESSAGE_BLOCK_COUNT_MAX * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
|
||||||
|
Array_u8_memset(&self->datum, 0);
|
||||||
|
self->messages_count = 0;
|
||||||
|
self->offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void MessageBlock_reset(MessageBlock* self){
|
||||||
|
Array_u8_memset(&self->datum, 0);
|
||||||
|
self->messages_count = 0;
|
||||||
|
self->offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void MessageBlock_destroy(MessageBlock* self){
|
||||||
|
if(!self)
|
||||||
|
return;
|
||||||
|
Array_u8_destroy(&self->datum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief write msg_meta and msg_meta->data_size bytes from msg_content to block and increase block.messages_count
|
||||||
|
/// @param block use MessageBlock_alloc() to create empty block
|
||||||
|
/// @param msg_meta use MessageMeta_construct() to create message metadata
|
||||||
|
/// @param msg_content array of size >= msg_meta.data_size
|
||||||
|
/// @return false if msg_meta and msg_content don't fit in block.datum
|
||||||
|
Result(bool) MessageBlock_writeMessage(MessageBlock* block,
|
||||||
|
const MessageMeta* msg_meta, const Array(u8) msg_content);
|
||||||
|
|
||||||
|
/// @brief read msg_meta and msg_content from block and decrease block.messages_count
|
||||||
|
/// @param block a block with correct .datum and .messages_count
|
||||||
|
/// @param msg_meta out meta copied from block_data
|
||||||
|
/// @param msg_content out content copied from block_data. Array of size >= MESSAGE_SIZE_MAX
|
||||||
|
/// @return false if there are no messages to read (block.messages_count == 0)
|
||||||
|
Result(bool) MessageBlock_readMessage(MessageBlock* block,
|
||||||
MessageMeta* msg_meta, Array(u8) msg_content);
|
MessageMeta* msg_meta, Array(u8) msg_content);
|
||||||
|
|
||||||
|
|
||||||
@@ -178,18 +211,19 @@ void SendMessageResponse_construct(SendMessageResponse* ptr, PacketHeader* heade
|
|||||||
typedef struct GetMessageBlockRequest {
|
typedef struct GetMessageBlockRequest {
|
||||||
i64 channel_id;
|
i64 channel_id;
|
||||||
i64 first_message_id;
|
i64 first_message_id;
|
||||||
u32 message_count;
|
u32 messages_count;
|
||||||
} ALIGN_PACKET_STRUCT GetMessageBlockRequest;
|
} ALIGN_PACKET_STRUCT GetMessageBlockRequest;
|
||||||
|
|
||||||
void GetMessageBlockRequest_construct(GetMessageBlockRequest* ptr, PacketHeader* header,
|
void GetMessageBlockRequest_construct(GetMessageBlockRequest* ptr, PacketHeader* header,
|
||||||
i64 channel_id, i64 first_message_id, u32 message_count);
|
i64 channel_id, i64 first_message_id, u32 messages_count);
|
||||||
|
|
||||||
|
|
||||||
typedef struct GetMessageBlockResponse {
|
typedef struct GetMessageBlockResponse {
|
||||||
MessageBlockMeta block_meta;
|
u32 messages_count;
|
||||||
|
u32 data_size;
|
||||||
/* stream of size data_size : sequence (MessageMeta, byte[MessageMeta.data_size]) */
|
/* stream of size data_size : sequence (MessageMeta, byte[MessageMeta.data_size]) */
|
||||||
} ALIGN_PACKET_STRUCT GetMessageBlockResponse;
|
} ALIGN_PACKET_STRUCT GetMessageBlockResponse;
|
||||||
|
|
||||||
void GetMessageBlockResponse_construct(GetMessageBlockResponse* ptr, PacketHeader* header,
|
void GetMessageBlockResponse_construct(GetMessageBlockResponse* ptr, PacketHeader* header,
|
||||||
MessageBlockMeta* block_meta);
|
u32 messages_count, u32 data_size);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ void ClientConnection_close(ClientConnection* conn){
|
|||||||
return;
|
return;
|
||||||
EncryptedSocketTCP_destroy(&conn->sock);
|
EncryptedSocketTCP_destroy(&conn->sock);
|
||||||
Array_u8_destroy(&conn->session_key);
|
Array_u8_destroy(&conn->session_key);
|
||||||
Array_u8_destroy(&conn->message_block);
|
MessageBlock_destroy(&conn->message_block);
|
||||||
Array_u8_destroy(&conn->message_content);
|
Array_u8_destroy(&conn->message_content);
|
||||||
ServerQueries_free(conn->queries);
|
ServerQueries_free(conn->queries);
|
||||||
tsqlite_connection_close(conn->db);
|
tsqlite_connection_close(conn->db);
|
||||||
@@ -26,7 +26,7 @@ Result(ClientConnection*) ClientConnection_accept(ConnectionHandlerArgs* args)
|
|||||||
conn->session_id = args->session_id;
|
conn->session_id = args->session_id;
|
||||||
|
|
||||||
// buffers
|
// buffers
|
||||||
conn->message_block = Array_u8_alloc(MESSAGE_BLOCK_COUNT_MAX * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
|
MessageBlock_alloc(&conn->message_block);
|
||||||
conn->message_content = Array_u8_alloc(MESSAGE_SIZE_MAX);
|
conn->message_content = Array_u8_alloc(MESSAGE_SIZE_MAX);
|
||||||
|
|
||||||
// database
|
// database
|
||||||
|
|||||||
@@ -63,14 +63,14 @@ Result(void) Channel_saveMessage(ServerQueries* q,
|
|||||||
|
|
||||||
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
||||||
i64 channel_id, i64 first_message_id, u32 count,
|
i64 channel_id, i64 first_message_id, u32 count,
|
||||||
MessageBlockMeta* block_meta, Array(u8) block_data)
|
MessageBlock* block)
|
||||||
{
|
{
|
||||||
Deferral(4);
|
Deferral(4);
|
||||||
try_assert(channel_id > 0);
|
|
||||||
try_assert(block_data.len >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
|
|
||||||
if(count == 0){
|
if(count == 0){
|
||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
}
|
}
|
||||||
|
try_assert(channel_id > 0);
|
||||||
|
try_assert(block->datum.len >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
|
||||||
|
|
||||||
tsqlite_statement* st = q->messages.get_block;
|
tsqlite_statement* st = q->messages.get_block;
|
||||||
Defer(tsqlite_statement_reset(st));
|
Defer(tsqlite_statement_reset(st));
|
||||||
@@ -78,9 +78,7 @@ Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
|||||||
try_void(tsqlite_statement_bind_i64(st, "$first_message_id", first_message_id));
|
try_void(tsqlite_statement_bind_i64(st, "$first_message_id", first_message_id));
|
||||||
try_void(tsqlite_statement_bind_i64(st, "$count", count));
|
try_void(tsqlite_statement_bind_i64(st, "$count", count));
|
||||||
|
|
||||||
zeroStruct(block_meta);
|
MessageBlock_reset(block);
|
||||||
MessageMeta msg_meta = {0};
|
|
||||||
Array(u8) msg_content;
|
|
||||||
str tmp_str = str_null;
|
str tmp_str = str_null;
|
||||||
while(true){
|
while(true){
|
||||||
try(bool has_result, i, tsqlite_statement_step(st));
|
try(bool has_result, i, tsqlite_statement_step(st));
|
||||||
@@ -88,17 +86,24 @@ Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// id
|
// id
|
||||||
try(msg_meta.id, i, tsqlite_statement_getResult_i64(st));
|
try(i64 message_id, i, tsqlite_statement_getResult_i64(st));
|
||||||
// sender_id
|
// sender_id
|
||||||
try(msg_meta.sender_id, i, tsqlite_statement_getResult_i64(st));
|
try(i64 sender_id, i, tsqlite_statement_getResult_i64(st));
|
||||||
// content
|
// content
|
||||||
|
Array(u8) msg_content;
|
||||||
try_void(tsqlite_statement_getResult_blob(st, &msg_content));
|
try_void(tsqlite_statement_getResult_blob(st, &msg_content));
|
||||||
// timestamp
|
// timestamp
|
||||||
try_void(tsqlite_statement_getResult_str(st, &tmp_str));
|
try_void(tsqlite_statement_getResult_str(st, &tmp_str));
|
||||||
try_void(DateTime_parse(tmp_str.data, &msg_meta.timestamp));
|
DateTime timestamp;
|
||||||
|
try_void(DateTime_parse(tmp_str.data, ×tamp));
|
||||||
|
|
||||||
try(u32 write_n, u, MessageBlock_writeMessage(&msg_meta, msg_content, block_meta, &block_data));
|
MessageMeta msg_meta = MessageMeta_construct(
|
||||||
try_assert(write_n > 0);
|
msg_content.len,
|
||||||
|
message_id,
|
||||||
|
sender_id,
|
||||||
|
timestamp);
|
||||||
|
try(bool write_success, u, MessageBlock_writeMessage(block, &msg_meta, msg_content));
|
||||||
|
try_assert(write_success == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
|
|||||||
@@ -22,12 +22,11 @@ Result(i64) Channel_saveMessage(ServerQueries* q,
|
|||||||
i64 channel_id, i64 sender_id, Array(u8) content,
|
i64 channel_id, i64 sender_id, Array(u8) content,
|
||||||
DateTime* out_timestamp_utc);
|
DateTime* out_timestamp_utc);
|
||||||
|
|
||||||
/// @brief try to find `count` messages starting from `first_message_id`
|
/// @brief try to find count messages with id >= first_message_id
|
||||||
/// @param out_meta writes here information about found messages, .count can be 0 if no messages found
|
/// @param dst_block writes messages here. messages_count can be 0 if no messages were found
|
||||||
/// @param out_block .len must be >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX)
|
|
||||||
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
||||||
i64 channel_id, i64 first_message_id, u32 count,
|
i64 channel_id, i64 first_message_id, u32 count,
|
||||||
MessageBlockMeta* out_block_meta, Array(u8) out_block_data);
|
MessageBlock* dst_block);
|
||||||
|
|
||||||
|
|
||||||
/// @return existing user id or 0
|
/// @return existing user id or 0
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ declare_RequestHandler(GetMessageBlock)
|
|||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate message_count
|
// validate messages_count
|
||||||
if(req.message_count < 1 || req.message_count > MESSAGE_BLOCK_COUNT_MAX){
|
if(req.messages_count < 1 || req.messages_count > MESSAGE_BLOCK_COUNT_MAX){
|
||||||
try_void(sendErrorMessage(log_ctx, conn, res_head,
|
try_void(sendErrorMessage(log_ctx, conn, res_head,
|
||||||
LogSeverity_Warn, STR("invalid message count in request") ));
|
LogSeverity_Warn, STR("invalid message count in request") ));
|
||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
@@ -36,21 +36,20 @@ declare_RequestHandler(GetMessageBlock)
|
|||||||
Return RESULT_VOID;
|
Return RESULT_VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset block meta
|
|
||||||
zeroStruct(&conn->message_block_meta);
|
|
||||||
// get message block from channel
|
// get message block from channel
|
||||||
try_void(Channel_loadMessageBlock(conn->queries,
|
try_void(Channel_loadMessageBlock(conn->queries,
|
||||||
req.channel_id, req.first_message_id, req.message_count,
|
req.channel_id, req.first_message_id, req.messages_count,
|
||||||
&conn->message_block_meta, conn->message_block));
|
&conn->message_block));
|
||||||
|
|
||||||
// send response
|
// send response
|
||||||
GetMessageBlockResponse res;
|
GetMessageBlockResponse res;
|
||||||
GetMessageBlockResponse_construct(&res, res_head, &conn->message_block_meta);
|
GetMessageBlockResponse_construct(&res, res_head,
|
||||||
|
conn->message_block.messages_count, conn->message_block.offset);
|
||||||
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, res_head));
|
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, res_head));
|
||||||
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res));
|
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res));
|
||||||
if(conn->message_block_meta.data_size != 0){
|
if(conn->message_block.offset != 0){
|
||||||
try_void(EncryptedSocketTCP_send(&conn->sock,
|
try_void(EncryptedSocketTCP_send(&conn->sock,
|
||||||
Array_u8_sliceTo(conn->message_block, conn->message_block_meta.data_size))
|
Array_u8_sliceTo(conn->message_block.datum, conn->message_block.offset))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "tlibc/filesystem.h"
|
|
||||||
#include "tlibc/time.h"
|
#include "tlibc/time.h"
|
||||||
#include "server/server_internal.h"
|
#include "server/server_internal.h"
|
||||||
#include "server/responses/responses.h"
|
#include "server/responses/responses.h"
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ typedef struct ClientConnection {
|
|||||||
i64 user_id; // 0 for unauthorized
|
i64 user_id; // 0 for unauthorized
|
||||||
|
|
||||||
/* buffers */
|
/* buffers */
|
||||||
MessageBlockMeta message_block_meta;
|
MessageBlock message_block; // requested message block
|
||||||
Array(u8) message_block;
|
Array(u8) message_content; // sent message
|
||||||
Array(u8) message_content;
|
|
||||||
|
|
||||||
/* database */
|
/* database */
|
||||||
tsqlite_connection* db;
|
tsqlite_connection* db;
|
||||||
|
|||||||
Reference in New Issue
Block a user