Skip to content

Commit

Permalink
Interim checkin with basic address and protocol classes
Browse files Browse the repository at this point in the history
  • Loading branch information
0xg0nz0 committed Feb 25, 2024
1 parent 97fdbbe commit 157db19
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 33 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"cinttypes": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp"
"variant": "cpp",
"csignal": "cpp",
"source_location": "cpp"
}
}
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
endif()

# set up library dependencies
find_package(ada CONFIG REQUIRED)
find_package(libuv CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(unofficial-sodium CONFIG REQUIRED)

# these do not correctly support CMake
find_path(ADA_INCLUDE_DIR ada.h REQUIRED)
find_path(SODIUM_INCLUDE_DIR sodium.h REQUIRED)

# customize the builds of key networking components; WolfSSL is not
Expand Down Expand Up @@ -59,9 +61,12 @@ add_library(
sdk/client.cc
sdk/model.cc
sdk/serialization.cc
sdk/net/address.cc
sdk/net/protocol.cc
sdk/net/iggy.cc
)
target_compile_features(iggy PRIVATE cxx_std_17)
target_include_directories(iggy PRIVATE ${SODIUM_INCLUDE_DIR} ${USOCKETS_INCLUDE_DIR})
target_include_directories(iggy PRIVATE ${SODIUM_INCLUDE_DIR} ${ADA_INCLUDE_DIR})

if (BUILD_TESTS)
add_subdirectory(tests)
Expand Down
35 changes: 4 additions & 31 deletions sdk/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,10 @@
#include <stdexcept>
#include <string>
#include "model.h"
#include "net/iggy.h"
#include "net/transport.h"

namespace iggy {
namespace transport {

/**
* @enum Transport
* @brief Available network transports for the Iggy server. Not all currently supported by the C++ client.
*/
enum Transport {
/**
* @brief Modern networking protocol from Google built on top of UDP.
*
* @ref [Wikipedia](https://en.wikipedia.org/wiki/QUIC)
*/
QUIC,

/**
* @brief Classic HTTP REST encoded as JSON. Not recommended for high performance applications.
*/
HTTP,

/**
* @brief Binary protocol over TCP/IP. This is the default transport.
*/
TCP
};
}; // namespace transport
namespace client {

/**
Expand All @@ -52,10 +29,6 @@ class Credentials {
~Credentials() { sodium_memzero(&password[0], password.size()); }
};

const unsigned short DEFAULT_HTTP_PORT = 3000;
const unsigned short DEFAULT_TCP_PORT = 8090;
const unsigned short DEFAULT_QUIC_PORT = 8080;

/**
* @struct Options
* @brief A struct to hold various options.
Expand All @@ -73,12 +46,12 @@ struct Options {
/**
* @brief The port the Iggy server is listening on; default depends on transport. Defaults to the DEFAULT_TCP_PORT.
*/
unsigned short port = DEFAULT_TCP_PORT;
unsigned short port = iggy::net::DEFAULT_TCP_PORT;

/**
* @brief The network transport to use when connecting to the server. Defaults to TCP.
*/
iggy::transport::Transport transport = iggy::transport::Transport::TCP;
iggy::net::transport::Transport transport = iggy::net::transport::Transport::TCP;

/**
* @brief The user credentials to use when connecting to the server.
Expand Down
34 changes: 34 additions & 0 deletions sdk/net/address.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "address.h"

const iggy::net::protocol::ProtocolDefinition& iggy::net::address::LogicalAddress::getProtocolDefinition() const {
return this->protocolProvider->getProtocolDefinition(this->getProtocol());
}

iggy::net::address::LogicalAddress::LogicalAddress(const std::string& url, const iggy::net::protocol::ProtocolProvider* protocolProvider) {
auto parse_result = ada::parse<ada::url>(url);
if (!parse_result) {
throw std::invalid_argument("Invalid URL: " + url);
}
auto value = parse_result.value();
auto protocol = value.get_protocol();
if (!protocolProvider->isSupported(protocol)) {
throw std::invalid_argument("Unsupported protocol: " + protocol);
}
this->url = value;
this->protocolProvider = protocolProvider;
}

const unsigned short iggy::net::address::LogicalAddress::getPort() const {
if (url.get_port().empty()) {
return this->getProtocolDefinition().getDefaultPort();
} else {
int port = std::stoi(url.get_port());

// this should not happen if ada::parse is working correctly
if (port < 0 || port > 65535) {
throw std::out_of_range("Port number out of range");
}

return port;
}
}
47 changes: 47 additions & 0 deletions sdk/net/address.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <ada.h>
#include <vector>
#include "protocol.h"
#include "transport.h"

namespace iggy {
namespace net {
namespace address {

/***
* @brief Logical address used in configuration and API to specify desired transport in a compact way, e.g. iggy:quic://localhost:8080.
*/
class LogicalAddress {
private:
ada::url url;
const iggy::net::protocol::ProtocolProvider* protocolProvider;

const iggy::net::protocol::ProtocolDefinition& getProtocolDefinition() const;
public:
/**
* @brief Construct a logical address from a URL.
* @param url URL to parse.
* @param protocolProvider Context object providing supported protocols and default ports.
* @throws std::invalid_argument if the URL is invalid or the protocol is unknown.
*/
LogicalAddress(const std::string& url, const iggy::net::protocol::ProtocolProvider* protocolProvider);

/**
* @brief Gets the protocol; you have a guarantee that it will be one of the supported protocols from ProtocolProvider.
*/
const std::string getProtocol() const noexcept { return url.get_protocol(); }

/**
* @brief Gets the hostname to connect to or raw IP address.
*/
const std::string getHost() const noexcept { return url.get_hostname(); }

/**
* @brief Gets the port to connect to; protocol default port will be substituted if not specified.
*/
const unsigned short getPort() const;
};
}; // namespace address
}; // namespace net
}; // namespace iggy
25 changes: 25 additions & 0 deletions sdk/net/iggy.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "iggy.h"

iggy::net::IggyProtocolProvider::IggyProtocolProvider() {
for (const auto& protocol : this->supportedProtocols) {
this->supportedProtocolLookup[protocol.getName()] = protocol;
}
}

const std::vector<iggy::net::protocol::ProtocolDefinition>& iggy::net::IggyProtocolProvider::getSupportedProtocols() const {
return this->supportedProtocols;
}

const iggy::net::protocol::ProtocolDefinition& iggy::net::IggyProtocolProvider::getProtocolDefinition(const std::string& protocol) const {
auto normalizedName = iggy::net::protocol::normalizeProtocolName(protocol);
auto it = this->supportedProtocolLookup.find(normalizedName);
if (it != this->supportedProtocolLookup.end()) {
return it->second;
} else {
throw std::invalid_argument("Unsupported protocol: " + protocol);
}
}

const bool iggy::net::IggyProtocolProvider::isSupported(const std::string& protocol) const {
return this->supportedProtocolLookup.count(iggy::net::protocol::normalizeProtocolName(protocol)) > 0;
}
45 changes: 45 additions & 0 deletions sdk/net/iggy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once
#include <map>
#include <vector>
#include "address.h"

namespace iggy {
namespace net {

const unsigned short DEFAULT_HTTP_PORT = 3000;
const unsigned short DEFAULT_TCP_PORT = 8090;
const unsigned short DEFAULT_QUIC_PORT = 8080;

const std::string QUIC_PROTOCOL = "iggy:quic";
const std::string TCP_PROTOCOL = "iggy:tcp";
const std::string TCP_TLS_PROTOCOL = "iggy:tcp+tls";
const std::string HTTP_PROTOCOL = "iggy:http";
const std::string HTTP_TLS_PROTOCOL = "iggy:http+tls";

using iggy::net::protocol::MessageEncoding;
using iggy::net::protocol::ProtocolDefinition;

/**
* @brief Provider that declares support and offers defaults for all Iggy C++ supported protocols.
*
* At this time we support iggy:quic, iggy:tcp (binary messaging) and iggy:http (with JSON messaging).
*/
class IggyProtocolProvider : iggy::net::protocol::ProtocolProvider {
private:
std::vector<ProtocolDefinition> supportedProtocols = {
ProtocolDefinition(QUIC_PROTOCOL, DEFAULT_QUIC_PORT, iggy::net::transport::QUIC, true, MessageEncoding::BINARY),
ProtocolDefinition(TCP_PROTOCOL, DEFAULT_TCP_PORT, iggy::net::transport::TCP, false, MessageEncoding::BINARY),
ProtocolDefinition(TCP_TLS_PROTOCOL, DEFAULT_TCP_PORT, iggy::net::transport::TCP, true, MessageEncoding::BINARY),
ProtocolDefinition(HTTP_PROTOCOL, DEFAULT_HTTP_PORT, iggy::net::transport::HTTP, false, MessageEncoding::TEXT),
ProtocolDefinition(HTTP_TLS_PROTOCOL, DEFAULT_HTTP_PORT, iggy::net::transport::HTTP, true, MessageEncoding::TEXT)};
std::map<std::string, ProtocolDefinition> supportedProtocolLookup;

public:
IggyProtocolProvider();
const std::vector<ProtocolDefinition>& getSupportedProtocols() const override;
const ProtocolDefinition& getProtocolDefinition(const std::string& protocol) const override;
const bool isSupported(const std::string& protocol) const override;
};

}; // namespace net
}; // namespace iggy
24 changes: 24 additions & 0 deletions sdk/net/protocol.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "address.h"
#include "protocol.h"

iggy::net::address::LogicalAddress iggy::net::protocol::ProtocolProvider::createAddress(const std::string& url) const {
return iggy::net::address::LogicalAddress(url, this);
}

const std::string iggy::net::protocol::normalizeProtocolName(const std::string& protocol) {
// convert to lowercase
std::string lowerStr = protocol;
std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);

// trim whitespace from the start
auto start = lowerStr.find_first_not_of(" \t\n\r\f\v");
if (start == std::string::npos) {
throw std::invalid_argument("Protocol name cannot be empty");
}

// trim whitespace from the end
auto end = lowerStr.find_last_not_of(" \t\n\r\f\v");

// return the trimmed, lowercase string
return lowerStr.substr(start, end - start + 1);
}
90 changes: 90 additions & 0 deletions sdk/net/protocol.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#pragma once

#include <string>
#include <vector>
#include "transport.h"

namespace iggy {
namespace net {
namespace address {
class LogicalAddress;
};

namespace protocol {

/**
* @brief Enumerates the supported message encodings.
*/
enum MessageEncoding { BINARY = 0, TEXT = 1 };

/**
* @brief Normalizes the protocol name to a canonical form.
*/
const std::string normalizeProtocolName(const std::string& protocol);


/**
* @brief Metadata about a protocol including its default port, transport, TLS support and message encoding.
*/
class ProtocolDefinition {
private:
std::string name;
unsigned short defaultPort;
iggy::net::transport::Transport transport;
bool tlsSupported;
MessageEncoding messageEncoding;

public:
ProtocolDefinition(const std::string& name,
unsigned short defaultPort,
iggy::net::transport::Transport transport,
bool tlsSupported,
MessageEncoding messageEncoding)
: name(iggy::net::protocol::normalizeProtocolName(name))
, defaultPort(defaultPort)
, transport(transport)
, tlsSupported(tlsSupported)
, messageEncoding(messageEncoding) {}

ProtocolDefinition() = default;
ProtocolDefinition(const ProtocolDefinition& other) = default;

const std::string& getName() const { return name; }
unsigned short getDefaultPort() const { return defaultPort; }
iggy::net::transport::Transport getTransport() const { return transport; };
bool isTlsSupported() const { return tlsSupported; }
MessageEncoding getMessageEncoding() const { return messageEncoding; }
};

/**
* @brief Interface to plug in library-specific information on supported protocols.
*/
class ProtocolProvider {
public:
ProtocolProvider() = default;
virtual ~ProtocolProvider() = default;

/**
* @brief Factory method to create a logical address from a URL.
*/
iggy::net::address::LogicalAddress createAddress(const std::string& url) const;

/**
* @brief Enumerates all the supported protocols in the provider.
*/
virtual const std::vector<ProtocolDefinition>& getSupportedProtocols() const = 0;

/**
* @brief Given a normalized protocol name returns the definition with protocol metadata.
*/
virtual const ProtocolDefinition& getProtocolDefinition(const std::string& protocol) const;

/**
* @brief Tests whether the given protocol is supported by this provider.
*/
virtual const bool isSupported(const std::string& protocol) const;
};

}; // namespace protocol
}; // namespace net
}; // namespace iggy
1 change: 1 addition & 0 deletions sdk/net/quic/address.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma once
1 change: 1 addition & 0 deletions sdk/net/quic/conn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma once
1 change: 1 addition & 0 deletions sdk/net/quic/stream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma once
1 change: 1 addition & 0 deletions sdk/net/quic/tls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#pragma once
Loading

0 comments on commit 157db19

Please sign in to comment.