From ede42d5bb352841e2e425972e12b8ef31ddf2123 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 11:08:04 +0200 Subject: [PATCH 01/16] Use std::unique_ptr for owning pointers --- source/http/client.cpp | 48 +++++++++----------------- source/http/client.h | 13 +++---- source/http/server.cpp | 64 +++++++++++++++-------------------- source/http/server.h | 10 +++--- source/net/clientsocket.cpp | 2 -- source/net/clientsocket.h | 2 +- source/net/datagramsocket.cpp | 6 ++-- source/net/protocol.cpp | 19 +++-------- source/net/protocol.h | 32 ++++++------------ source/net/resolve.cpp | 19 ++++------- source/net/resolve.h | 7 ++-- source/net/socket.cpp | 12 +++---- source/net/socket.h | 7 ++-- source/net/streamsocket.cpp | 11 ++---- 14 files changed, 95 insertions(+), 157 deletions(-) diff --git a/source/http/client.cpp b/source/http/client.cpp index 30c336e..e8790aa 100644 --- a/source/http/client.cpp +++ b/source/http/client.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include "client.h" @@ -13,9 +12,6 @@ namespace Http { Client::~Client() { - delete sock; - delete request; - delete response; } void Client::use_event_dispatcher(IO::EventDispatcher *ed) @@ -30,14 +26,11 @@ void Client::use_event_dispatcher(IO::EventDispatcher *ed) void Client::use_resolver(Net::Resolver *r) { if(resolver) - { - delete resolve_listener; - resolve_listener = nullptr; - } + resolve_listener.reset(); resolver = r; if(resolver) - resolve_listener = new ResolveListener(*this); + resolve_listener = make_unique(*this); } void Client::start_request(const Request &r) @@ -45,15 +38,13 @@ void Client::start_request(const Request &r) if(request) throw invalid_state("already processing a request"); - delete sock; - sock = nullptr; + sock.reset(); - request = new Request(r); + request = make_unique(r); if(!user_agent.empty()) request->set_header("User-Agent", user_agent); - delete response; - response = nullptr; + response.reset(); in_buf.clear(); string host = r.get_header("Host"); @@ -63,7 +54,7 @@ void Client::start_request(const Request &r) resolve_tag = resolver->resolve(host); else { - RefPtr addr = Net::resolve(host); + unique_ptr addr(Net::resolve(host)); address_resolved(resolve_tag, *addr); } } @@ -72,7 +63,7 @@ const Response *Client::get_url(const std::string &url) { start_request(Request::from_url(url)); wait_response(); - return response; + return response.get(); } void Client::tick() @@ -87,10 +78,8 @@ void Client::tick() { signal_response_complete.emit(*response); - delete sock; - sock = nullptr; - delete request; - request = nullptr; + sock.reset(); + request.reset(); } } @@ -102,10 +91,8 @@ void Client::wait_response() void Client::abort() { - delete sock; - sock = nullptr; - delete request; - request = nullptr; + sock.reset(); + request.reset(); } void Client::address_resolved(unsigned tag, const Net::SockAddr &addr) @@ -114,7 +101,7 @@ void Client::address_resolved(unsigned tag, const Net::SockAddr &addr) return; resolve_tag = 0; - sock = new Net::StreamSocket(addr.get_family()); + sock = make_unique(addr.get_family()); sock->set_block(false); sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available)); @@ -131,8 +118,7 @@ void Client::resolve_failed(unsigned tag, const exception &err) return; resolve_tag = 0; - delete request; - request = nullptr; + request.reset(); if(signal_socket_error.empty()) throw err; @@ -143,8 +129,7 @@ void Client::connect_finished(const exception *err) { if(err) { - delete request; - request = nullptr; + request.reset(); if(signal_socket_error.empty()) throw *err; @@ -190,7 +175,7 @@ void Client::data_available() { if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos) { - response = new Response(Response::parse(in_buf)); + response = make_unique(Response::parse(in_buf)); response->set_user_data(request->get_user_data()); in_buf = string(); } @@ -205,8 +190,7 @@ void Client::data_available() { signal_response_complete.emit(*response); - delete request; - request = nullptr; + request.reset(); } } diff --git a/source/http/client.h b/source/http/client.h index 2012498..d79389a 100644 --- a/source/http/client.h +++ b/source/http/client.h @@ -1,6 +1,7 @@ #ifndef MSP_HTTP_CLIENT_H_ #define MSP_HTTP_CLIENT_H_ +#include #include #include #include @@ -30,14 +31,14 @@ private: void resolve_failed(unsigned, const std::exception &); }; - Net::StreamSocket *sock = nullptr; + std::unique_ptr sock; IO::EventDispatcher *event_disp = nullptr; Net::Resolver *resolver = nullptr; - ResolveListener *resolve_listener = nullptr; + std::unique_ptr resolve_listener; unsigned resolve_tag = 0; std::string user_agent = "libmspnet/1.0"; - Request *request = nullptr; - Response *response = nullptr; + std::unique_ptr request; + std::unique_ptr response; std::string in_buf; Client(const Client &); @@ -53,8 +54,8 @@ public: void tick(); void wait_response(); void abort(); - const Request *get_request() const { return request; } - const Response *get_response() const { return response; } + const Request *get_request() const { return request.get(); } + const Response *get_response() const { return response.get(); } private: void address_resolved(unsigned, const Net::SockAddr &); void resolve_failed(unsigned, const std::exception &); diff --git a/source/http/server.cpp b/source/http/server.cpp index a7435d3..066fabe 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,7 @@ Server::~Server() void Server::listen(unsigned port) { - RefPtr addr = Net::resolve("*", format("%d", port), Net::INET6); + unique_ptr addr(Net::resolve("*", format("%d", port), Net::INET6)); sock.listen(*addr, 8); sock.signal_data_available.connect(sigc::mem_fun(this, &Server::data_available)); } @@ -105,12 +104,13 @@ void Server::close_connections(const Time::TimeDelta &timeout) void Server::data_available() { - Net::StreamSocket *csock = sock.accept(); - clients.push_back(Client(csock)); - csock->signal_data_available.connect(sigc::bind(sigc::mem_fun(this, &Server::client_data_available), sigc::ref(clients.back()))); - csock->signal_end_of_file.connect(sigc::bind(sigc::mem_fun(this, &Server::client_end_of_file), sigc::ref(clients.back()))); + unique_ptr csock(sock.accept()); + clients.emplace_back(move(csock)); + Client &cl = clients.back(); + cl.sock->signal_data_available.connect(sigc::bind(sigc::mem_fun(this, &Server::client_data_available), sigc::ref(clients.back()))); + cl.sock->signal_end_of_file.connect(sigc::bind(sigc::mem_fun(this, &Server::client_end_of_file), sigc::ref(clients.back()))); if(event_disp) - event_disp->add(*csock); + event_disp->add(*cl.sock); } void Server::client_data_available(Client &cl) @@ -136,14 +136,14 @@ void Server::client_data_available(Client &cl) return; } - RefPtr response; + unique_ptr response; if(!cl.request) { if(cl.in_buf.find("\r\n\r\n")!=string::npos || cl.in_buf.find("\n\n")!=string::npos) { try { - cl.request = new Request(Request::parse(cl.in_buf)); + cl.request = make_unique(Request::parse(cl.in_buf)); string addr_str = cl.sock->get_peer_address().str(); string::size_type colon = addr_str.find(':'); @@ -151,18 +151,18 @@ void Server::client_data_available(Client &cl) if(cl.request->get_method()!="GET" && cl.request->get_method()!="POST") { - response = new Response(NOT_IMPLEMENTED); + response = make_unique(NOT_IMPLEMENTED); response->add_content("Method not implemented\n"); } else if(cl.request->get_path()[0]!='/') { - response = new Response(BAD_REQUEST); + response = make_unique(BAD_REQUEST); response->add_content("Path must be absolute\n"); } } catch(const exception &e) { - response = new Response(BAD_REQUEST); + response = make_unique(BAD_REQUEST); response->add_content(format("An error occurred while parsing request headers:\ntype: %s\nwhat: %s", Debug::demangle(typeid(e).name()), e.what())); } @@ -181,30 +181,28 @@ void Server::client_data_available(Client &cl) if(cl.request->has_header("Connection")) cl.keepalive = !strcasecmp(cl.request->get_header("Connection"), "keep-alive"); - response = new Response(NONE); + response = make_unique(NONE); try { - cl.response = response.get(); - responses[cl.response] = &cl; - signal_request.emit(*cl.request, *response); - if(cl.async) - response.release(); - else + cl.response = move(response); + responses[cl.response.get()] = &cl; + signal_request.emit(*cl.request, *cl.response); + if(!cl.async) { - responses.erase(cl.response); - cl.response = nullptr; + responses.erase(cl.response.get()); + response = move(cl.response); if(response->get_status()==NONE) { - response = new Response(NOT_FOUND); + response = make_unique(NOT_FOUND); response->add_content("The requested resource was not found\n"); } } } catch(const exception &e) { - responses.erase(cl.response); - cl.response = nullptr; - response = new Response(INTERNAL_ERROR); + responses.erase(cl.response.get()); + cl.response.reset(); + response = make_unique(INTERNAL_ERROR); response->add_content(format("An error occurred while processing the request:\ntype: %s\nwhat: %s", Debug::demangle(typeid(e).name()), e.what())); } @@ -232,10 +230,8 @@ void Server::send_response(Client &cl, Response &resp) cl.async = false; if(cl.keepalive) { - delete cl.request; - cl.request = nullptr; - delete cl.response; - cl.response = nullptr; + cl.request.reset(); + cl.response.reset(); } else { @@ -255,15 +251,9 @@ Server::Client &Server::get_client_by_response(Response &resp) } -Server::Client::Client(RefPtr s): - sock(s) +Server::Client::Client(unique_ptr s): + sock(move(s)) { } -Server::Client::~Client() -{ - delete request; - delete response; -} - } // namespace Http } // namespace Msp diff --git a/source/http/server.h b/source/http/server.h index e6de227..dd8b2c6 100644 --- a/source/http/server.h +++ b/source/http/server.h @@ -1,7 +1,6 @@ #ifndef MSP_HTTP_SERVER_H_ #define MSP_HTTP_SERVER_H_ -#include #include #include #include @@ -20,16 +19,15 @@ public: private: struct Client { - RefPtr sock; + std::unique_ptr sock; std::string in_buf; - Request *request = nullptr; - Response *response = nullptr; + std::unique_ptr request; + std::unique_ptr response; bool keepalive = false; bool async = false; bool stale = false; - Client(RefPtr); - ~Client(); + Client(std::unique_ptr); }; Net::StreamServerSocket sock; diff --git a/source/net/clientsocket.cpp b/source/net/clientsocket.cpp index 89e2d08..e6e10e9 100644 --- a/source/net/clientsocket.cpp +++ b/source/net/clientsocket.cpp @@ -19,8 +19,6 @@ ClientSocket::ClientSocket(const Private &p, const SockAddr &paddr): ClientSocket::~ClientSocket() { signal_flush_required.emit(); - - delete peer_addr; } void ClientSocket::shutdown(IO::Mode m) diff --git a/source/net/clientsocket.h b/source/net/clientsocket.h index 2359fd3..c33ed93 100644 --- a/source/net/clientsocket.h +++ b/source/net/clientsocket.h @@ -18,7 +18,7 @@ public: protected: bool connecting = false; bool connected = false; - SockAddr *peer_addr = nullptr; + std::unique_ptr peer_addr; ClientSocket(const Private &, const SockAddr &); ClientSocket(Family, int, int); diff --git a/source/net/datagramsocket.cpp b/source/net/datagramsocket.cpp index e92e286..7ee057f 100644 --- a/source/net/datagramsocket.cpp +++ b/source/net/datagramsocket.cpp @@ -20,13 +20,11 @@ bool DatagramSocket::connect(const SockAddr &addr) SockAddr::SysAddr sa = addr.to_sys(); check_sys_connect_error(::connect(priv->handle, reinterpret_cast(&sa.addr), sa.size)); - delete peer_addr; - peer_addr = addr.copy(); + peer_addr.reset(addr.copy()); - delete local_addr; SockAddr::SysAddr lsa; getsockname(priv->handle, reinterpret_cast(&lsa.addr), &lsa.size); - local_addr = SockAddr::new_from_sys(lsa); + local_addr.reset(SockAddr::new_from_sys(lsa)); connected = true; diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index ce200a2..402b84a 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -18,29 +18,20 @@ Protocol::Protocol(unsigned npi): .fields(&PacketHeader::type, &PacketHeader::length); } -Protocol::~Protocol() -{ - for(auto &kvp: packet_class_defs) - delete kvp.second; -} - unsigned Protocol::get_next_packet_class_id() { static unsigned next_id = 1; return next_id++; } -void Protocol::add_packet(PacketDefBase *pdef) +void Protocol::add_packet(unique_ptr pdef) { - PacketDefBase *&ptr = packet_class_defs[pdef->get_class_id()]; + unique_ptr &ptr = packet_class_defs[pdef->get_class_id()]; if(ptr) - { packet_id_defs.erase(ptr->get_id()); - delete ptr; - } - ptr = pdef; - if(unsigned id = pdef->get_id()) - packet_id_defs[id] = pdef; + ptr = move(pdef); + if(unsigned id = ptr->get_id()) + packet_id_defs[id] = ptr.get(); } const Protocol::PacketDefBase &Protocol::get_packet_by_class_id(unsigned id) const diff --git a/source/net/protocol.h b/source/net/protocol.h index 35d3cb2..f040a69 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -142,11 +143,10 @@ private: class PacketTypeDef: public PacketDefBase { private: - Serializer

*serializer; + std::unique_ptr> serializer; public: PacketTypeDef(unsigned); - ~PacketTypeDef(); unsigned get_class_id() const override { return get_packet_class_id

(); } @@ -188,17 +188,13 @@ private: PacketHeader(std::uint16_t, std::uint16_t); }; - typedef std::map PacketMap; - PacketTypeDef header_def; unsigned next_packet_id; - PacketMap packet_class_defs; - PacketMap packet_id_defs; + std::map> packet_class_defs; + std::map packet_id_defs; protected: Protocol(unsigned = 1); -public: - ~Protocol(); private: static unsigned get_next_packet_class_id(); @@ -206,7 +202,7 @@ private: template static unsigned get_packet_class_id(); - void add_packet(PacketDefBase *); + void add_packet(std::unique_ptr); protected: template @@ -248,9 +244,10 @@ unsigned Protocol::get_packet_class_id() template Protocol::PacketDefBuilder> Protocol::add(unsigned id) { - PacketTypeDef

*pdef = new PacketTypeDef

(id); - add_packet(pdef); - return PacketDefBuilder>(*this, *pdef, Serializer

()); + std::unique_ptr> pdef = std::make_unique>(id); + PacketDefBuilder> next(*this, *pdef, Serializer

()); + add_packet(move(pdef)); + return next; } template @@ -413,21 +410,14 @@ const char *Protocol::FieldSerializer::deserialize(C &com, const cha template Protocol::PacketTypeDef

::PacketTypeDef(unsigned i): PacketDefBase(i), - serializer(new Serializer

) + serializer(std::make_unique>()) { } -template -Protocol::PacketTypeDef

::~PacketTypeDef() -{ - delete serializer; -} - template template void Protocol::PacketTypeDef

::set_serializer(const S &ser) { - delete serializer; - serializer = new S(ser); + serializer = std::make_unique(ser); } template diff --git a/source/net/resolve.cpp b/source/net/resolve.cpp index e06bce7..078d6ef 100644 --- a/source/net/resolve.cpp +++ b/source/net/resolve.cpp @@ -102,7 +102,7 @@ unsigned Resolver::resolve(const string &host, const string &serv, Family family task.host = host; task.serv = serv; task.family = family; - thread.add_task(task); + thread.add_task(move(task)); return task.tag; } @@ -133,8 +133,7 @@ void Resolver::task_done() { if(signal_resolve_failed.empty()) { - RefPtr err = task->error; - task->error = nullptr; + unique_ptr err = move(task->error); thread.pop_complete_task(); throw *err; } @@ -159,11 +158,11 @@ Resolver::WorkerThread::~WorkerThread() join(); } -void Resolver::WorkerThread::add_task(const Task &t) +void Resolver::WorkerThread::add_task(Task &&t) { MutexLock lock(queue_mutex); bool was_starved = (queue.empty() || queue.back().is_complete()); - queue.push_back(t); + queue.push_back(move(t)); if(was_starved) sem.signal(); } @@ -181,11 +180,7 @@ void Resolver::WorkerThread::pop_complete_task() { MutexLock lock(queue_mutex); if(!queue.empty() && queue.front().is_complete()) - { - delete queue.front().addr; - delete queue.front().error; queue.pop_front(); - } } void Resolver::WorkerThread::main() @@ -209,16 +204,16 @@ void Resolver::WorkerThread::main() { try { - SockAddr *addr = Net::resolve(task->host, task->serv, task->family); + unique_ptr addr(Net::resolve(task->host, task->serv, task->family)); { MutexLock lock(queue_mutex); - task->addr = addr; + task->addr = move(addr); } } catch(const runtime_error &e) { MutexLock lock(queue_mutex); - task->error = new runtime_error(e); + task->error = make_unique(e); } notify_pipe.put(1); } diff --git a/source/net/resolve.h b/source/net/resolve.h index 65d1db8..ae0411b 100644 --- a/source/net/resolve.h +++ b/source/net/resolve.h @@ -2,6 +2,7 @@ #define MSP_NET_RESOLVE_H_ #include +#include #include #include #include @@ -40,8 +41,8 @@ private: std::string host; std::string serv; Family family = UNSPEC; - SockAddr *addr = nullptr; - std::runtime_error *error = nullptr; + std::unique_ptr addr; + std::unique_ptr error; bool is_complete() const { return addr || error; } }; @@ -59,7 +60,7 @@ private: WorkerThread(); ~WorkerThread(); - void add_task(const Task &); + void add_task(Task &&); Task *get_complete_task(); void pop_complete_task(); diff --git a/source/net/socket.cpp b/source/net/socket.cpp index 67bd381..877c1da 100644 --- a/source/net/socket.cpp +++ b/source/net/socket.cpp @@ -11,7 +11,7 @@ namespace Msp { namespace Net { Socket::Socket(const Private &p): - priv(new Private) + priv(make_unique()) { mode = IO::M_RDWR; @@ -19,13 +19,13 @@ Socket::Socket(const Private &p): SockAddr::SysAddr sa; getsockname(priv->handle, reinterpret_cast(&sa.addr), &sa.size); - local_addr = SockAddr::new_from_sys(sa); + local_addr.reset(SockAddr::new_from_sys(sa)); platform_init(); } Socket::Socket(Family af, int type, int proto): - priv(new Private) + priv(make_unique()) { mode = IO::M_RDWR; @@ -38,9 +38,6 @@ Socket::Socket(Family af, int type, int proto): Socket::~Socket() { platform_cleanup(); - - delete local_addr; - delete priv; } void Socket::set_block(bool b) @@ -74,8 +71,7 @@ void Socket::bind(const SockAddr &addr) if(err==-1) throw system_error("bind"); - delete local_addr; - local_addr = addr.copy(); + local_addr.reset(addr.copy()); } const SockAddr &Socket::get_local_address() const diff --git a/source/net/socket.h b/source/net/socket.h index c55a539..7a47793 100644 --- a/source/net/socket.h +++ b/source/net/socket.h @@ -1,6 +1,7 @@ #ifndef MSP_NET_SOCKET_H_ #define MSP_NET_SOCKET_H_ +#include #include #include #include @@ -30,8 +31,8 @@ protected: struct Private; - Private *priv = nullptr; - SockAddr *local_addr = nullptr; + std::unique_ptr priv; + std::unique_ptr local_addr; Socket(const Private &); Socket(Family, int, int); @@ -50,7 +51,7 @@ public: users of the address. */ void bind(const SockAddr &); - bool is_bound() const { return local_addr; } + bool is_bound() const { return static_cast(local_addr); } const SockAddr &get_local_address() const; void set_timeout(const Time::TimeDelta &); diff --git a/source/net/streamsocket.cpp b/source/net/streamsocket.cpp index c8eb1c4..02a7b54 100644 --- a/source/net/streamsocket.cpp +++ b/source/net/streamsocket.cpp @@ -34,13 +34,11 @@ bool StreamSocket::connect(const SockAddr &addr) set_socket_events(S_CONNECT); } - delete peer_addr; - peer_addr = addr.copy(); + peer_addr.reset(addr.copy()); - delete local_addr; SockAddr::SysAddr lsa; getsockname(priv->handle, reinterpret_cast(&lsa.addr), &lsa.size); - local_addr = SockAddr::new_from_sys(lsa); + local_addr.reset(SockAddr::new_from_sys(lsa)); if(finished) { @@ -99,10 +97,7 @@ void StreamSocket::on_event(IO::PollEvent ev) signal_connect_finished.emit(0); if(err!=0) - { - delete peer_addr; - peer_addr = nullptr; - } + peer_addr.reset(); set_socket_events((err==0) ? S_INPUT : S_NONE); } -- 2.43.0 From cc2e643d6ec3025db3fb6f0a4c1f77af05cf1026 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 11:39:07 +0200 Subject: [PATCH 02/16] Include the matching header first in .cpp files This avoids accidentally using transitively included headers. --- source/http/client.cpp | 2 +- source/http/formdata.cpp | 2 +- source/http/header.cpp | 2 +- source/http/message.cpp | 2 +- source/http/request.cpp | 2 +- source/http/response.cpp | 2 +- source/http/server.cpp | 2 +- source/http/utils.cpp | 2 +- source/http/version.cpp | 2 +- source/net/clientsocket.cpp | 2 +- source/net/communicator.cpp | 2 +- source/net/constants.cpp | 2 +- source/net/datagramsocket.cpp | 2 +- source/net/inet.cpp | 2 +- source/net/inet6.cpp | 2 +- source/net/protocol.cpp | 2 +- source/net/resolve.cpp | 4 ++-- source/net/serversocket.cpp | 2 +- source/net/sockaddr.cpp | 1 + source/net/socket.cpp | 2 +- source/net/streamserversocket.cpp | 2 +- source/net/streamsocket.cpp | 2 +- source/net/unix/socket.cpp | 4 ++-- source/net/unix/unix.cpp | 4 ++-- source/net/windows/socket.cpp | 4 ++-- source/net/windows/unix.cpp | 4 ++-- 26 files changed, 31 insertions(+), 30 deletions(-) diff --git a/source/http/client.cpp b/source/http/client.cpp index e8790aa..1c4c953 100644 --- a/source/http/client.cpp +++ b/source/http/client.cpp @@ -1,7 +1,7 @@ +#include "client.h" #include #include #include -#include "client.h" #include "request.h" #include "response.h" diff --git a/source/http/formdata.cpp b/source/http/formdata.cpp index 4ec3f88..4f2b7b3 100644 --- a/source/http/formdata.cpp +++ b/source/http/formdata.cpp @@ -1,9 +1,9 @@ +#include "formdata.h" #include #include "header.h" #include "request.h" #include "submessage.h" #include "utils.h" -#include "formdata.h" using namespace std; diff --git a/source/http/header.cpp b/source/http/header.cpp index b55223e..a112e91 100644 --- a/source/http/header.cpp +++ b/source/http/header.cpp @@ -1,6 +1,6 @@ +#include "header.h" #include #include -#include "header.h" #include "message.h" using namespace std; diff --git a/source/http/message.cpp b/source/http/message.cpp index 4f17017..a6ab465 100644 --- a/source/http/message.cpp +++ b/source/http/message.cpp @@ -1,8 +1,8 @@ +#include "message.h" #include #include #include #include -#include "message.h" using namespace std; diff --git a/source/http/request.cpp b/source/http/request.cpp index 15056d6..437b8ba 100644 --- a/source/http/request.cpp +++ b/source/http/request.cpp @@ -1,7 +1,7 @@ +#include "request.h" #include #include #include -#include "request.h" #include "utils.h" using namespace std; diff --git a/source/http/response.cpp b/source/http/response.cpp index 6fa4cc3..6005d35 100644 --- a/source/http/response.cpp +++ b/source/http/response.cpp @@ -1,6 +1,6 @@ +#include "response.h" #include #include -#include "response.h" using namespace std; diff --git a/source/http/server.cpp b/source/http/server.cpp index 066fabe..9c9a876 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -1,3 +1,4 @@ +#include "server.h" #include #include #include @@ -9,7 +10,6 @@ #include #include "request.h" #include "response.h" -#include "server.h" using namespace std; diff --git a/source/http/utils.cpp b/source/http/utils.cpp index 8662c2e..8aa966c 100644 --- a/source/http/utils.cpp +++ b/source/http/utils.cpp @@ -1,8 +1,8 @@ +#include "utils.h" #include #include #include #include -#include "utils.h" using namespace std; diff --git a/source/http/version.cpp b/source/http/version.cpp index c9fd1c9..d6f846c 100644 --- a/source/http/version.cpp +++ b/source/http/version.cpp @@ -1,7 +1,7 @@ +#include "version.h" #include #include #include -#include "version.h" using namespace std; diff --git a/source/net/clientsocket.cpp b/source/net/clientsocket.cpp index e6e10e9..fbbd241 100644 --- a/source/net/clientsocket.cpp +++ b/source/net/clientsocket.cpp @@ -1,6 +1,6 @@ #include "platform_api.h" -#include #include "clientsocket.h" +#include #include "socket_private.h" namespace Msp { diff --git a/source/net/communicator.cpp b/source/net/communicator.cpp index 3ac62d7..51202b5 100644 --- a/source/net/communicator.cpp +++ b/source/net/communicator.cpp @@ -1,5 +1,5 @@ -#include #include "communicator.h" +#include #include "streamsocket.h" using namespace std; diff --git a/source/net/constants.cpp b/source/net/constants.cpp index 5088e51..4de22b4 100644 --- a/source/net/constants.cpp +++ b/source/net/constants.cpp @@ -1,6 +1,6 @@ -#include #include "platform_api.h" #include "constants.h" +#include using namespace std; diff --git a/source/net/datagramsocket.cpp b/source/net/datagramsocket.cpp index 7ee057f..5493554 100644 --- a/source/net/datagramsocket.cpp +++ b/source/net/datagramsocket.cpp @@ -1,8 +1,8 @@ #include "platform_api.h" +#include "datagramsocket.h" #include #include #include -#include "datagramsocket.h" #include "sockaddr_private.h" #include "socket_private.h" diff --git a/source/net/inet.cpp b/source/net/inet.cpp index dbbd12d..f44b1c0 100644 --- a/source/net/inet.cpp +++ b/source/net/inet.cpp @@ -1,6 +1,6 @@ #include "platform_api.h" -#include #include "inet.h" +#include #include "sockaddr_private.h" using namespace std; diff --git a/source/net/inet6.cpp b/source/net/inet6.cpp index 8ddf1d5..7580058 100644 --- a/source/net/inet6.cpp +++ b/source/net/inet6.cpp @@ -1,6 +1,6 @@ +#include "inet6.h" #include "platform_api.h" #include -#include "inet6.h" #include "sockaddr_private.h" using namespace std; diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index 402b84a..63cc6f1 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -1,9 +1,9 @@ +#include "protocol.h" #include #include #include #include #include -#include "protocol.h" using namespace std; diff --git a/source/net/resolve.cpp b/source/net/resolve.cpp index 078d6ef..9ec5395 100644 --- a/source/net/resolve.cpp +++ b/source/net/resolve.cpp @@ -1,9 +1,9 @@ -#include "platform_api.h" +#include "resolve.h" #include #include +#include "platform_api.h" #include "sockaddr_private.h" #include "socket.h" -#include "resolve.h" using namespace std; diff --git a/source/net/serversocket.cpp b/source/net/serversocket.cpp index 01fa445..66876c5 100644 --- a/source/net/serversocket.cpp +++ b/source/net/serversocket.cpp @@ -1,5 +1,5 @@ -#include #include "serversocket.h" +#include using namespace std; diff --git a/source/net/sockaddr.cpp b/source/net/sockaddr.cpp index 36e9c68..6ac9189 100644 --- a/source/net/sockaddr.cpp +++ b/source/net/sockaddr.cpp @@ -1,3 +1,4 @@ +#include "sockaddr.h" #include #include "platform_api.h" #include "inet.h" diff --git a/source/net/socket.cpp b/source/net/socket.cpp index 877c1da..84db637 100644 --- a/source/net/socket.cpp +++ b/source/net/socket.cpp @@ -1,8 +1,8 @@ #include "platform_api.h" +#include "socket.h" #include #include #include "sockaddr_private.h" -#include "socket.h" #include "socket_private.h" using namespace std; diff --git a/source/net/streamserversocket.cpp b/source/net/streamserversocket.cpp index b91491d..8e655fc 100644 --- a/source/net/streamserversocket.cpp +++ b/source/net/streamserversocket.cpp @@ -1,11 +1,11 @@ #include "platform_api.h" +#include "streamserversocket.h" #include #include #include #include #include "sockaddr_private.h" #include "socket_private.h" -#include "streamserversocket.h" #include "streamsocket.h" using namespace std; diff --git a/source/net/streamsocket.cpp b/source/net/streamsocket.cpp index 02a7b54..9e2d79a 100644 --- a/source/net/streamsocket.cpp +++ b/source/net/streamsocket.cpp @@ -1,11 +1,11 @@ #include "platform_api.h" +#include "streamsocket.h" #include #include #include #include #include "sockaddr_private.h" #include "socket_private.h" -#include "streamsocket.h" namespace Msp { namespace Net { diff --git a/source/net/unix/socket.cpp b/source/net/unix/socket.cpp index b8cc580..d87cdd3 100644 --- a/source/net/unix/socket.cpp +++ b/source/net/unix/socket.cpp @@ -1,12 +1,12 @@ +#include "platform_api.h" +#include "socket.h" #include #include #include -#include "platform_api.h" #include #include #include #include "sockaddr_private.h" -#include "socket.h" #include "socket_private.h" using namespace std; diff --git a/source/net/unix/unix.cpp b/source/net/unix/unix.cpp index eba7fb6..648d53a 100644 --- a/source/net/unix/unix.cpp +++ b/source/net/unix/unix.cpp @@ -1,8 +1,8 @@ +#include "platform_api.h" +#include "unix.h" #include #include -#include "platform_api.h" #include "sockaddr_private.h" -#include "unix.h" using namespace std; diff --git a/source/net/windows/socket.cpp b/source/net/windows/socket.cpp index dfcff62..07db95d 100644 --- a/source/net/windows/socket.cpp +++ b/source/net/windows/socket.cpp @@ -1,9 +1,9 @@ -#include #include "platform_api.h" +#include "socket.h" +#include #include #include #include "sockaddr_private.h" -#include "socket.h" #include "socket_private.h" using namespace std; diff --git a/source/net/windows/unix.cpp b/source/net/windows/unix.cpp index 6a56ee0..b38a258 100644 --- a/source/net/windows/unix.cpp +++ b/source/net/windows/unix.cpp @@ -1,7 +1,7 @@ -#include #include "platform_api.h" -#include "sockaddr_private.h" #include "unix.h" +#include +#include "sockaddr_private.h" using namespace std; -- 2.43.0 From 593c8202bf8c6ecc563aaacf0811aef953b45f09 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 11:43:25 +0200 Subject: [PATCH 03/16] Move the address family enum into sockaddr.h --- source/net/constants.cpp | 35 ----------------------------------- source/net/constants.h | 17 ++--------------- source/net/resolve.h | 4 +--- source/net/sockaddr.cpp | 25 +++++++++++++++++++++++++ source/net/sockaddr.h | 10 +++++++++- source/net/sockaddr_private.h | 4 ++++ source/net/socket.h | 1 - 7 files changed, 41 insertions(+), 55 deletions(-) delete mode 100644 source/net/constants.cpp diff --git a/source/net/constants.cpp b/source/net/constants.cpp deleted file mode 100644 index 4de22b4..0000000 --- a/source/net/constants.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "platform_api.h" -#include "constants.h" -#include - -using namespace std; - -namespace Msp { -namespace Net { - -int family_to_sys(Family f) -{ - switch(f) - { - case UNSPEC: return AF_UNSPEC; - case INET: return AF_INET; - case INET6: return AF_INET6; - case UNIX: return AF_UNIX; - default: throw invalid_argument("family_to_sys"); - } -} - -Family family_from_sys(int f) -{ - switch(f) - { - case AF_UNSPEC: return UNSPEC; - case AF_INET: return INET; - case AF_INET6: return INET6; - case AF_UNIX: return UNIX; - default: throw invalid_argument("family_from_sys"); - } -} - -} // namespace Net -} // namespace Msp diff --git a/source/net/constants.h b/source/net/constants.h index 0d94661..2e6d544 100644 --- a/source/net/constants.h +++ b/source/net/constants.h @@ -1,21 +1,8 @@ #ifndef MSP_NET_CONSTANTS_H_ #define MSP_NET_CONSTANTS_H_ -namespace Msp { -namespace Net { +#warning "This header is deprected and should not be used" -enum Family -{ - UNSPEC, - INET, - INET6, - UNIX -}; - -int family_to_sys(Family); -Family family_from_sys(int); - -} // namespace Net -} // namespace Msp +#include "sockaddr.h" #endif diff --git a/source/net/resolve.h b/source/net/resolve.h index ae0411b..212a7f0 100644 --- a/source/net/resolve.h +++ b/source/net/resolve.h @@ -9,13 +9,11 @@ #include #include #include -#include "constants.h" +#include "sockaddr.h" namespace Msp { namespace Net { -class SockAddr; - /** Resolves host and service names into a socket address. If host is empty, the loopback address will be used. If host is "*", the wildcard address will be used. If service is empty, a socket address with a null service will be diff --git a/source/net/sockaddr.cpp b/source/net/sockaddr.cpp index 6ac9189..a3d3eed 100644 --- a/source/net/sockaddr.cpp +++ b/source/net/sockaddr.cpp @@ -31,5 +31,30 @@ SockAddr::SysAddr::SysAddr() addr.ss_family = AF_UNSPEC; } + +int family_to_sys(Family f) +{ + switch(f) + { + case UNSPEC: return AF_UNSPEC; + case INET: return AF_INET; + case INET6: return AF_INET6; + case UNIX: return AF_UNIX; + default: throw invalid_argument("family_to_sys"); + } +} + +Family family_from_sys(int f) +{ + switch(f) + { + case AF_UNSPEC: return UNSPEC; + case AF_INET: return INET; + case AF_INET6: return INET6; + case AF_UNIX: return UNIX; + default: throw invalid_argument("family_from_sys"); + } +} + } // namespace Net } // namespace Msp diff --git a/source/net/sockaddr.h b/source/net/sockaddr.h index 9db8b18..f2f9695 100644 --- a/source/net/sockaddr.h +++ b/source/net/sockaddr.h @@ -2,11 +2,19 @@ #define MSP_NET_SOCKADDR_H_ #include -#include "constants.h" namespace Msp { namespace Net { +enum Family +{ + UNSPEC, + INET, + INET6, + UNIX +}; + + class SockAddr { public: diff --git a/source/net/sockaddr_private.h b/source/net/sockaddr_private.h index 5fca410..2a3d14b 100644 --- a/source/net/sockaddr_private.h +++ b/source/net/sockaddr_private.h @@ -19,6 +19,10 @@ struct SockAddr::SysAddr SysAddr(); }; + +int family_to_sys(Family); +Family family_from_sys(int); + } // namespace Net } // namespace Msp diff --git a/source/net/socket.h b/source/net/socket.h index 7a47793..3e3c2ae 100644 --- a/source/net/socket.h +++ b/source/net/socket.h @@ -5,7 +5,6 @@ #include #include #include -#include "constants.h" #include "sockaddr.h" namespace Msp { -- 2.43.0 From 63432878fc84bc666bb74efaeee588fa6d03d5b2 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 12:17:12 +0200 Subject: [PATCH 04/16] Check for EOF return before checking for errors The check_sys_error function converts errors from non-blocking operations which would block into a zero return, making them indistinguishable from a zero return due to the other end closing the connection. --- source/net/clientsocket.cpp | 17 +++++++++++------ source/net/unix/socket.cpp | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/source/net/clientsocket.cpp b/source/net/clientsocket.cpp index fbbd241..7987f4b 100644 --- a/source/net/clientsocket.cpp +++ b/source/net/clientsocket.cpp @@ -3,6 +3,8 @@ #include #include "socket_private.h" +using namespace std; + namespace Msp { namespace Net { @@ -75,15 +77,18 @@ size_t ClientSocket::do_read(char *buf, size_t size) if(size==0) return 0; - size_t ret = check_sys_error(::recv(priv->handle, buf, size, 0), "recv"); - if(ret==0 && !eof_flag) + make_signed::type ret = ::recv(priv->handle, buf, size, 0); + if(ret==0) { - eof_flag = true; - set_socket_events(S_NONE); - signal_end_of_file.emit(); + if(!eof_flag) + { + set_socket_events(S_NONE); + set_eof(); + } + return 0; } - return ret; + return check_sys_error(ret, "recv"); } } // namespace Net diff --git a/source/net/unix/socket.cpp b/source/net/unix/socket.cpp index d87cdd3..2c1e2ad 100644 --- a/source/net/unix/socket.cpp +++ b/source/net/unix/socket.cpp @@ -64,7 +64,7 @@ size_t check_sys_error(make_signed::type ret, const char *func) { if(ret<0) { - if(errno==EAGAIN) + if(errno==EAGAIN || errno==EWOULDBLOCK) return 0; else throw system_error(func); -- 2.43.0 From 6057616e00a6792e219fab2ce958306e737faa67 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 13:14:57 +0200 Subject: [PATCH 05/16] Don't create the WinSock helper until the first socket is created --- source/net/windows/socket.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/net/windows/socket.cpp b/source/net/windows/socket.cpp index 07db95d..eadc22a 100644 --- a/source/net/windows/socket.cpp +++ b/source/net/windows/socket.cpp @@ -27,7 +27,7 @@ public: } }; -WinSockHelper wsh; +unique_ptr wsh; } @@ -37,6 +37,8 @@ namespace Net { void Socket::platform_init() { + if(!wsh) + wsh = make_unique(); *priv->event = CreateEvent(0, false, false, 0); } -- 2.43.0 From afd2a8365ccc49b1b69ca034dd4abae37ee83eed Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 13:54:17 +0200 Subject: [PATCH 06/16] Adjust copyability of classes In most cases the use of std::unique_ptr already makes classes move-only where necessary. --- source/http/client.h | 2 -- source/net/communicator.h | 3 ++- source/net/sockaddr.h | 4 ++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/source/http/client.h b/source/http/client.h index d79389a..cd53f61 100644 --- a/source/http/client.h +++ b/source/http/client.h @@ -41,8 +41,6 @@ private: std::unique_ptr response; std::string in_buf; - Client(const Client &); - Client &operator=(const Client &); public: ~Client(); diff --git a/source/net/communicator.h b/source/net/communicator.h index 822b16e..b98f488 100644 --- a/source/net/communicator.h +++ b/source/net/communicator.h @@ -2,6 +2,7 @@ #define MSP_NET_COMMUNICATOR_H_ #include +#include #include #include "protocol.h" @@ -23,7 +24,7 @@ public: }; -class Communicator +class Communicator: public NonCopyable { public: sigc::signal signal_handshake_done; diff --git a/source/net/sockaddr.h b/source/net/sockaddr.h index f2f9695..817de4d 100644 --- a/source/net/sockaddr.h +++ b/source/net/sockaddr.h @@ -22,6 +22,10 @@ public: protected: SockAddr() = default; + SockAddr(const SockAddr &) = default; + SockAddr(SockAddr &&) = default; + SockAddr &operator=(const SockAddr &) = default; + SockAddr &operator=(SockAddr &&) = default; public: virtual ~SockAddr() = default; -- 2.43.0 From 1ccb251524403a7318908e076c4f805bd3927247 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 14:06:36 +0200 Subject: [PATCH 07/16] Use the append function from string utilities --- source/http/request.cpp | 6 +----- source/http/utils.cpp | 16 +++------------- source/net/inet6.cpp | 3 ++- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/source/http/request.cpp b/source/http/request.cpp index 437b8ba..685de33 100644 --- a/source/http/request.cpp +++ b/source/http/request.cpp @@ -53,11 +53,7 @@ Request Request::from_url(const string &str) string path = urlencode(url.path); if(path.empty()) path = "/"; - if(!url.query.empty()) - { - path += '?'; - path += url.query; - } + append(path, "?", url.query); Request result("GET", path); result.set_header("Host", url.host); diff --git a/source/http/utils.cpp b/source/http/utils.cpp index 8aa966c..7e1037e 100644 --- a/source/http/utils.cpp +++ b/source/http/utils.cpp @@ -106,16 +106,8 @@ string build_url(const Url &url) str += url.scheme+"://"; str += url.host; str += urlencode(url.path); - if(!url.query.empty()) - { - str += '?'; - str += url.query; - } - if(!url.fragment.empty()) - { - str += '#'; - str += url.fragment; - } + append(str, "?", url.query); + append(str, "#", url.fragment); return str; } @@ -137,9 +129,7 @@ string build_query(const Query &query) string str; for(const auto &kvp: query) { - if(!str.empty()) - str += '&'; - str += urlencode_plus(kvp.first); + append(str, "&", urlencode_plus(kvp.first)); str += '='; str += urlencode_plus(kvp.second); } diff --git a/source/net/inet6.cpp b/source/net/inet6.cpp index 7580058..da584d4 100644 --- a/source/net/inet6.cpp +++ b/source/net/inet6.cpp @@ -1,6 +1,7 @@ #include "inet6.h" #include "platform_api.h" #include +#include #include "sockaddr_private.h" using namespace std; @@ -34,9 +35,9 @@ string Inet6Addr::str() const string result = "["; for(unsigned i=0; i<16; i+=2) { - unsigned short part = (addr[i]<<8) | addr[i+1]; if(i>0) result += ':'; + unsigned short part = (addr[i]<<8) | addr[i+1]; result += format("%x", part); } result += ']'; -- 2.43.0 From 9d41b029e940bacf17a3c8d7cc06162f0a41b70a Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 17:59:16 +0200 Subject: [PATCH 08/16] Mark sockets as close-on-exec upon creation on Linux --- source/net/socket.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/net/socket.cpp b/source/net/socket.cpp index 84db637..2a1a71c 100644 --- a/source/net/socket.cpp +++ b/source/net/socket.cpp @@ -29,8 +29,13 @@ Socket::Socket(Family af, int type, int proto): { mode = IO::M_RDWR; - // TODO use SOCK_CLOEXEC on Linux +#ifdef __linux__ + type |= SOCK_CLOEXEC; +#endif priv->handle = socket(family_to_sys(af), type, proto); +#ifndef __linux__ + set_inherit(false); +#endif platform_init(); } -- 2.43.0 From 0cd8309240cd48fdc16d9cc433297e33a0a2cdd0 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 11 Dec 2022 18:26:02 +0200 Subject: [PATCH 09/16] Fix processing of IPv6 addresses in HTTP server --- source/http/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/http/server.cpp b/source/http/server.cpp index 9c9a876..04bae0b 100644 --- a/source/http/server.cpp +++ b/source/http/server.cpp @@ -146,7 +146,7 @@ void Server::client_data_available(Client &cl) cl.request = make_unique(Request::parse(cl.in_buf)); string addr_str = cl.sock->get_peer_address().str(); - string::size_type colon = addr_str.find(':'); + string::size_type colon = addr_str.find(':', (addr_str[0]=='[' ? addr_str.find(']')+1 : 0)); cl.request->set_header("-Client-Host", addr_str.substr(0, colon)); if(cl.request->get_method()!="GET" && cl.request->get_method()!="POST") -- 2.43.0 From 394c9a732192fce9b3b453dfdb9e92400af2a4f8 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 00:30:50 +0200 Subject: [PATCH 10/16] Decorate things which constitute the public API --- source/http/client.h | 3 ++- source/http/formdata.h | 3 ++- source/http/header.h | 3 ++- source/http/message.h | 3 ++- source/http/request.h | 3 ++- source/http/response.h | 3 ++- source/http/server.h | 3 ++- source/http/status.h | 3 ++- source/http/utils.h | 15 ++++++++------- source/http/version.h | 5 +++-- source/net/clientsocket.cpp | 1 + source/net/clientsocket.h | 3 ++- source/net/communicator.h | 7 ++++--- source/net/datagramsocket.h | 3 ++- source/net/inet.h | 3 ++- source/net/inet6.h | 3 ++- source/net/mspnet_api.h | 18 ++++++++++++++++++ source/net/protocol.h | 7 ++++--- source/net/receiver.h | 4 +++- source/net/resolve.h | 7 ++++--- source/net/serversocket.h | 3 ++- source/net/sockaddr.h | 3 ++- source/net/socket.h | 5 +++-- source/net/streamserversocket.h | 3 ++- source/net/streamsocket.h | 3 ++- source/net/unix.h | 3 ++- 26 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 source/net/mspnet_api.h diff --git a/source/http/client.h b/source/http/client.h index cd53f61..b1f097d 100644 --- a/source/http/client.h +++ b/source/http/client.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -14,7 +15,7 @@ namespace Http { class Request; class Response; -class Client +class MSPNET_API Client { public: sigc::signal signal_response_complete; diff --git a/source/http/formdata.h b/source/http/formdata.h index 3232891..cf78acd 100644 --- a/source/http/formdata.h +++ b/source/http/formdata.h @@ -3,13 +3,14 @@ #include #include +#include namespace Msp { namespace Http { class Request; -class FormData +class MSPNET_API FormData { private: std::map fields; diff --git a/source/http/header.h b/source/http/header.h index 0c6324b..1beb95f 100644 --- a/source/http/header.h +++ b/source/http/header.h @@ -4,13 +4,14 @@ #include #include #include +#include namespace Msp { namespace Http { class Message; -struct Header +struct MSPNET_API Header { enum Style { diff --git a/source/http/message.h b/source/http/message.h index 7984372..be2224d 100644 --- a/source/http/message.h +++ b/source/http/message.h @@ -4,12 +4,13 @@ #include #include #include +#include #include "version.h" namespace Msp { namespace Http { -class Message +class MSPNET_API Message { protected: typedef std::map HeaderMap; diff --git a/source/http/request.h b/source/http/request.h index 979c785..11af993 100644 --- a/source/http/request.h +++ b/source/http/request.h @@ -2,12 +2,13 @@ #define MSP_HTTP_REQUEST_H_ #include +#include #include "message.h" namespace Msp { namespace Http { -class Request: public Message +class MSPNET_API Request: public Message { private: std::string method; diff --git a/source/http/response.h b/source/http/response.h index f7c6812..8644b72 100644 --- a/source/http/response.h +++ b/source/http/response.h @@ -1,13 +1,14 @@ #ifndef MSP_HTTP_RESPONSE_H_ #define MSP_HTTP_RESPONSE_H_ +#include #include "message.h" #include "status.h" namespace Msp { namespace Http { -class Response: public Message +class MSPNET_API Response: public Message { private: Status status; diff --git a/source/http/server.h b/source/http/server.h index dd8b2c6..8665fa3 100644 --- a/source/http/server.h +++ b/source/http/server.h @@ -2,6 +2,7 @@ #define MSP_HTTP_SERVER_H_ #include +#include #include #include @@ -11,7 +12,7 @@ namespace Http { class Request; class Response; -class Server +class MSPNET_API Server { public: sigc::signal signal_request; diff --git a/source/http/status.h b/source/http/status.h index 5e7d54b..5f4656e 100644 --- a/source/http/status.h +++ b/source/http/status.h @@ -2,6 +2,7 @@ #define MSP_HTTPSERVER_STATUS_H_ #include +#include namespace Msp { namespace Http { @@ -20,7 +21,7 @@ enum Status NOT_IMPLEMENTED = 501 }; -extern std::ostream &operator<<(std::ostream &, Status); +MSPNET_API std::ostream &operator<<(std::ostream &, Status); } // namespace Http } // namespace Msp diff --git a/source/http/utils.h b/source/http/utils.h index a25a748..2920d2d 100644 --- a/source/http/utils.h +++ b/source/http/utils.h @@ -3,6 +3,7 @@ #include #include +#include namespace Msp { namespace Http { @@ -25,13 +26,13 @@ struct Url typedef std::map Query; -std::string urlencode(const std::string &, EncodeLevel =SAFE); -std::string urlencode_plus(const std::string &, EncodeLevel =SAFE); -std::string urldecode(const std::string &); -Url parse_url(const std::string &); -std::string build_url(const Url &); -Query parse_query(const std::string &); -std::string build_query(const Query &); +MSPNET_API std::string urlencode(const std::string &, EncodeLevel =SAFE); +MSPNET_API std::string urlencode_plus(const std::string &, EncodeLevel =SAFE); +MSPNET_API std::string urldecode(const std::string &); +MSPNET_API Url parse_url(const std::string &); +MSPNET_API std::string build_url(const Url &); +MSPNET_API Query parse_query(const std::string &); +MSPNET_API std::string build_query(const Query &); } // namespace Http } // namespace Msp diff --git a/source/http/version.h b/source/http/version.h index 19a50b9..665e619 100644 --- a/source/http/version.h +++ b/source/http/version.h @@ -2,14 +2,15 @@ #define MSP_HTTP_MISC_H_ #include +#include namespace Msp { namespace Http { typedef unsigned Version; -Version parse_version(const std::string &); -std::string version_str(Version); +MSPNET_API Version parse_version(const std::string &); +MSPNET_API std::string version_str(Version); } // namespace Http } // namespace Msp diff --git a/source/net/clientsocket.cpp b/source/net/clientsocket.cpp index 7987f4b..51a8dfa 100644 --- a/source/net/clientsocket.cpp +++ b/source/net/clientsocket.cpp @@ -74,6 +74,7 @@ size_t ClientSocket::do_read(char *buf, size_t size) if(!connected) throw bad_socket_state("not connected"); + // XXX This breaks level-triggered semantics on Windows if(size==0) return 0; diff --git a/source/net/clientsocket.h b/source/net/clientsocket.h index c33ed93..80d927a 100644 --- a/source/net/clientsocket.h +++ b/source/net/clientsocket.h @@ -1,6 +1,7 @@ #ifndef MSP_NET_CLIENTSOCKET_H_ #define MSP_NET_CLIENTSOCKET_H_ +#include "mspnet_api.h" #include "socket.h" namespace Msp { @@ -9,7 +10,7 @@ namespace Net { /** ClientSockets are used for sending and receiving data over the network. */ -class ClientSocket: public Socket +class MSPNET_API ClientSocket: public Socket { public: /** Emitted when the socket finishes connecting. */ diff --git a/source/net/communicator.h b/source/net/communicator.h index b98f488..e9f646a 100644 --- a/source/net/communicator.h +++ b/source/net/communicator.h @@ -4,6 +4,7 @@ #include #include #include +#include "mspnet_api.h" #include "protocol.h" namespace Msp { @@ -11,20 +12,20 @@ namespace Net { class StreamSocket; -class sequence_error: public invalid_state +class MSPNET_API sequence_error: public invalid_state { public: sequence_error(const std::string &w): invalid_state(w) { } }; -class incompatible_protocol: public std::runtime_error +class MSPNET_API incompatible_protocol: public std::runtime_error { public: incompatible_protocol(const std::string &w): std::runtime_error(w) { } }; -class Communicator: public NonCopyable +class MSPNET_API Communicator: public NonCopyable { public: sigc::signal signal_handshake_done; diff --git a/source/net/datagramsocket.h b/source/net/datagramsocket.h index e5ded78..a978b9f 100644 --- a/source/net/datagramsocket.h +++ b/source/net/datagramsocket.h @@ -2,11 +2,12 @@ #define MSP_NET_DATAGRAMSOCKET_H_ #include "clientsocket.h" +#include "mspnet_api.h" namespace Msp { namespace Net { -class DatagramSocket: public ClientSocket +class MSPNET_API DatagramSocket: public ClientSocket { public: DatagramSocket(Family, int = 0); diff --git a/source/net/inet.h b/source/net/inet.h index 517b111..fb76d7e 100644 --- a/source/net/inet.h +++ b/source/net/inet.h @@ -1,6 +1,7 @@ #ifndef MSP_NET_INET_H_ #define MSP_NET_INET_H_ +#include "mspnet_api.h" #include "sockaddr.h" namespace Msp { @@ -9,7 +10,7 @@ namespace Net { /** Address class for IPv4 sockets. */ -class InetAddr: public SockAddr +class MSPNET_API InetAddr: public SockAddr { private: unsigned char addr[4] = { }; diff --git a/source/net/inet6.h b/source/net/inet6.h index d4455e5..38cba33 100644 --- a/source/net/inet6.h +++ b/source/net/inet6.h @@ -1,12 +1,13 @@ #ifndef MSP_NET_INET6_H_ #define MSP_NET_INET6_H_ +#include "mspnet_api.h" #include "sockaddr.h" namespace Msp { namespace Net { -class Inet6Addr: public SockAddr +class MSPNET_API Inet6Addr: public SockAddr { private: unsigned char addr[16] = { }; diff --git a/source/net/mspnet_api.h b/source/net/mspnet_api.h new file mode 100644 index 0000000..eccdd41 --- /dev/null +++ b/source/net/mspnet_api.h @@ -0,0 +1,18 @@ +#ifndef MSP_NET_API_H_ +#define MSP_NET_API_H_ + +#if defined(_WIN32) +#if defined(MSPNET_BUILD) +#define MSPNET_API __declspec(dllexport) +#elif defined(MSPNET_IMPORT) +#define MSPNET_API __declspec(dllimport) +#else +#define MSPNET_API +#endif +#elif defined(__GNUC__) +#define MSPNET_API __attribute__((visibility("default"))) +#else +#define MSPNET_API +#endif + +#endif diff --git a/source/net/protocol.h b/source/net/protocol.h index f040a69..8068d32 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -7,26 +7,27 @@ #include #include #include +#include "mspnet_api.h" #include "receiver.h" namespace Msp { namespace Net { -class bad_packet: public std::runtime_error +class MSPNET_API bad_packet: public std::runtime_error { public: bad_packet(const std::string &w): std::runtime_error(w) { } }; -class buffer_error: public std::runtime_error +class MSPNET_API buffer_error: public std::runtime_error { public: buffer_error(const std::string &w): std::runtime_error(w) { } }; -class Protocol +class MSPNET_API Protocol { private: template diff --git a/source/net/receiver.h b/source/net/receiver.h index de7209e..9af1bb7 100644 --- a/source/net/receiver.h +++ b/source/net/receiver.h @@ -1,10 +1,12 @@ #ifndef MSP_NET_RECEIVER_H_ #define MSP_NET_RECEIVER_H_ +#include "mspnet_api.h" + namespace Msp { namespace Net { -class ReceiverBase +class MSPNET_API ReceiverBase { protected: ReceiverBase() = default; diff --git a/source/net/resolve.h b/source/net/resolve.h index 212a7f0..3071b77 100644 --- a/source/net/resolve.h +++ b/source/net/resolve.h @@ -9,6 +9,7 @@ #include #include #include +#include "mspnet_api.h" #include "sockaddr.h" namespace Msp { @@ -18,19 +19,19 @@ namespace Net { the loopback address will be used. If host is "*", the wildcard address will be used. If service is empty, a socket address with a null service will be returned. With the IP families, these are not very useful. */ -SockAddr *resolve(const std::string &, const std::string &, Family = UNSPEC); +MSPNET_API SockAddr *resolve(const std::string &, const std::string &, Family = UNSPEC); /** And overload of resolve() that takes host and service as a single string, separated by a colon. If the host part contains colons, such as is the case with a numeric IPv6 address, it must be enclosed in brackets. */ -SockAddr *resolve(const std::string &, Family = UNSPEC); +MSPNET_API SockAddr *resolve(const std::string &, Family = UNSPEC); /** An asynchronous name resolver. Blocking calls are performed in a thread and completion is notified with one of the two signals. */ -class Resolver +class MSPNET_API Resolver { private: struct Task diff --git a/source/net/serversocket.h b/source/net/serversocket.h index 48ab9ba..8c082b3 100644 --- a/source/net/serversocket.h +++ b/source/net/serversocket.h @@ -1,6 +1,7 @@ #ifndef MSP_NET_SERVERSOCKET_H_ #define MSP_NET_SERVERSOCKET_H_ +#include "mspnet_api.h" #include "socket.h" namespace Msp { @@ -12,7 +13,7 @@ class ClientSocket; ServerSockets are used to receive incoming connections. They cannot be used for sending and receiving data. */ -class ServerSocket: public Socket +class MSPNET_API ServerSocket: public Socket { protected: ServerSocket(Family, int, int); diff --git a/source/net/sockaddr.h b/source/net/sockaddr.h index 817de4d..931d4f4 100644 --- a/source/net/sockaddr.h +++ b/source/net/sockaddr.h @@ -2,6 +2,7 @@ #define MSP_NET_SOCKADDR_H_ #include +#include "mspnet_api.h" namespace Msp { namespace Net { @@ -15,7 +16,7 @@ enum Family }; -class SockAddr +class MSPNET_API SockAddr { public: struct SysAddr; diff --git a/source/net/socket.h b/source/net/socket.h index 3e3c2ae..b89c175 100644 --- a/source/net/socket.h +++ b/source/net/socket.h @@ -5,19 +5,20 @@ #include #include #include +#include "mspnet_api.h" #include "sockaddr.h" namespace Msp { namespace Net { -class bad_socket_state: public invalid_state +class MSPNET_API bad_socket_state: public invalid_state { public: bad_socket_state(const std::string &w): invalid_state(w) { } }; -class Socket: public IO::EventObject +class MSPNET_API Socket: public IO::EventObject { protected: enum SocketEvent diff --git a/source/net/streamserversocket.h b/source/net/streamserversocket.h index bd2487d..bbe1c91 100644 --- a/source/net/streamserversocket.h +++ b/source/net/streamserversocket.h @@ -1,13 +1,14 @@ #ifndef MSP_NET_STREAMSERVERSOCKET_H_ #define MSP_NET_STREAMSERVERSOCKET_H_ +#include "mspnet_api.h" #include "serversocket.h" #include "streamsocket.h" namespace Msp { namespace Net { -class StreamServerSocket: public ServerSocket +class MSPNET_API StreamServerSocket: public ServerSocket { private: bool listening = false; diff --git a/source/net/streamsocket.h b/source/net/streamsocket.h index 6f478d2..84b347d 100644 --- a/source/net/streamsocket.h +++ b/source/net/streamsocket.h @@ -2,11 +2,12 @@ #define MSP_NET_STREAMSOCKET_H_ #include "clientsocket.h" +#include "mspnet_api.h" namespace Msp { namespace Net { -class StreamSocket: public ClientSocket +class MSPNET_API StreamSocket: public ClientSocket { friend class StreamServerSocket; diff --git a/source/net/unix.h b/source/net/unix.h index a7431a9..86f1ddd 100644 --- a/source/net/unix.h +++ b/source/net/unix.h @@ -1,12 +1,13 @@ #ifndef MSP_NET_UNIX_H_ #define MSP_NET_UNIX_H_ +#include "mspnet_api.h" #include "sockaddr.h" namespace Msp { namespace Net { -class UnixAddr: public SockAddr +class MSPNET_API UnixAddr: public SockAddr { private: std::string path; -- 2.43.0 From d6ba60220dadabe552f9f7dc2b4fc34b51cf4d32 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 00:31:22 +0200 Subject: [PATCH 11/16] Add some useful factory functions to InetAddr and Inet6Addr --- source/net/inet.cpp | 16 ++++++++++++++++ source/net/inet.h | 3 +++ source/net/inet6.cpp | 15 +++++++++++++++ source/net/inet6.h | 3 +++ 4 files changed, 37 insertions(+) diff --git a/source/net/inet.cpp b/source/net/inet.cpp index f44b1c0..17546e8 100644 --- a/source/net/inet.cpp +++ b/source/net/inet.cpp @@ -16,6 +16,22 @@ InetAddr::InetAddr(const SysAddr &sa) port = ntohs(sai.sin_port); } +InetAddr InetAddr::wildcard(unsigned port) +{ + InetAddr addr; + addr.port = port; + return addr; +} + +InetAddr InetAddr::localhost(unsigned port) +{ + InetAddr addr; + addr.addr[0] = 127; + addr.addr[3] = 1; + addr.port = port; + return addr; +} + SockAddr::SysAddr InetAddr::to_sys() const { SysAddr sa; diff --git a/source/net/inet.h b/source/net/inet.h index fb76d7e..d970559 100644 --- a/source/net/inet.h +++ b/source/net/inet.h @@ -20,6 +20,9 @@ public: InetAddr() = default; InetAddr(const SysAddr &); + static InetAddr wildcard(unsigned); + static InetAddr localhost(unsigned); + InetAddr *copy() const override { return new InetAddr(*this); } SysAddr to_sys() const override; diff --git a/source/net/inet6.cpp b/source/net/inet6.cpp index da584d4..1b8fc39 100644 --- a/source/net/inet6.cpp +++ b/source/net/inet6.cpp @@ -16,6 +16,21 @@ Inet6Addr::Inet6Addr(const SysAddr &sa) port = htons(sai6.sin6_port); } +Inet6Addr Inet6Addr::wildcard(unsigned port) +{ + Inet6Addr addr; + addr.port = port; + return addr; +} + +Inet6Addr Inet6Addr::localhost(unsigned port) +{ + Inet6Addr addr; + addr.addr[15] = 1; + addr.port = port; + return addr; +} + SockAddr::SysAddr Inet6Addr::to_sys() const { SysAddr sa; diff --git a/source/net/inet6.h b/source/net/inet6.h index 38cba33..f6b0cf4 100644 --- a/source/net/inet6.h +++ b/source/net/inet6.h @@ -17,6 +17,9 @@ public: Inet6Addr() = default; Inet6Addr(const SysAddr &); + static Inet6Addr wildcard(unsigned); + static Inet6Addr localhost(unsigned); + Inet6Addr *copy() const override { return new Inet6Addr(*this); } SysAddr to_sys() const override; -- 2.43.0 From c2e9e03b191a6ffe44a83be32aadf2a325491c02 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 09:36:07 +0200 Subject: [PATCH 12/16] Add a base id parameter to Protocol's serialization functions This allows multiplexing protocols on a single connection by externally adjusting their packet ID. --- source/net/protocol.cpp | 4 ++-- source/net/protocol.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index 63cc6f1..c611292 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -44,13 +44,13 @@ const Protocol::PacketDefBase &Protocol::get_packet_by_id(unsigned id) const return *get_item(packet_id_defs, id); } -size_t Protocol::dispatch(ReceiverBase &rcv, const char *buf, size_t size) const +size_t Protocol::dispatch(ReceiverBase &rcv, const char *buf, size_t size, unsigned base_id) const { PacketHeader header; const char *ptr = header_def.deserialize(header, buf, buf+size); if(header.length>size) throw bad_packet("truncated"); - const PacketDefBase &pdef = get_packet_by_id(header.type); + const PacketDefBase &pdef = get_packet_by_id(header.type-base_id); ptr = pdef.dispatch(rcv, ptr, ptr+header.length); return ptr-buf; } diff --git a/source/net/protocol.h b/source/net/protocol.h index 8068d32..0721ec5 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -226,10 +226,10 @@ protected: public: template - std::size_t serialize(const P &, char *, std::size_t) const; + std::size_t serialize(const P &, char *, std::size_t, unsigned = 0) const; std::size_t get_packet_size(const char *, std::size_t) const; - std::size_t dispatch(ReceiverBase &, const char *, std::size_t) const; + std::size_t dispatch(ReceiverBase &, const char *, std::size_t, unsigned = 0) const; std::uint64_t get_hash() const; }; @@ -265,14 +265,14 @@ const Protocol::PacketTypeDef

&Protocol::get_packet_by_class() const } template -std::size_t Protocol::serialize(const P &pkt, char *buf, std::size_t size) const +std::size_t Protocol::serialize(const P &pkt, char *buf, std::size_t size, unsigned base_id) const { const PacketTypeDef

&pdef = get_packet_by_class

(); if(!pdef.get_id()) throw std::invalid_argument("no packet id"); char *ptr = pdef.serialize(pkt, buf+4, buf+size); size = ptr-buf; - header_def.serialize(PacketHeader(pdef.get_id(), size), buf, buf+4); + header_def.serialize(PacketHeader(base_id+pdef.get_id(), size), buf, buf+4); return size; } -- 2.43.0 From 449a2f3417748761f94f3002b1c15819c4d83365 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 10:04:25 +0200 Subject: [PATCH 13/16] Provide access to packet IDs in Protcool --- source/net/protocol.cpp | 20 ++++++++++++++++---- source/net/protocol.h | 28 +++++++++++++++++++--------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index c611292..358966f 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -44,6 +44,13 @@ const Protocol::PacketDefBase &Protocol::get_packet_by_id(unsigned id) const return *get_item(packet_id_defs, id); } +unsigned Protocol::get_max_packet_id() const +{ + if(packet_id_defs.empty()) + return 0; + return prev(packet_id_defs.end())->first; +} + size_t Protocol::dispatch(ReceiverBase &rcv, const char *buf, size_t size, unsigned base_id) const { PacketHeader header; @@ -55,13 +62,18 @@ size_t Protocol::dispatch(ReceiverBase &rcv, const char *buf, size_t size, unsig return ptr-buf; } -size_t Protocol::get_packet_size(const char *buf, size_t size) const +bool Protocol::get_packet_header(PacketHeader &header, const char *buf, size_t size) const { if(size<4) - return 0; - PacketHeader header; + return false; header_def.deserialize(header, buf, buf+size); - return header.length; + return true; +} + +size_t Protocol::get_packet_size(const char *buf, size_t size) const +{ + PacketHeader header; + return (get_packet_header(header, buf, size) ? header.length : 0); } uint64_t Protocol::get_hash() const diff --git a/source/net/protocol.h b/source/net/protocol.h index 0721ec5..248b97e 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -29,6 +29,16 @@ public: class MSPNET_API Protocol { +public: + struct PacketHeader + { + std::uint16_t type = 0; + std::uint16_t length = 0; + + PacketHeader() = default; + PacketHeader(std::uint16_t, std::uint16_t); + }; + private: template struct BasicTraits; @@ -180,15 +190,6 @@ private: auto fields(T1 P::*first, T2 P::*second, Rest P::*...rest) { return fields(first).fields(second, rest...); } }; - struct PacketHeader - { - std::uint16_t type = 0; - std::uint16_t length = 0; - - PacketHeader() = default; - PacketHeader(std::uint16_t, std::uint16_t); - }; - PacketTypeDef header_def; unsigned next_packet_id; std::map> packet_class_defs; @@ -225,9 +226,18 @@ protected: const PacketTypeDef

&get_packet_by_class() const; public: + template + bool has_packet() const { return packet_class_defs.count(get_packet_class_id

()); } + + template + unsigned get_packet_id() const { return get_item(packet_class_defs, get_packet_class_id

())->get_id(); } + + unsigned get_max_packet_id() const; + template std::size_t serialize(const P &, char *, std::size_t, unsigned = 0) const; + bool get_packet_header(PacketHeader &, const char *, std::size_t) const; std::size_t get_packet_size(const char *, std::size_t) const; std::size_t dispatch(ReceiverBase &, const char *, std::size_t, unsigned = 0) const; -- 2.43.0 From f17a55dc7fc44d1516db445550f55ed31e7534fa Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 23:32:10 +0200 Subject: [PATCH 14/16] Redesign Communicator to support multiple protocols This makes it easier for a library to provide a base protocol and allow applications to add their own protocols on top. It's also used for the Communicator's internal handshake protocol. --- source/net/communicator.cpp | 227 +++++++++++++++++++++++++----------- source/net/communicator.h | 42 +++++-- 2 files changed, 193 insertions(+), 76 deletions(-) diff --git a/source/net/communicator.cpp b/source/net/communicator.cpp index 51202b5..73b10c2 100644 --- a/source/net/communicator.cpp +++ b/source/net/communicator.cpp @@ -8,7 +8,15 @@ namespace { using namespace Msp::Net; -struct Handshake +// Sent when a protocol is added locally but isn't known to the other end yet +struct PrepareProtocol +{ + uint64_t hash; + uint16_t base; +}; + +// Sent to confirm that a protocol is known to both peers +struct AcceptProtocol { uint64_t hash; }; @@ -20,67 +28,107 @@ public: HandshakeProtocol(); }; -HandshakeProtocol::HandshakeProtocol(): - Protocol(0x7F00) +HandshakeProtocol::HandshakeProtocol() { - add(&Handshake::hash); + add(&PrepareProtocol::hash, &PrepareProtocol::base); + add(&AcceptProtocol::hash); } +} -class HandshakeReceiver: public PacketReceiver -{ -private: - uint64_t hash = 0; -public: - uint64_t get_hash() const { return hash; } - void receive(const Handshake &) override; -}; +namespace Msp { +namespace Net { -void HandshakeReceiver::receive(const Handshake &shake) +struct Communicator::Handshake: public PacketReceiver, + public PacketReceiver { - hash = shake.hash; -} + Communicator &communicator; + HandshakeProtocol protocol; -} + Handshake(Communicator &c): communicator(c) { } + void receive(const PrepareProtocol &) override; + void receive(const AcceptProtocol &) override; +}; -namespace Msp { -namespace Net { -Communicator::Communicator(StreamSocket &s, const Protocol &p, ReceiverBase &r): +Communicator::Communicator(StreamSocket &s): socket(s), - protocol(p), - receiver(r), + handshake(new Handshake(*this)), in_buf(new char[buf_size]), in_begin(in_buf), in_end(in_buf), out_buf(new char[buf_size]) { socket.signal_data_available.connect(sigc::mem_fun(this, &Communicator::data_available)); + + protocols.emplace_back(0, ref(handshake->protocol), ref(*handshake)); + if(socket.is_connected()) + prepare_protocol(protocols.back()); + else + socket.signal_connect_finished.connect(sigc::mem_fun(this, &Communicator::connect_finished)); +} + +Communicator::Communicator(StreamSocket &s, const Protocol &p, ReceiverBase &r): + Communicator(s) +{ + add_protocol(p, r); } Communicator::~Communicator() { delete[] in_buf; delete[] out_buf; + delete handshake; } -void Communicator::initiate_handshake() +void Communicator::add_protocol(const Protocol &proto, ReceiverBase &recv) { - if(handshake_status!=0) - throw sequence_error("handshaking already done"); + if(!good) + throw sequence_error("connection aborted"); + + unsigned max_id = proto.get_max_packet_id(); + if(!max_id) + throw invalid_argument("Communicator::add_protocol"); - send_handshake(); - handshake_status = 1; + uint64_t hash = proto.get_hash(); + auto i = find_member(protocols, hash, &ActiveProtocol::hash); + if(i==protocols.end()) + { + const ActiveProtocol &last = protocols.back(); + if(!last.protocol) + throw sequence_error("previous protocol is incomplete"); + unsigned base = last.base; + base += (last.protocol->get_max_packet_id()+0xFF)&~0xFF; + + if(base+max_id>std::numeric_limits::max()) + throw invalid_state("Communicator::add_protocol"); + + protocols.emplace_back(base, proto, recv); + + if(socket.is_connected() && protocols.front().ready) + prepare_protocol(protocols.back()); + } + else if(!i->protocol) + { + i->protocol = &proto; + i->last = i->base+max_id; + i->receiver = &recv; + accept_protocol(*i); + } +} + +bool Communicator::is_protocol_ready(const Protocol &proto) const +{ + auto i = find_member(protocols, &proto, &ActiveProtocol::protocol); + return (i!=protocols.end() && i->ready); } void Communicator::send_data(size_t size) { if(!good) throw sequence_error("connection aborted"); - if(handshake_status!=2) - throw sequence_error("handshake incomplete"); try { @@ -95,6 +143,14 @@ void Communicator::send_data(size_t size) } } +void Communicator::connect_finished(const exception *exc) +{ + if(exc) + good = false; + else + prepare_protocol(protocols.front()); +} + void Communicator::data_available() { if(!good) @@ -103,31 +159,7 @@ void Communicator::data_available() try { in_end += socket.read(in_end, in_buf+buf_size-in_end); - - bool more = true; - while(more) - { - if(handshake_status==2) - more = receive_packet(protocol, receiver); - else - { - HandshakeProtocol hsproto; - HandshakeReceiver hsrecv; - if((more = receive_packet(hsproto, hsrecv))) - { - if(handshake_status==0) - send_handshake(); - - if(hsrecv.get_hash()==protocol.get_hash()) - { - handshake_status = 2; - signal_handshake_done.emit(); - } - else - throw incompatible_protocol("hash mismatch"); - } - } - } + while(receive_packet()) ; } catch(const exception &e) { @@ -138,37 +170,98 @@ void Communicator::data_available() } } -bool Communicator::receive_packet(const Protocol &proto, ReceiverBase &recv) +bool Communicator::receive_packet() { - int psz = proto.get_packet_size(in_begin, in_end-in_begin); - if(psz && psz<=in_end-in_begin) + Protocol::PacketHeader header; + size_t available = in_end-in_begin; + if(handshake->protocol.get_packet_header(header, in_begin, available) && header.length<=available) { + auto i = lower_bound_member(protocols, header.type, &ActiveProtocol::last); + if(i==protocols.end() || header.typebase || header.type>i->last) + throw key_error(header.type); + char *pkt = in_begin; - in_begin += psz; - proto.dispatch(recv, pkt, psz); + in_begin += header.length; + i->protocol->dispatch(*i->receiver, pkt, header.length, i->base); return true; } else { if(in_end==in_buf+buf_size) { - size_t used = in_end-in_begin; - memmove(in_buf, in_begin, used); + memmove(in_buf, in_begin, available); in_begin = in_buf; - in_end = in_begin+used; + in_end = in_begin+available; } return false; } } -void Communicator::send_handshake() +void Communicator::prepare_protocol(const ActiveProtocol &proto) +{ + PrepareProtocol prepare; + prepare.hash = proto.hash; + prepare.base = proto.base; + /* Use send_data() directly because this function is called to prepare the + handshake protocol too and send() would fail readiness check. */ + send_data(handshake->protocol.serialize(prepare, out_buf, buf_size)); +} + +void Communicator::accept_protocol(ActiveProtocol &proto) +{ + proto.accepted = true; + + AcceptProtocol accept; + accept.hash = proto.hash; + send_data(handshake->protocol.serialize(accept, out_buf, buf_size)); +} + + +Communicator::ActiveProtocol::ActiveProtocol(uint16_t b, const Protocol &p, ReceiverBase &r): + hash(p.get_hash()), + base(b), + last(base+p.get_max_packet_id()), + protocol(&p), + receiver(&r) +{ } + +Communicator::ActiveProtocol::ActiveProtocol(uint16_t b, uint64_t h): + hash(h), + base(b), + last(base) +{ } + + +void Communicator::Handshake::receive(const PrepareProtocol &prepare) +{ + auto i = lower_bound_member(communicator.protocols, prepare.base, &ActiveProtocol::base); + if(i!=communicator.protocols.end() && i->base==prepare.base) + communicator.accept_protocol(*i); + else + communicator.protocols.emplace(i, prepare.base, prepare.hash); +} + +void Communicator::Handshake::receive(const AcceptProtocol &accept) { - Handshake shake; - shake.hash = protocol.get_hash(); + auto i = find_member(communicator.protocols, accept.hash, &ActiveProtocol::hash); + if(i==communicator.protocols.end()) + throw key_error(accept.hash); + + + if(i->ready) + return; - HandshakeProtocol hsproto; - size_t size = hsproto.serialize(shake, out_buf, buf_size); - socket.write(out_buf, size); + i->ready = true; + if(!i->accepted) + communicator.accept_protocol(*i); + if(i->protocol==&protocol) + { + for(const ActiveProtocol &p: communicator.protocols) + if(!p.ready) + communicator.prepare_protocol(p); + } + else + communicator.signal_protocol_ready.emit(*i->protocol); } } // namespace Net diff --git a/source/net/communicator.h b/source/net/communicator.h index e9f646a..eb9893d 100644 --- a/source/net/communicator.h +++ b/source/net/communicator.h @@ -28,14 +28,29 @@ public: class MSPNET_API Communicator: public NonCopyable { public: - sigc::signal signal_handshake_done; + sigc::signal signal_protocol_ready; sigc::signal signal_error; private: + struct ActiveProtocol + { + std::uint64_t hash = 0; + std::uint16_t base = 0; + std::uint16_t last = 0; + bool accepted = false; + bool ready = false; + const Protocol *protocol = nullptr; + ReceiverBase *receiver = nullptr; + + ActiveProtocol(std::uint16_t, const Protocol &, ReceiverBase &); + ActiveProtocol(std::uint16_t, std::uint64_t); + }; + + struct Handshake; + StreamSocket &socket; - const Protocol &protocol; - ReceiverBase &receiver; - int handshake_status = 0; + std::vector protocols; + Handshake *handshake = nullptr; std::size_t buf_size = 65536; char *in_buf = nullptr; char *in_begin = nullptr; @@ -44,11 +59,12 @@ private: bool good = true; public: + Communicator(StreamSocket &); Communicator(StreamSocket &, const Protocol &, ReceiverBase &); ~Communicator(); - void initiate_handshake(); - bool is_handshake_done() const { return handshake_status==2; } + void add_protocol(const Protocol &, ReceiverBase &); + bool is_protocol_ready(const Protocol &) const; template void send(const P &); @@ -56,15 +72,23 @@ public: private: void send_data(std::size_t); + void connect_finished(const std::exception *); void data_available(); - bool receive_packet(const Protocol &, ReceiverBase &); - void send_handshake(); + bool receive_packet(); + + void prepare_protocol(const ActiveProtocol &); + void accept_protocol(ActiveProtocol &); }; template void Communicator::send(const P &pkt) { - send_data(protocol.serialize(pkt, out_buf, buf_size)); + auto i = find_if(protocols, [](const ActiveProtocol &p){ return p.protocol && p.protocol->has_packet

(); }); + if(i==protocols.end()) + throw key_error(typeid(P).name()); + else if(!i->ready) + throw sequence_error("protocol not ready"); + send_data(i->protocol->serialize(pkt, out_buf, buf_size, i->base)); } } // namespace Net -- 2.43.0 From 3f46fef7032d97b0dd82971ffece1062fd6b05b8 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 15 Jan 2023 23:50:54 +0200 Subject: [PATCH 15/16] Remove function overloads for manually specifying packet IDs Since the IDs can be transposed when sending and receiving, this isn't terribly useful anymore. For Communicator's multi-protocol support having abnormally high packet IDs is detrimental. --- source/net/protocol.cpp | 5 ++--- source/net/protocol.h | 20 ++++---------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index 358966f..7fde8fe 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -10,9 +10,8 @@ using namespace std; namespace Msp { namespace Net { -Protocol::Protocol(unsigned npi): - header_def(0), - next_packet_id(npi) +Protocol::Protocol(): + header_def(0) { PacketDefBuilder>(*this, header_def, Serializer()) .fields(&PacketHeader::type, &PacketHeader::length); diff --git a/source/net/protocol.h b/source/net/protocol.h index 248b97e..e766733 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -191,12 +191,12 @@ private: }; PacketTypeDef header_def; - unsigned next_packet_id; + unsigned next_packet_id = 1; std::map> packet_class_defs; std::map packet_id_defs; protected: - Protocol(unsigned = 1); + Protocol(); private: static unsigned get_next_packet_class_id(); @@ -207,12 +207,6 @@ private: void add_packet(std::unique_ptr); protected: - template - PacketDefBuilder> add(unsigned); - - template - auto add(unsigned id, T P::*field, Rest P::*...rest) { return add

(id).fields(field, rest...); } - template PacketDefBuilder> add(); @@ -253,20 +247,14 @@ unsigned Protocol::get_packet_class_id() } template -Protocol::PacketDefBuilder> Protocol::add(unsigned id) +Protocol::PacketDefBuilder> Protocol::add() { - std::unique_ptr> pdef = std::make_unique>(id); + std::unique_ptr> pdef = std::make_unique>(next_packet_id++); PacketDefBuilder> next(*this, *pdef, Serializer

()); add_packet(move(pdef)); return next; } -template -Protocol::PacketDefBuilder> Protocol::add() -{ - return add

(next_packet_id++); -} - template const Protocol::PacketTypeDef

&Protocol::get_packet_by_class() const { -- 2.43.0 From b451f834a6b5440fb1064cf96c69eb5447ae86cf Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 1 Jun 2023 10:31:43 +0300 Subject: [PATCH 16/16] Add a dynamic receiver class for more flexible packet handling It's useful for middleware libraries which may not know all packet types at compile time, or if different packets need to be routed to different receivers. --- source/net/protocol.cpp | 9 ++++++- source/net/protocol.h | 11 +++++++++ source/net/receiver.cpp | 16 +++++++++++++ source/net/receiver.h | 53 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 source/net/receiver.cpp diff --git a/source/net/protocol.cpp b/source/net/protocol.cpp index 7fde8fe..5e82a80 100644 --- a/source/net/protocol.cpp +++ b/source/net/protocol.cpp @@ -57,7 +57,14 @@ size_t Protocol::dispatch(ReceiverBase &rcv, const char *buf, size_t size, unsig if(header.length>size) throw bad_packet("truncated"); const PacketDefBase &pdef = get_packet_by_id(header.type-base_id); - ptr = pdef.dispatch(rcv, ptr, ptr+header.length); + if(DynamicReceiver *drcv = dynamic_cast(&rcv)) + { + Variant pkt; + ptr = pdef.deserialize(pkt, ptr, ptr+header.length); + drcv->receive(pdef.get_id(), pkt); + } + else + ptr = pdef.dispatch(rcv, ptr, ptr+header.length); return ptr-buf; } diff --git a/source/net/protocol.h b/source/net/protocol.h index e766733..5051338 100644 --- a/source/net/protocol.h +++ b/source/net/protocol.h @@ -147,6 +147,7 @@ private: virtual unsigned get_class_id() const = 0; unsigned get_id() const { return id; } virtual std::uint64_t get_hash() const = 0; + virtual const char *deserialize(Variant &, const char *, const char *) const = 0; virtual const char *dispatch(ReceiverBase &, const char *, const char *) const = 0; }; @@ -169,6 +170,7 @@ private: std::uint64_t get_hash() const override { return serializer->get_hash(); } char *serialize(const P &, char *, char *) const; const char *deserialize(P &, const char *, const char *) const; + const char *deserialize(Variant &, const char *, const char *) const override; const char *dispatch(ReceiverBase &, const char *, const char *) const override; }; @@ -431,6 +433,15 @@ const char *Protocol::PacketTypeDef

::deserialize(P &pkt, const char *buf, con return serializer->deserialize(pkt, buf, end); } +template +const char *Protocol::PacketTypeDef

::deserialize(Variant &var_pkt, const char *buf, const char *end) const +{ + P pkt; + const char *ptr = serializer->deserialize(pkt, buf, end); + var_pkt = std::move(pkt); + return ptr; +} + template const char *Protocol::PacketTypeDef

::dispatch(ReceiverBase &rcv, const char *buf, const char *end) const { diff --git a/source/net/receiver.cpp b/source/net/receiver.cpp new file mode 100644 index 0000000..46c12f3 --- /dev/null +++ b/source/net/receiver.cpp @@ -0,0 +1,16 @@ +#include "receiver.h" + +namespace Msp { +namespace Net { + +void DynamicDispatcher::receive(unsigned packet_id, const Variant &packet) +{ + auto i = lower_bound_member(targets, packet_id, &Target::packet_id); + if(i==targets.end() || i->packet_id!=packet_id) + throw key_error(packet_id); + + i->func(*i->receiver, packet); +} + +} // namespace Net +} // namespace Msp diff --git a/source/net/receiver.h b/source/net/receiver.h index 9af1bb7..a0a9f29 100644 --- a/source/net/receiver.h +++ b/source/net/receiver.h @@ -1,6 +1,10 @@ #ifndef MSP_NET_RECEIVER_H_ #define MSP_NET_RECEIVER_H_ +#include +#include +#include +#include #include "mspnet_api.h" namespace Msp { @@ -14,6 +18,7 @@ public: virtual ~ReceiverBase() = default; }; + template class PacketReceiver: public virtual ReceiverBase { @@ -23,6 +28,54 @@ public: virtual void receive(const P &) = 0; }; + +class MSPNET_API DynamicReceiver: public ReceiverBase +{ +protected: + DynamicReceiver() = default; +public: + virtual void receive(unsigned, const Variant &) = 0; +}; + + +class MSPNET_API DynamicDispatcher: public DynamicReceiver +{ +private: + using DispatchFunc = void(ReceiverBase &, const Variant &); + + struct Target + { + unsigned packet_id; + ReceiverBase *receiver; + DispatchFunc *func; + + Target(unsigned i, ReceiverBase &r, DispatchFunc *f): packet_id(i), receiver(&r), func(f) { } + }; + + std::vector targets; + +public: + template + void add_receiver(unsigned, PacketReceiver

&); + + void receive(unsigned, const Variant &) override; +}; + + +template +void DynamicDispatcher::add_receiver(unsigned packet_id, PacketReceiver

&r) +{ + auto i = lower_bound_member(targets, packet_id, &Target::packet_id); + if(i!=targets.end() && i->packet_id==packet_id) + throw key_error(packet_id); + + auto dispatch = [](ReceiverBase &receiver, const Variant &packet){ + dynamic_cast &>(receiver).receive(packet.value

()); + }; + + targets.emplace(i, packet_id, r, +dispatch); +} + } // namespace Net } // namespace Msp -- 2.43.0