--- a/bash-completion.sh Fri Jul 29 18:03:49 2022 +0200
+++ b/bash-completion.sh Sat Jul 30 00:16:40 2022 +0200
@@ -29,26 +29,39 @@
"uds:///tmp/relpipe.socket"
)
- CONNECTION_OPTIONS=(
+ OPTIONS=(
+ "protocol"
"role"
"mode"
+ "host"
+ "port"
+ "path"
)
- CONNECTION_ROLES=(
+ PROTOCOLS=(
+ "tcp"
+ "udp"
+ "uds"
+ "sctp"
+ )
+
+ ROLES=(
"client"
"server"
)
- CONNECTION_MODES=(
+ MODES=(
"stream"
"datagram"
)
if [[ "$w1" == "--relation" && "x$w0" == "x" ]]; then COMPREPLY=("''")
elif [[ "$w1" == "--connection-string" ]]; then COMPREPLY=($(compgen -W "${CONNECTION_STRINGS[*]}" -- "$w0"))
- elif [[ "$w1" == "--connection-option" ]]; then COMPREPLY=($(compgen -W "${CONNECTION_OPTIONS[*]}" -- "$w0"))
- elif [[ "$w2" == "--connection-option" && "$w1" == "role" ]]; then COMPREPLY=($(compgen -W "${CONNECTION_ROLES[*]}" -- "$w0"))
- elif [[ "$w2" == "--connection-option" && "$w1" == "mode" ]]; then COMPREPLY=($(compgen -W "${CONNECTION_MODES[*]}" -- "$w0"))
+ elif [[ "$w1" == "--connection-option" ]]; then COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
+ elif [[ "$w2" == "--connection-option" && "$w1" == "protocol" ]]; then COMPREPLY=($(compgen -W "${PROTOCOLS[*]}" -- "$w0"))
+ elif [[ "$w2" == "--connection-option" && "$w1" == "role" ]]; then COMPREPLY=($(compgen -W "${ROLES[*]}" -- "$w0"))
+ elif [[ "$w2" == "--connection-option" && "$w1" == "mode" ]]; then COMPREPLY=($(compgen -W "${MODES[*]}" -- "$w0"))
+ elif [[ "$w2" == "--connection-option" && "$w1" == "path" ]]; then COMPREPLY=($(compgen -f -- "$w0"))
else
OPTIONS=(
"--relation"
--- a/src/CLIParser.h Fri Jul 29 18:03:49 2022 +0200
+++ b/src/CLIParser.h Sat Jul 30 00:16:40 2022 +0200
@@ -18,6 +18,7 @@
#include <vector>
#include <iostream>
+#include <codecvt>
#include <relpipe/common/type/typedefs.h>
#include <relpipe/cli/CLI.h>
@@ -31,6 +32,7 @@
class CLIParser {
private:
+ std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
relpipe::common::type::StringX readNext(const std::vector<relpipe::common::type::StringX>& arguments, int& i) {
if (i < arguments.size()) return arguments[i++];
@@ -46,6 +48,13 @@
else throw relpipe::cli::RelpipeCLIException(L"Unable to parse boolean value: " + value + L" (expecting true or false)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
}
+ void setIfMissing(std::vector<Configuration::SocketOption>& options, const std::string& name, const std::string& value) {
+ auto n = convertor.from_bytes(name);
+ auto v = convertor.from_bytes(value);
+ for (auto o : options) if (o.name == n) return;
+ options.push_back({n, v});
+ }
+
public:
static const relpipe::common::type::StringX OPTION_RELATION;
@@ -61,7 +70,8 @@
if (option == OPTION_RELATION) {
c.relation = readNext(arguments, i);
} else if (option == OPTION_CONNECTION_STRING) {
- c.connectionString = readNext(arguments, i);
+ auto connectionString = readNext(arguments, i);
+ // FIXME: parse connection string and convert it to options
} else if (option == OPTION_CONNECTION_OPTION) {
auto name = readNext(arguments, i);
auto value = readNext(arguments, i);
@@ -69,6 +79,13 @@
} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
}
+ using namespace options;
+ setIfMissing(c.options, OPTION_PROTOCOL, PROTOCOL_UDP);
+ setIfMissing(c.options, OPTION_ROLE, ROLE_CLIENT);
+ setIfMissing(c.options, OPTION_MODE, MODE_DATAGRAM);
+ setIfMissing(c.options, OPTION_HOST, "127.0.0.1");
+ setIfMissing(c.options, OPTION_PORT, "64000");
+
return c;
}
--- a/src/Configuration.h Fri Jul 29 18:03:49 2022 +0200
+++ b/src/Configuration.h Sat Jul 30 00:16:40 2022 +0200
@@ -21,11 +21,33 @@
#include <relpipe/common/type/typedefs.h>
+#include "Socket.h"
+
namespace relpipe {
namespace out {
namespace socket {
+namespace options {
+static const char OPTION_PROTOCOL[] = "protocol";
+static const char OPTION_ROLE[] = "role";
+static const char OPTION_MODE[] = "mode";
+static const char OPTION_HOST[] = "host";
+static const char OPTION_PORT[] = "port";
+static const char OPTION_PATH[] = "path";
+
+static const char PROTOCOL_TCP[] = "tcp";
+static const char PROTOCOL_UDP[] = "udp";
+static const char PROTOCOL_UDS[] = "uds";
+static const char PROTOCOL_SCTP[] = "sctp";
+
+static const char ROLE_CLIENT[] = "client";
+static const char ROLE_SERVER[] = "server";
+
+static const char MODE_STREAM[] = "stream";
+static const char MODE_DATAGRAM[] = "datagram";
+}
+
class Configuration {
public:
@@ -42,7 +64,6 @@
relpipe::common::type::StringX relation = L"socket";
- relpipe::common::type::StringX connectionString = L"udp://127.0.0.1:64000";
std::vector<SocketOption> options;
virtual ~Configuration() {
--- a/src/Socket.cpp Fri Jul 29 18:03:49 2022 +0200
+++ b/src/Socket.cpp Sat Jul 30 00:16:40 2022 +0200
@@ -27,19 +27,19 @@
#include <regex>
#include "Socket.h"
+#include "Configuration.h"
namespace relpipe {
namespace out {
namespace socket {
-static const char PROTOCOL_TCP[] = "tcp://";
-static const char PROTOCOL_UDP[] = "udp://";
-static const char PROTOCOL_UDS[] = "uds://";
-static const char PROTOCOL_SCTP[] = "sctp://";
-static const char PROTOCOL_TCP_LISTEN[] = "tcp-listen://";
-static const char PROTOCOL_UDP_LISTEN[] = "udp-listen://";
-static const char PROTOCOL_UDS_LISTEN[] = "uds-listen://";
-static const char PROTOCOL_SCTP_LISTEN[] = "sctp-listen://";
+using namespace relpipe::out::socket::options;
+
+static const std::string findOption(SocketOptions options, std::string name, bool required = false, const std::string defaultValue = "") {
+ for (auto o : options) if (o.name == name) return o.value;
+ if (required) throw std::invalid_argument("Option " + name + " is required but was not found");
+ else return defaultValue;
+}
class FD {
private:
@@ -58,27 +58,17 @@
}
};
-/**
- * @param connectionString
- * @return protocol, hostname or path, port (optional)
- */
-static std::tuple<std::string, std::string, uint16_t> parseConnectionString(const std::string& connectionString) {
- // TODO: support „:“ in domain socket paths?
- std::regex pattern("([^:]+)://([^:]+)(:([0-9]+))?");
-
-}
-
class UDPSocket : public Socket {
private:
struct sockaddr_in remoteAddress;
public:
- static std::shared_ptr<Socket> open(const std::string& connectionString) {
+ static std::shared_ptr<Socket> open(const SocketOptions& options) {
std::shared_ptr<UDPSocket> s = std::make_shared<UDPSocket>();
memset((char *) &s->remoteAddress, 0, sizeof (s->remoteAddress));
s->remoteAddress.sin_family = AF_INET;
- s->remoteAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // TODO: use getaddrinfo() instead (because of error -1 = 255.255.255.255)
- s->remoteAddress.sin_port = htons(1234);
+ s->remoteAddress.sin_addr.s_addr = inet_addr(findOption(options, OPTION_HOST, true).c_str()); // TODO: use getaddrinfo() instead (because of error -1 = 255.255.255.255)
+ s->remoteAddress.sin_port = htons(std::stoi(findOption(options, OPTION_PORT, true)));
return s;
}
@@ -94,33 +84,41 @@
};
-template<const char* protocol, typename SocketClass>
+template<const char* protocol, const char* role, const char* mode, typename SocketClass>
class TemplateSocketFactory : public SocketFactory {
public:
- bool canHandle(const std::string& connectionString) override {
- return connectionString.rfind(protocol, 0) == 0;
+ bool canHandle(const SocketOptions& options) override {
+ return findOption(options, OPTION_PROTOCOL) == protocol
+ && findOption(options, OPTION_ROLE) == role
+ && findOption(options, OPTION_MODE) == mode;
}
- std::shared_ptr<Socket> open(const std::string& connectionString) override {
- // TODO: pass string to constructor
- // TODO: return shared_ptr?
- return SocketClass::open(connectionString);
+ std::shared_ptr<Socket> open(const SocketOptions& options) override {
+ return SocketClass::open(options);
}
};
static std::vector<std::shared_ptr<SocketFactory>> factories
{
- // FIXME: different classes than TCPSocket
- std::make_shared<TemplateSocketFactory<PROTOCOL_TCP, UDPSocket >> (),
- std::make_shared<TemplateSocketFactory<PROTOCOL_UDP, UDPSocket >> (),
- std::make_shared<TemplateSocketFactory<PROTOCOL_UDS, UDPSocket >> (),
- std::make_shared<TemplateSocketFactory<PROTOCOL_SCTP, UDPSocket >> (),
+ // FIXME: different classes than UDPSocket
+ std::make_shared<TemplateSocketFactory<PROTOCOL_TCP, ROLE_CLIENT, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_TCP, ROLE_SERVER, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDP, ROLE_CLIENT, MODE_DATAGRAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDP, ROLE_SERVER, MODE_DATAGRAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_SCTP, ROLE_CLIENT, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_SCTP, ROLE_CLIENT, MODE_DATAGRAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_SCTP, ROLE_SERVER, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_SCTP, ROLE_SERVER, MODE_DATAGRAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDS, ROLE_CLIENT, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDS, ROLE_CLIENT, MODE_DATAGRAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDS, ROLE_SERVER, MODE_STREAM, UDPSocket >> (),
+ std::make_shared<TemplateSocketFactory<PROTOCOL_UDS, ROLE_SERVER, MODE_DATAGRAM, UDPSocket >> (),
};
-std::shared_ptr<SocketFactory> SocketFactory::find(const std::string& connectionString) {
- for (auto f : factories) if (f->canHandle(connectionString)) return f;
- throw std::logic_error("Unable to find a SocketFactory for connection string: " + connectionString);
+std::shared_ptr<SocketFactory> SocketFactory::find(const SocketOptions& options) {
+ for (auto f : factories) if (f->canHandle(options)) return f;
+ throw std::logic_error("Unable to find a SocketFactory"); // TODO: add relevant options?
}
--- a/src/Socket.h Fri Jul 29 18:03:49 2022 +0200
+++ b/src/Socket.h Sat Jul 30 00:16:40 2022 +0200
@@ -17,6 +17,7 @@
#pragma once
#include <string>
+#include <vector>
#include <cstring>
#include <unistd.h>
#include <stdexcept>
@@ -47,12 +48,14 @@
virtual ~SocketOption() = default;
};
+using SocketOptions = std::vector<SocketOption>;
+
class SocketFactory {
public:
virtual ~SocketFactory() = default;
- virtual bool canHandle(const std::string& connectionString) = 0;
- virtual std::shared_ptr<Socket> open(const std::string& connectionString) = 0;
- static std::shared_ptr<SocketFactory> find(const std::string& connectionString);
+ virtual bool canHandle(const SocketOptions& options) = 0;
+ virtual std::shared_ptr<Socket> open(const SocketOptions& options) = 0;
+ static std::shared_ptr<SocketFactory> find(const SocketOptions& options);
};
--- a/src/SocketHandler.h Fri Jul 29 18:03:49 2022 +0200
+++ b/src/SocketHandler.h Sat Jul 30 00:16:40 2022 +0200
@@ -56,8 +56,7 @@
} else {
std::vector<SocketOption> options;
for (auto o : configuration.options) options.push_back({convertor.to_bytes(o.name), convertor.to_bytes(o.value)});
- const std::string connectionString = convertor.to_bytes(configuration.connectionString);
- if (auto f = SocketFactory::find(connectionString)) socket = f->open(connectionString);
+ if (auto f = SocketFactory::find(options)) socket = f->open(options);
}
}