client, builtin/client_file: Transferring of file content into cache
This commit is contained in:
parent
5899a081da
commit
60c0a790bb
@ -58,6 +58,7 @@ set(CLIENT_SRCS
|
|||||||
src/client/state.cpp
|
src/client/state.cpp
|
||||||
src/client/app.cpp
|
src/client/app.cpp
|
||||||
src/core/log.cpp
|
src/core/log.cpp
|
||||||
|
src/impl/fs.cpp
|
||||||
src/impl/tcpsocket.cpp
|
src/impl/tcpsocket.cpp
|
||||||
src/impl/sha1.cpp
|
src/impl/sha1.cpp
|
||||||
src/impl/packet_stream.cpp
|
src/impl/packet_stream.cpp
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "interface/module.h"
|
#include "interface/module.h"
|
||||||
#include "interface/server.h"
|
#include "interface/server.h"
|
||||||
#include "interface/event.h"
|
#include "interface/event.h"
|
||||||
#include "interface/fs.h"
|
|
||||||
#include "interface/sha1.h"
|
#include "interface/sha1.h"
|
||||||
#include "client_file/include/api.h"
|
#include "client_file/include/api.h"
|
||||||
#include "network/include/api.h"
|
#include "network/include/api.h"
|
||||||
@ -45,12 +44,16 @@ struct Module: public interface::Module, public client_file::Interface
|
|||||||
log_v(MODULE, "client_file init");
|
log_v(MODULE, "client_file init");
|
||||||
m_server->sub_event(this, Event::t("core:start"));
|
m_server->sub_event(this, Event::t("core:start"));
|
||||||
m_server->sub_event(this, Event::t("network:new_client"));
|
m_server->sub_event(this, Event::t("network:new_client"));
|
||||||
|
m_server->sub_event(this,
|
||||||
|
Event::t("network:packet_received/core:request_file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void event(const Event::Type &type, const Event::Private *p)
|
void event(const Event::Type &type, const Event::Private *p)
|
||||||
{
|
{
|
||||||
EVENT_VOIDN("core:start", on_start)
|
EVENT_VOIDN("core:start", on_start)
|
||||||
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
|
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
|
||||||
|
EVENT_TYPEN("network:packet_received/core:request_file", on_request_file,
|
||||||
|
network::Packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_start()
|
void on_start()
|
||||||
@ -116,6 +119,43 @@ struct Module: public interface::Module, public client_file::Interface
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_request_file(const network::Packet &packet)
|
||||||
|
{
|
||||||
|
ss_ file_name;
|
||||||
|
ss_ file_hash;
|
||||||
|
std::istringstream is(packet.data, std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::BinaryInputArchive ar(is);
|
||||||
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
|
}
|
||||||
|
log_v(MODULE, "File request: %s %s", cs(file_name),
|
||||||
|
cs(interface::sha1::hex(file_hash)));
|
||||||
|
auto it = m_files.find(file_name);
|
||||||
|
if(it == m_files.end()){
|
||||||
|
log_w(MODULE, "Requested file does not exist: \"%s\"", cs(file_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const FileInfo &info = *it->second.get();
|
||||||
|
if(info.hash != file_hash){
|
||||||
|
log_w(MODULE, "Requested file differs in hash: \"%s\": "
|
||||||
|
"requested %s, actual %s", cs(file_name),
|
||||||
|
cs(interface::sha1::hex(file_hash)),
|
||||||
|
cs(interface::sha1::hex(info.hash)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::ostringstream os(std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::BinaryOutputArchive ar(os);
|
||||||
|
ar(info.name);
|
||||||
|
ar(info.hash);
|
||||||
|
ar(info.content);
|
||||||
|
}
|
||||||
|
network::access(m_server, [&](network::Interface * inetwork){
|
||||||
|
inetwork->send(packet.sender, "core:file_content", os.str());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Interface
|
// Interface
|
||||||
|
|
||||||
void add_file_content(const ss_ &name, const ss_ &content)
|
void add_file_content(const ss_ &name, const ss_ &content)
|
||||||
|
@ -15,9 +15,11 @@ namespace network
|
|||||||
struct Packet: public interface::Event::Private
|
struct Packet: public interface::Event::Private
|
||||||
{
|
{
|
||||||
typedef size_t Type;
|
typedef size_t Type;
|
||||||
|
PeerInfo::Id sender = 0;
|
||||||
ss_ name;
|
ss_ name;
|
||||||
ss_ data;
|
ss_ data;
|
||||||
Packet(const ss_ &name, const ss_ &data): name(name), data(data){}
|
Packet(PeerInfo::Id sender, const ss_ &name, const ss_ &data):
|
||||||
|
sender(sender), name(name), data(data){}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NewClient: public interface::Event::Private
|
struct NewClient: public interface::Event::Private
|
||||||
|
@ -141,8 +141,8 @@ struct Module: public interface::Module, public network::Interface
|
|||||||
peer.packet_stream.input(peer.socket_buffer,
|
peer.packet_stream.input(peer.socket_buffer,
|
||||||
[&](const ss_ & name, const ss_ & data){
|
[&](const ss_ & name, const ss_ & data){
|
||||||
// Emit event
|
// Emit event
|
||||||
m_server->emit_event(ss_()+"network:packet_received:"+name,
|
m_server->emit_event(ss_()+"network:packet_received/"+name,
|
||||||
new Packet(name, data));
|
new Packet(peer.id, name, data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +277,7 @@ struct CApp: public Polycode::EventHandler, public App
|
|||||||
lua_setglobal(L, "__buildat_" #name);\
|
lua_setglobal(L, "__buildat_" #name);\
|
||||||
}
|
}
|
||||||
DEF_BUILDAT_FUNC(send_packet);
|
DEF_BUILDAT_FUNC(send_packet);
|
||||||
|
DEF_BUILDAT_FUNC(get_file_content)
|
||||||
|
|
||||||
ss_ init_lua_path = g_client_config.share_path+"/client/init.lua";
|
ss_ init_lua_path = g_client_config.share_path+"/client/init.lua";
|
||||||
int error = luaL_dofile(L, init_lua_path.c_str());
|
int error = luaL_dofile(L, init_lua_path.c_str());
|
||||||
@ -343,6 +344,22 @@ struct CApp: public Polycode::EventHandler, public App
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get_file_content(name: string)
|
||||||
|
static int l_get_file_content(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t name_len = 0;
|
||||||
|
const char *name_c = lua_tolstring(L, 1, &name_len);
|
||||||
|
ss_ name(name_c, name_len);
|
||||||
|
|
||||||
|
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
|
||||||
|
CApp *self = (CApp*)lua_touserdata(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
ss_ content = self->m_state->get_file_content(name);
|
||||||
|
lua_pushlstring(L, content.c_str(), content.size());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App* createApp(Polycode::PolycodeView *view)
|
App* createApp(Polycode::PolycodeView *view)
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "client/config.h"
|
#include "client/config.h"
|
||||||
#include "interface/tcpsocket.h"
|
#include "interface/tcpsocket.h"
|
||||||
#include "interface/packet_stream.h"
|
#include "interface/packet_stream.h"
|
||||||
|
#include "interface/sha1.h"
|
||||||
|
#include "interface/fs.h"
|
||||||
#include <cereal/archives/binary.hpp>
|
#include <cereal/archives/binary.hpp>
|
||||||
#include <cereal/types/string.hpp>
|
#include <cereal/types/string.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -22,11 +24,25 @@ struct CState: public State
|
|||||||
std::deque<char> m_socket_buffer;
|
std::deque<char> m_socket_buffer;
|
||||||
interface::PacketStream m_packet_stream;
|
interface::PacketStream m_packet_stream;
|
||||||
sp_<app::App> m_app;
|
sp_<app::App> m_app;
|
||||||
|
ss_ m_cache_path;
|
||||||
|
|
||||||
CState(sp_<app::App> app):
|
CState(sp_<app::App> app):
|
||||||
m_socket(interface::createTCPSocket()),
|
m_socket(interface::createTCPSocket()),
|
||||||
m_app(app)
|
m_app(app),
|
||||||
{}
|
m_cache_path(g_client_config.cache_path+"/remote")
|
||||||
|
{
|
||||||
|
// Create directory for cached files
|
||||||
|
auto *fs = interface::getGlobalFilesystem();
|
||||||
|
fs->create_directories(m_cache_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update()
|
||||||
|
{
|
||||||
|
if(m_socket->wait_data(0)){
|
||||||
|
read_socket();
|
||||||
|
handle_socket_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool connect(const ss_ &address, const ss_ &port)
|
bool connect(const ss_ &address, const ss_ &port)
|
||||||
{
|
{
|
||||||
@ -47,12 +63,10 @@ struct CState: public State
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void update()
|
ss_ get_file_content(const ss_ &name)
|
||||||
{
|
{
|
||||||
if(m_socket->wait_data(0)){
|
// TODO
|
||||||
read_socket();
|
return "Rullatortilla";
|
||||||
handle_socket_buffer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_socket()
|
void read_socket()
|
||||||
@ -99,30 +113,53 @@ struct CState: public State
|
|||||||
ar(file_name);
|
ar(file_name);
|
||||||
ar(file_hash);
|
ar(file_hash);
|
||||||
}
|
}
|
||||||
// TODO: Check if we already have this file
|
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
||||||
ss_ path = g_client_config.cache_path+"/remote/"+file_hash;
|
// Check if we already have this file
|
||||||
|
ss_ path = m_cache_path+"/"+file_hash_hex;
|
||||||
std::ifstream ifs(path, std::ios::binary);
|
std::ifstream ifs(path, std::ios::binary);
|
||||||
if(ifs.good()){
|
if(ifs.good()){
|
||||||
// We have it; no need to ask this file
|
// We have it; no need to ask this file
|
||||||
|
log_i(MODULE, "%s %s: cached",
|
||||||
|
cs(file_hash_hex), cs(file_name));
|
||||||
} else {
|
} else {
|
||||||
// We don't have it; request this file
|
// We don't have it; request this file
|
||||||
|
log_i(MODULE, "%s %s: requesting",
|
||||||
|
cs(file_hash_hex), cs(file_name));
|
||||||
|
std::ostringstream os(std::ios::binary);
|
||||||
|
{
|
||||||
|
cereal::BinaryOutputArchive ar(os);
|
||||||
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
}
|
}
|
||||||
|
send_packet("core:request_file", os.str());
|
||||||
}
|
}
|
||||||
if(packet_name == "core:transfer_file"){
|
return;
|
||||||
|
}
|
||||||
|
if(packet_name == "core:file_content"){
|
||||||
ss_ file_name;
|
ss_ file_name;
|
||||||
|
ss_ file_hash;
|
||||||
ss_ file_content;
|
ss_ file_content;
|
||||||
std::istringstream is(data, std::ios::binary);
|
std::istringstream is(data, std::ios::binary);
|
||||||
{
|
{
|
||||||
cereal::BinaryInputArchive ar(is);
|
cereal::BinaryInputArchive ar(is);
|
||||||
ar(file_name);
|
ar(file_name);
|
||||||
|
ar(file_hash);
|
||||||
ar(file_content);
|
ar(file_content);
|
||||||
}
|
}
|
||||||
// TODO: Check filename for malicious characters "/.\"\\"
|
ss_ file_hash2 = interface::sha1::calculate(file_content);
|
||||||
// TODO: Never use a filename in the filesystem that was supplied by
|
if(file_hash != file_hash2){
|
||||||
// server
|
log_w(MODULE, "Requested file differs in hash: \"%s\": "
|
||||||
ss_ path = g_client_config.cache_path+"/remote/"+file_name;
|
"requested %s, actual %s", cs(file_name),
|
||||||
|
cs(interface::sha1::hex(file_hash)),
|
||||||
|
cs(interface::sha1::hex(file_hash2)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ss_ file_hash_hex = interface::sha1::hex(file_hash);
|
||||||
|
ss_ path = g_client_config.cache_path+"/remote/"+file_hash_hex;
|
||||||
|
log_i(MODULE, "Saving %s to %s", cs(file_name), cs(path));
|
||||||
std::ofstream of(path, std::ios::binary);
|
std::ofstream of(path, std::ios::binary);
|
||||||
of<<file_content;
|
of<<file_content;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -15,9 +15,10 @@ namespace client
|
|||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
virtual ~State(){}
|
virtual ~State(){}
|
||||||
|
virtual void update() = 0;
|
||||||
virtual bool connect(const ss_ &address, const ss_ &port) = 0;
|
virtual bool connect(const ss_ &address, const ss_ &port) = 0;
|
||||||
virtual void send_packet(const ss_ &name, const ss_ &data) = 0;
|
virtual void send_packet(const ss_ &name, const ss_ &data) = 0;
|
||||||
virtual void update() = 0;
|
virtual ss_ get_file_content(const ss_ &name) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
State* createState(sp_<app::App> app);
|
State* createState(sp_<app::App> app);
|
||||||
|
@ -17,6 +17,10 @@ struct CFilesystem : public Filesystem
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
bool create_directories(const ss_ &path)
|
||||||
|
{
|
||||||
|
return c55fs::CreateAllDirs(path);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Filesystem* getGlobalFilesystem()
|
Filesystem* getGlobalFilesystem()
|
||||||
|
@ -11,13 +11,13 @@ void PacketStream::input(std::deque<char> &socket_buffer,
|
|||||||
if(socket_buffer.size() < 6)
|
if(socket_buffer.size() < 6)
|
||||||
return;
|
return;
|
||||||
size_t type =
|
size_t type =
|
||||||
socket_buffer[0]<<0 |
|
(socket_buffer[0] & 0xff)<<0 |
|
||||||
socket_buffer[1]<<8;
|
(socket_buffer[1] & 0xff)<<8;
|
||||||
size_t size =
|
size_t size =
|
||||||
socket_buffer[2]<<0 |
|
(socket_buffer[2] & 0xff)<<0 |
|
||||||
socket_buffer[3]<<8 |
|
(socket_buffer[3] & 0xff)<<8 |
|
||||||
socket_buffer[4]<<16 |
|
(socket_buffer[4] & 0xff)<<16 |
|
||||||
socket_buffer[5]<<24;
|
(socket_buffer[5] & 0xff)<<24;
|
||||||
log_d(MODULE, "size=%zu", size);
|
log_d(MODULE, "size=%zu", size);
|
||||||
if(socket_buffer.size() < 6 + size)
|
if(socket_buffer.size() < 6 + size)
|
||||||
return;
|
return;
|
||||||
|
@ -13,6 +13,8 @@ namespace interface
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtual sv_<Node> list_directory(const ss_ &path) = 0;
|
virtual sv_<Node> list_directory(const ss_ &path) = 0;
|
||||||
|
|
||||||
|
virtual bool create_directories(const ss_ &path) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Filesystem* getGlobalFilesystem();
|
Filesystem* getGlobalFilesystem();
|
||||||
|
@ -82,9 +82,7 @@ struct Module: public interface::Module
|
|||||||
log_v(MODULE, "on_files_sent(): recipient=%zu", event.recipient);
|
log_v(MODULE, "on_files_sent(): recipient=%zu", event.recipient);
|
||||||
network::access(m_server, [&](network::Interface * inetwork){
|
network::access(m_server, [&](network::Interface * inetwork){
|
||||||
inetwork->send(event.recipient, "core:run_script",
|
inetwork->send(event.recipient, "core:run_script",
|
||||||
"print(\"TODO: Run init.lua\")");
|
"__buildat_run_script_file(\"test1/init.lua\")");
|
||||||
inetwork->send(event.recipient, "core:run_script",
|
|
||||||
"buildat:send_packet(\"foo\", \"bar\")");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user