mirror of
https://github.com/oxen-io/oxen-mq.git
synced 2023-12-13 21:00:31 +01:00
ad04c53c0e
The existing code was overly complicated by trying to track indices in the `connections` vector, which complication happening because things get removed from `connections` requiring all the internal index values to be updated. So we ended up with a connection ID inside the ConnectionID object, plus a map of those connection IDs to the `connections` index, and need a map back from indices to ConnectionIDs. Though this seems to work usually, I recently noticed an oxen-storage-server sending oxend requests on the wrong connection and so I suspect there is some rare edge cases here where a failed connection index might not be updated properly. This PR simplifies the whole thing by making getting rid of connection ids entirely and keeping the connections in a map (with connection ids that never change). This might end up being a little less efficient than the vector, but it's unlikely to matter and the added complexity isn't worth it.
96 lines
3.5 KiB
C++
96 lines
3.5 KiB
C++
#pragma once
|
|
#include "auth.h"
|
|
#include "bt_value.h"
|
|
#include <string_view>
|
|
#include <iosfwd>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
namespace oxenmq {
|
|
|
|
struct ConnectionID;
|
|
|
|
namespace detail {
|
|
template <typename... T>
|
|
bt_dict build_send(ConnectionID to, std::string_view cmd, T&&... opts);
|
|
}
|
|
|
|
/// Opaque data structure representing a connection which supports ==, !=, < and std::hash. For
|
|
/// connections to service node this is the service node pubkey (and you can pass a 32-byte string
|
|
/// anywhere a ConnectionID is called for). For non-SN remote connections you need to keep a copy
|
|
/// of the ConnectionID returned by connect_remote().
|
|
struct ConnectionID {
|
|
// Default construction; creates a ConnectionID with an invalid internal ID that will not match
|
|
// an actual connection.
|
|
ConnectionID() : ConnectionID(0) {}
|
|
// Construction from a service node pubkey
|
|
ConnectionID(std::string pubkey_) : id{SN_ID}, pk{std::move(pubkey_)} {
|
|
if (pk.size() != 32)
|
|
throw std::runtime_error{"Invalid pubkey: expected 32 bytes"};
|
|
}
|
|
// Construction from a service node pubkey
|
|
ConnectionID(std::string_view pubkey_) : ConnectionID(std::string{pubkey_}) {}
|
|
|
|
ConnectionID(const ConnectionID&) = default;
|
|
ConnectionID(ConnectionID&&) = default;
|
|
ConnectionID& operator=(const ConnectionID&) = default;
|
|
ConnectionID& operator=(ConnectionID&&) = default;
|
|
|
|
// Returns true if this is a ConnectionID (false for a default-constructed, invalid id)
|
|
explicit operator bool() const {
|
|
return id != 0;
|
|
}
|
|
|
|
// Two ConnectionIDs are equal if they are both SNs and have matching pubkeys, or they are both
|
|
// not SNs and have matching internal IDs and routes. (Pubkeys do not have to match for
|
|
// non-SNs).
|
|
bool operator==(const ConnectionID &o) const {
|
|
if (sn() && o.sn())
|
|
return pk == o.pk;
|
|
return id == o.id && route == o.route;
|
|
}
|
|
bool operator!=(const ConnectionID &o) const { return !(*this == o); }
|
|
bool operator<(const ConnectionID &o) const {
|
|
if (sn() && o.sn())
|
|
return pk < o.pk;
|
|
return id < o.id || (id == o.id && route < o.route);
|
|
}
|
|
|
|
// Returns true if this ConnectionID represents a SN connection
|
|
bool sn() const { return id == SN_ID; }
|
|
|
|
// Returns this connection's pubkey, if any. (Note that all curve connections have pubkeys, not
|
|
// only SNs).
|
|
const std::string& pubkey() const { return pk; }
|
|
|
|
// Returns a copy of the ConnectionID with the route set to empty.
|
|
ConnectionID unrouted() { return ConnectionID{id, pk, ""}; }
|
|
|
|
private:
|
|
ConnectionID(int64_t id) : id{id} {}
|
|
ConnectionID(int64_t id, std::string pubkey, std::string route = "")
|
|
: id{id}, pk{std::move(pubkey)}, route{std::move(route)} {}
|
|
|
|
constexpr static int64_t SN_ID = -1;
|
|
int64_t id = 0;
|
|
std::string pk;
|
|
std::string route;
|
|
friend class OxenMQ;
|
|
friend struct std::hash<ConnectionID>;
|
|
template <typename... T>
|
|
friend bt_dict detail::build_send(ConnectionID to, std::string_view cmd, T&&... opts);
|
|
friend std::ostream& operator<<(std::ostream& o, const ConnectionID& conn);
|
|
};
|
|
|
|
} // namespace oxenmq
|
|
namespace std {
|
|
template <> struct hash<oxenmq::ConnectionID> {
|
|
size_t operator()(const oxenmq::ConnectionID &c) const {
|
|
return c.sn() ? oxenmq::already_hashed{}(c.pk) :
|
|
std::hash<int64_t>{}(c.id) + std::hash<std::string>{}(c.route);
|
|
}
|
|
};
|
|
} // namespace std
|
|
|