mirror of
https://github.com/falk-werner/webfuse
synced 2024-10-27 20:34:10 +00:00
implemented basic request
This commit is contained in:
parent
bc024481f9
commit
7924fa1191
@ -23,6 +23,7 @@ add_library(webfuse_static STATIC
|
|||||||
src/webfuse/filesystem/empty_filesystem.cpp
|
src/webfuse/filesystem/empty_filesystem.cpp
|
||||||
src/webfuse/ws/config.cpp
|
src/webfuse/ws/config.cpp
|
||||||
src/webfuse/ws/server.cpp
|
src/webfuse/ws/server.cpp
|
||||||
|
src/webfuse/ws/message.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(webfuse_static PUBLIC src)
|
target_include_directories(webfuse_static PUBLIC src)
|
||||||
|
36
doc/protocol.md
Normal file
36
doc/protocol.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# Webufse 2 Protocol
|
||||||
|
|
||||||
|
## Endianness
|
||||||
|
|
||||||
|
All numeric data types are transferred in [Big Endian](https://en.wikipedia.org/wiki/Endianness).
|
||||||
|
For instance, the uint32 value 1 will be transferred as
|
||||||
|
|
||||||
|
00 00 00 01
|
||||||
|
|
||||||
|
## Data Types
|
||||||
|
|
||||||
|
### Basic data types
|
||||||
|
|
||||||
|
| Data | Width | Description |
|
||||||
|
| ---- | ------ | ----------- |
|
||||||
|
| bool | 8 bit | Represents a boolean value |
|
||||||
|
| i32 | 32 bit |
|
||||||
|
|
||||||
|
## Message
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### access
|
||||||
|
|
||||||
|
| Field | Data Type | Description |
|
||||||
|
| ----- | ---------------- | ----------- |
|
||||||
|
| | uint32 | message id |
|
||||||
|
| type | uint8 | message type (0x00) |
|
||||||
|
| path | string | |
|
||||||
|
| mode | access_mode (i8) |
|
||||||
|
|
||||||
|
#### Response
|
||||||
|
|
||||||
|
| Field |
|
@ -1,11 +1,157 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import os
|
||||||
import websockets
|
import websockets
|
||||||
|
import errno
|
||||||
|
|
||||||
async def hello():
|
F_OK = 0
|
||||||
async with websockets.connect('ws://localhost:8081') as websocket:
|
R_OK = 4
|
||||||
await websocket.send('Hello')
|
W_OK = 2
|
||||||
await websocket.recv()
|
X_OK = 1
|
||||||
|
|
||||||
asyncio.run(hello())
|
RESPONSE = 0x80
|
||||||
|
|
||||||
|
ERRNO = {
|
||||||
|
-errno.E2BIG : -7,
|
||||||
|
-errno.EACCES : -13,
|
||||||
|
-errno.EAGAIN : -11,
|
||||||
|
-errno.EBADF : -9,
|
||||||
|
-errno.EBUSY : -16,
|
||||||
|
-errno.EDESTADDRREQ : -89,
|
||||||
|
-errno.EDQUOT : -122,
|
||||||
|
-errno.EEXIST : -17,
|
||||||
|
-errno.EFAULT : -14,
|
||||||
|
-errno.EFBIG : -27,
|
||||||
|
-errno.EINTR : -4,
|
||||||
|
-errno.EINVAL : -22,
|
||||||
|
-errno.EIO : -5,
|
||||||
|
-errno.EISDIR : -21,
|
||||||
|
-errno.ELOOP : -40,
|
||||||
|
-errno.EMFILE : -24,
|
||||||
|
-errno.EMLINK : -31,
|
||||||
|
-errno.ENAMETOOLONG : -36,
|
||||||
|
-errno.ENFILE : -23,
|
||||||
|
-errno.ENODATA : -61,
|
||||||
|
-errno.ENODEV : -19,
|
||||||
|
-errno.ENOENT : -2,
|
||||||
|
-errno.ENOMEM : -12,
|
||||||
|
-errno.ENOSPC : -28,
|
||||||
|
-errno.ENOSYS : -38,
|
||||||
|
-errno.ENOTDIR : -20,
|
||||||
|
-errno.ENOTEMPTY : -39,
|
||||||
|
-errno.ENOTSUP : -95,
|
||||||
|
-errno.ENXIO : -6,
|
||||||
|
-errno.EOVERFLOW : -75,
|
||||||
|
-errno.EPERM : -1,
|
||||||
|
-errno.EPIPE : -32,
|
||||||
|
-errno.ERANGE : -34,
|
||||||
|
-errno.EROFS : -30,
|
||||||
|
-errno.ETXTBSY : -26,
|
||||||
|
-errno.EXDEV : -18
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageReader:
|
||||||
|
def __init__(self, buffer):
|
||||||
|
self.buffer = buffer
|
||||||
|
self.offset = 0
|
||||||
|
|
||||||
|
def read_u8(self):
|
||||||
|
value = self.buffer[self.offset]
|
||||||
|
self.offset += 1
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_u32(self):
|
||||||
|
value = (self.buffer[self.offset] << 24) + (self.buffer[self.offset + 1] << 16) + (self.buffer[self.offset + 2] << 8) + self.buffer[self.offset + 3]
|
||||||
|
self.offset += 4
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_str(self):
|
||||||
|
return self.read_bytes().decode()
|
||||||
|
|
||||||
|
def read_bytes(self):
|
||||||
|
size = self.read_u32()
|
||||||
|
value = self.buffer[self.offset : self.offset + size]
|
||||||
|
self.offset += size
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_path(self, base_path):
|
||||||
|
local_path = self.read_str().lstrip('/')
|
||||||
|
return os.path.join(base_path, local_path)
|
||||||
|
|
||||||
|
def read_access_mode(self):
|
||||||
|
value = self.read_u8()
|
||||||
|
mode = os.F_OK if F_OK == (value & F_OK) else 0
|
||||||
|
mode += os.R_OK if R_OK == (value & R_OK) else 0
|
||||||
|
mode += os.W_OK if W_OK == (value & W_OK) else 0
|
||||||
|
mode += os.X_OK if X_OK == (value & X_OK) else 0
|
||||||
|
return mode
|
||||||
|
|
||||||
|
|
||||||
|
class MessageWriter:
|
||||||
|
def __init__(self, message_id, message_type):
|
||||||
|
self.buffer = []
|
||||||
|
self.write_u32(message_id)
|
||||||
|
self.write_u8(message_type)
|
||||||
|
|
||||||
|
def write_u8(self, value):
|
||||||
|
self.buffer.append(value)
|
||||||
|
|
||||||
|
def write_u32(self, value):
|
||||||
|
a = (value >> 24) & 0xff
|
||||||
|
b = (value >> 16) & 0xff
|
||||||
|
c = (value >> 8) & 0xff
|
||||||
|
d = value & 0xff
|
||||||
|
self.buffer.extend([a, b, c, d])
|
||||||
|
|
||||||
|
def write_i32(self, value):
|
||||||
|
self.write_u32(value & 0xffffffff)
|
||||||
|
|
||||||
|
def write_result(self, value):
|
||||||
|
if 0 > value:
|
||||||
|
if value in ERRNO:
|
||||||
|
value = ERRNO[value]
|
||||||
|
self.write_i32(value)
|
||||||
|
|
||||||
|
def get_bytes(self):
|
||||||
|
return bytearray(self.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
class FilesystemProvider:
|
||||||
|
def __init__(self, path, url):
|
||||||
|
self.root = os.path.abspath(path)
|
||||||
|
self.url = url
|
||||||
|
self.commands = {
|
||||||
|
0x01: FilesystemProvider.access
|
||||||
|
}
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
async with websockets.connect(self.url) as connection:
|
||||||
|
while True:
|
||||||
|
request = await connection.recv()
|
||||||
|
reader = MessageReader(request)
|
||||||
|
message_id = reader.read_u32()
|
||||||
|
message_type = reader.read_u8()
|
||||||
|
writer = MessageWriter(message_id, RESPONSE + message_type)
|
||||||
|
if message_type in self.commands:
|
||||||
|
method = self.commands[message_type]
|
||||||
|
method(self, reader, writer)
|
||||||
|
else:
|
||||||
|
print("unknown message type: %d" % message_type)
|
||||||
|
response = writer.get_bytes()
|
||||||
|
await connection.send(response)
|
||||||
|
|
||||||
|
def access(self, reader, writer):
|
||||||
|
path = reader.read_path(self.root)
|
||||||
|
mode = reader.read_access_mode()
|
||||||
|
result = -errno.EACCES
|
||||||
|
try:
|
||||||
|
if os.access(path, mode) == True:
|
||||||
|
result = 0
|
||||||
|
except OSError as ex:
|
||||||
|
result = -ex.errno
|
||||||
|
writer.write_result(result)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
provider = FilesystemProvider('.', 'ws://localhost:8081')
|
||||||
|
asyncio.run(provider.run())
|
||||||
|
@ -18,8 +18,10 @@ status filesystem::access(std::string const & path, access_mode mode)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::access_req);
|
||||||
auto resp = proxy.perform(req).get();
|
req.add_str(path);
|
||||||
|
req.add_i8(mode);
|
||||||
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -32,8 +34,8 @@ status filesystem::getattr(std::string const & path, file_attributes & attr)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::getattr_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -46,8 +48,8 @@ status filesystem::readlink(std::string const & path, std::string & out)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::readlink_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -60,8 +62,8 @@ status filesystem::symlink(std::string const & target, std::string const & linkp
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::symlink_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -74,8 +76,8 @@ status filesystem::link(std::string const & old_path, std::string const & new_pa
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::link_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -88,8 +90,8 @@ status filesystem::rename(std::string const & old_path, std::string const & new_
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::rename_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -102,8 +104,8 @@ status filesystem::chmod(std::string const & path, filemode mode)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::chmod_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -116,8 +118,8 @@ status filesystem::chown(std::string const & path, user_id uid, group_id gid)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::chown_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -130,8 +132,8 @@ status filesystem::truncate(std::string const & path, uint64_t offset, filehandl
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::truncate_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -144,8 +146,8 @@ status filesystem::fsync(std::string const & path, bool is_datasync, filehandle
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::fsync_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -158,8 +160,8 @@ status filesystem::open(std::string const & path, openflags flags, filehandle &
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::open_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -172,8 +174,8 @@ status filesystem::mknod(std::string const & path, filemode mode, uint64_t rdev)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::mknod_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -186,8 +188,8 @@ status filesystem::create(std::string const & path, filemode mode, filehandle &
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::create_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -200,8 +202,8 @@ status filesystem::release(std::string const & path, filehandle handle)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::release_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -214,8 +216,8 @@ status filesystem::unlink(std::string const & path)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::unlink_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -228,8 +230,8 @@ status filesystem::read(std::string const & path, char * buffer, size_t buffer_s
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::read_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -242,8 +244,8 @@ status filesystem::write(std::string const & path, char const * buffer, size_t b
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::write_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -256,8 +258,8 @@ status filesystem::mkdir(std::string const & path, filemode mode)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::mkdir_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -270,8 +272,8 @@ status filesystem::readdir(std::string const & path, std::vector<std::string> &
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::readdir_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -284,8 +286,8 @@ status filesystem::rmdir(std::string const & path)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::rmdir_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -298,8 +300,8 @@ status filesystem::statfs(std::string const & path, filesystem_statistics & stat
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string req;
|
message req(message_type::statfs_req);
|
||||||
auto resp = proxy.perform(req).get();
|
proxy.perform(std::move(req));
|
||||||
return status::bad_enoent;
|
return status::bad_enoent;
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
|
37
src/webfuse/message_type.hpp
Normal file
37
src/webfuse/message_type.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef WEBFUSE_MESSAGETYPE_HPP
|
||||||
|
#define WEBFUSE_MESSAGETYPE_HPP
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
namespace webfuse
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class message_type: uint8_t
|
||||||
|
{
|
||||||
|
access_req = 0x01,
|
||||||
|
getattr_req = 0x02,
|
||||||
|
readlink_req = 0x03,
|
||||||
|
symlink_req = 0x04,
|
||||||
|
link_req = 0x05,
|
||||||
|
rename_req = 0x06,
|
||||||
|
chmod_req = 0x07,
|
||||||
|
chown_req = 0x08,
|
||||||
|
truncate_req = 0x09,
|
||||||
|
fsync_req = 0x0a,
|
||||||
|
open_req = 0x0b,
|
||||||
|
mknod_req = 0x0c,
|
||||||
|
create_req = 0x0d,
|
||||||
|
release_req = 0x0e,
|
||||||
|
unlink_req = 0x0f,
|
||||||
|
read_req = 0x10,
|
||||||
|
write_req = 0x11,
|
||||||
|
mkdir_req = 0x12,
|
||||||
|
readdir_req = 0x13,
|
||||||
|
rmdir_req = 0x14,
|
||||||
|
statfs_req = 0x15
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
129
src/webfuse/ws/message.cpp
Normal file
129
src/webfuse/ws/message.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "message.hpp"
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
|
||||||
|
namespace webfuse
|
||||||
|
{
|
||||||
|
|
||||||
|
message::message(message_type msg_type)
|
||||||
|
: id(0)
|
||||||
|
, data(LWS_PRE)
|
||||||
|
{
|
||||||
|
add_u32(0);
|
||||||
|
add_u8(static_cast<uint8_t>(msg_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
message::message(message && other)
|
||||||
|
{
|
||||||
|
this->id = other.id;
|
||||||
|
this->data = std::move(other.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
message& message::operator=(message && other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
this->id = other.id;
|
||||||
|
this->data = std::move(other.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::set_id(uint32_t value)
|
||||||
|
{
|
||||||
|
id = id;
|
||||||
|
data[LWS_PRE ] = (id >> 24) & 0xff;
|
||||||
|
data[LWS_PRE + 1] = (id >> 16) & 0xff;
|
||||||
|
data[LWS_PRE + 2] = (id >> 8) & 0xff;
|
||||||
|
data[LWS_PRE + 3] = id & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t message::get_id() const
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_bool(bool value)
|
||||||
|
{
|
||||||
|
data.push_back(value ? 0x01 : 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_u8(uint8_t value)
|
||||||
|
{
|
||||||
|
data.push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_i8(int8_t value)
|
||||||
|
{
|
||||||
|
data.push_back(static_cast<uint8_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_i32(int32_t value)
|
||||||
|
{
|
||||||
|
add_u32((static_cast<uint32_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_u32(uint32_t value)
|
||||||
|
{
|
||||||
|
auto const offset = data.size();
|
||||||
|
data.resize(offset + 4);
|
||||||
|
data[offset ] = (value >> 24) & 0xff;
|
||||||
|
data[offset + 1] = (value >> 16) & 0xff;
|
||||||
|
data[offset + 2] = (value >> 8) & 0xff;
|
||||||
|
data[offset + 3] = value & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_u64(uint64_t value)
|
||||||
|
{
|
||||||
|
auto const offset = data.size();
|
||||||
|
data.resize(offset + 8);
|
||||||
|
data[offset ] = (value >> 56) & 0xff;
|
||||||
|
data[offset + 1] = (value >> 48) & 0xff;
|
||||||
|
data[offset + 2] = (value >> 40) & 0xff;
|
||||||
|
data[offset + 3] = (value >> 32) & 0xff;
|
||||||
|
data[offset + 4] = (value >> 24) & 0xff;
|
||||||
|
data[offset + 5] = (value >> 16) & 0xff;
|
||||||
|
data[offset + 6] = (value >> 8) & 0xff;
|
||||||
|
data[offset + 7] = value & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_str(std::string const &value)
|
||||||
|
{
|
||||||
|
add_data(value.data(), value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_data(char const * buffer, size_t size)
|
||||||
|
{
|
||||||
|
uint32_t const effective_size = size & 0xffffffff;
|
||||||
|
add_u32(effective_size);
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
auto const offset = data.size();
|
||||||
|
data.resize(offset + effective_size);
|
||||||
|
void * to = reinterpret_cast<void*>(&data.data()[offset]);
|
||||||
|
void const * from = reinterpret_cast<void const *>(buffer);
|
||||||
|
memcpy(to, from, effective_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void message::add_strings(std::vector<std::string> const & list)
|
||||||
|
{
|
||||||
|
uint32_t const count = list.size() & 0xffffffff;
|
||||||
|
add_u32(count);
|
||||||
|
for (auto const & item: list)
|
||||||
|
{
|
||||||
|
add_str(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char * message::get_data(size_t &size)
|
||||||
|
{
|
||||||
|
size = data.size() - LWS_PRE;
|
||||||
|
void * result = reinterpret_cast<void *>(&data.data()[LWS_PRE]);
|
||||||
|
|
||||||
|
return reinterpret_cast<unsigned char *>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
45
src/webfuse/ws/message.hpp
Normal file
45
src/webfuse/ws/message.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef WEBFUSE_MESSAGEBUILDER_HPP
|
||||||
|
#define WEBFUSE_MESSAGEBUILDER_HPP
|
||||||
|
|
||||||
|
#include "webfuse/message_type.hpp"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace webfuse
|
||||||
|
{
|
||||||
|
|
||||||
|
class message
|
||||||
|
{
|
||||||
|
message(message const &) = delete;
|
||||||
|
message& operator=(message const &) = delete;
|
||||||
|
public:
|
||||||
|
explicit message(message_type msg_type);
|
||||||
|
~message() = default;
|
||||||
|
message(message && other);
|
||||||
|
message& operator=(message && other);
|
||||||
|
|
||||||
|
void set_id(uint32_t value);
|
||||||
|
uint32_t get_id() const;
|
||||||
|
|
||||||
|
void add_bool(bool value);
|
||||||
|
void add_u8(uint8_t value);
|
||||||
|
void add_i8(int8_t value);
|
||||||
|
void add_i32(int32_t value);
|
||||||
|
void add_u32(uint32_t value);
|
||||||
|
void add_u64(uint64_t value);
|
||||||
|
void add_str(std::string const &value);
|
||||||
|
void add_data(char const * buffer, size_t size);
|
||||||
|
void add_strings(std::vector<std::string> const & list);
|
||||||
|
|
||||||
|
unsigned char * get_data(size_t &size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t id;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
23
src/webfuse/ws/messagereader.hpp
Normal file
23
src/webfuse/ws/messagereader.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef WEBFUSE_MESSAGEREADER_HPP
|
||||||
|
#define WEBFUSE_MESSAGEREADER_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace webfuse
|
||||||
|
{
|
||||||
|
|
||||||
|
class messagereader
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit messagereader(std::string && value);
|
||||||
|
~messagereader() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string data;
|
||||||
|
size_t pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,40 +1,108 @@
|
|||||||
#include "webfuse/ws/server.hpp"
|
#include "webfuse/ws/server.hpp"
|
||||||
|
#include "webfuse/ws/message.hpp"
|
||||||
|
|
||||||
#include <libwebsockets.h>
|
#include <libwebsockets.h>
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <future>
|
||||||
|
#include <chrono>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct user_data
|
||||||
|
{
|
||||||
|
struct lws * connection = nullptr;
|
||||||
|
|
||||||
|
std::mutex mut;
|
||||||
|
std::queue<webfuse::message> requests;
|
||||||
|
std::unordered_map<uint32_t, std::promise<std::string>> pending_responses;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
||||||
static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
|
static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
|
||||||
void *user, void *in, size_t len)
|
void *user, void *in, size_t len)
|
||||||
{
|
{
|
||||||
|
auto const * protocol = lws_get_protocol(wsi);
|
||||||
|
if (nullptr == protocol) { return 0; }
|
||||||
|
if (&ws_server_callback != protocol->callback) { return 0; }
|
||||||
|
|
||||||
|
auto * data = reinterpret_cast<user_data *>(protocol->user);
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
switch(reason)
|
switch(reason)
|
||||||
{
|
{
|
||||||
case LWS_CALLBACK_PROTOCOL_INIT:
|
|
||||||
std::cout << "lws: protocol init "<< std::endl;
|
|
||||||
break;
|
|
||||||
case LWS_CALLBACK_ESTABLISHED:
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
std::cout << "lws: established "<< std::endl;
|
std::cout << "lws: established "<< std::endl;
|
||||||
|
if (nullptr == data->connection)
|
||||||
|
{
|
||||||
|
data->connection = wsi;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_CLOSED:
|
case LWS_CALLBACK_CLOSED:
|
||||||
std::cout << "lws: closed "<< std::endl;
|
std::cout << "lws: closed "<< std::endl;
|
||||||
|
if (wsi == data->connection)
|
||||||
|
{
|
||||||
|
data->connection = nullptr;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_RECEIVE:
|
case LWS_CALLBACK_RECEIVE:
|
||||||
std::cout << "lws: receive "<< std::endl;
|
std::cout << "lws: receive "<< std::endl;
|
||||||
break;
|
break;
|
||||||
case LWS_CALLBACK_SERVER_WRITEABLE:
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
std::cout << "lws: server writable "<< std::endl;
|
std::cout << "lws: server writable "<< std::endl;
|
||||||
|
{
|
||||||
|
webfuse::message msg(webfuse::message_type::access_req);
|
||||||
|
bool has_msg = false;
|
||||||
|
bool has_more = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(data->mut);
|
||||||
|
has_msg = !(data->requests.empty());
|
||||||
|
if (has_msg)
|
||||||
|
{
|
||||||
|
has_msg = true;
|
||||||
|
msg = std::move(data->requests.front());
|
||||||
|
data->requests.pop();
|
||||||
|
|
||||||
|
has_more = !(data->requests.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_msg)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
unsigned char * raw_data = msg.get_data(size);
|
||||||
|
int const rc = lws_write(data->connection, raw_data, size, LWS_WRITE_BINARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -57,7 +125,7 @@ public:
|
|||||||
protocols[0].name = "webfuse2";
|
protocols[0].name = "webfuse2";
|
||||||
protocols[0].callback = &ws_server_callback;
|
protocols[0].callback = &ws_server_callback;
|
||||||
protocols[0].per_session_data_size = 0;
|
protocols[0].per_session_data_size = 0;
|
||||||
protocols[0].user = nullptr;
|
protocols[0].user = reinterpret_cast<void*>(&data);
|
||||||
|
|
||||||
memset(reinterpret_cast<void*>(&info), 0, sizeof(info));
|
memset(reinterpret_cast<void*>(&info), 0, sizeof(info));
|
||||||
info.port = config.port;
|
info.port = config.port;
|
||||||
@ -74,6 +142,22 @@ public:
|
|||||||
thread = std::thread([this]() {
|
thread = std::thread([this]() {
|
||||||
while (!shutdown_requested)
|
while (!shutdown_requested)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard lock(data.mut);
|
||||||
|
if (!data.requests.empty())
|
||||||
|
{
|
||||||
|
if (nullptr != data.connection)
|
||||||
|
{
|
||||||
|
lws_callback_on_writable(data.connection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.requests = std::move(std::queue<webfuse::message>());
|
||||||
|
data.pending_responses.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lws_service(context, 0);
|
lws_service(context, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +177,7 @@ public:
|
|||||||
lws_protocols protocols[2];
|
lws_protocols protocols[2];
|
||||||
lws_context_creation_info info;
|
lws_context_creation_info info;
|
||||||
lws_context * context;
|
lws_context * context;
|
||||||
|
user_data data;
|
||||||
};
|
};
|
||||||
|
|
||||||
ws_server::ws_server(ws_config const & config)
|
ws_server::ws_server(ws_config const & config)
|
||||||
@ -124,20 +209,26 @@ ws_server& ws_server::operator=(ws_server && other)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future<std::string> ws_server::perform(std::string const & req)
|
void ws_server::perform(message msg)
|
||||||
{
|
{
|
||||||
std::promise<std::string> resp;
|
std::future<std::string> f;
|
||||||
|
{
|
||||||
|
std::promise<std::string> p;
|
||||||
|
f = p.get_future();
|
||||||
|
|
||||||
try
|
std::lock_guard lock(d->data.mut);
|
||||||
{
|
d->data.requests.emplace(std::move(msg));
|
||||||
throw std::runtime_error("not implemented");
|
d->data.pending_responses.emplace(42, std::move(p));
|
||||||
}
|
}
|
||||||
catch (std::exception const & ex)
|
|
||||||
|
lws_cancel_service(d->context);
|
||||||
|
if(std::future_status::timeout == f.wait_for(std::chrono::seconds(1)))
|
||||||
{
|
{
|
||||||
resp.set_exception(std::current_exception());
|
throw std::runtime_error("timeout");
|
||||||
}
|
}
|
||||||
|
std::string resp = f.get();
|
||||||
return resp.get_future();
|
|
||||||
|
throw std::runtime_error("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,8 +2,12 @@
|
|||||||
#define WEBFUSE_WSSERVER_HPP
|
#define WEBFUSE_WSSERVER_HPP
|
||||||
|
|
||||||
#include "webfuse/ws/config.hpp"
|
#include "webfuse/ws/config.hpp"
|
||||||
#include <future>
|
#include "webfuse/ws/message.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace webfuse
|
namespace webfuse
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -16,7 +20,8 @@ public:
|
|||||||
~ws_server();
|
~ws_server();
|
||||||
ws_server(ws_server && other);
|
ws_server(ws_server && other);
|
||||||
ws_server& operator=(ws_server && other);
|
ws_server& operator=(ws_server && other);
|
||||||
std::future<std::string> perform(std::string const & req);
|
|
||||||
|
void perform(message msg);
|
||||||
private:
|
private:
|
||||||
class detail;
|
class detail;
|
||||||
detail * d;
|
detail * d;
|
||||||
|
Loading…
Reference in New Issue
Block a user