Skip to content

Commit

Permalink
lsquic (#254)
Browse files Browse the repository at this point in the history
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
  • Loading branch information
turuslan authored Oct 5, 2024
1 parent 66764ac commit 8cb6fe2
Show file tree
Hide file tree
Showing 27 changed files with 1,504 additions and 22 deletions.
6 changes: 6 additions & 0 deletions cmake/Hunter/config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@
# SHA1 1234567890abcdef1234567890abcdef12345678
# CMAKE_ARGS "CMAKE_VARIABLE=value"
# )

hunter_config(lsquic
URL https://github.com/qdrvm/lsquic/archive/b79ff5c4be9936089d32dc1bc2dac70d54651e80.zip
SHA1 d628f8d7ec68ec33d7f404fbb7b433c62980aa13
KEEP_PACKAGE_SOURCES
)
4 changes: 2 additions & 2 deletions cmake/Hunter/init.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ set(
include(${CMAKE_CURRENT_LIST_DIR}/HunterGate.cmake)

HunterGate(
URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm21.zip
SHA1 5b52ab9a309771f172ca609a46e26dde60a8edd7
URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm22.zip
SHA1 72b046fe42baefb968898e46f9442a7e9bc4e1b0
LOCAL
)
3 changes: 3 additions & 0 deletions cmake/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ find_package(OpenSSL CONFIG REQUIRED)
hunter_add_package(libsecp256k1)
find_package(libsecp256k1 CONFIG REQUIRED)

hunter_add_package(lsquic)
find_package(lsquic CONFIG REQUIRED)

# https://developers.google.com/protocol-buffers/
hunter_add_package(Protobuf)
find_package(Protobuf CONFIG REQUIRED)
Expand Down
1 change: 1 addition & 0 deletions cmake/libp2pConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
include(CMakeFindDependencyMacro)

find_dependency(Boost CONFIG REQUIRED random filesystem program_options)
find_dependency(lsquic CONFIG REQUIRED)
find_dependency(OpenSSL CONFIG REQUIRED)
find_dependency(Protobuf CONFIG REQUIRED)
find_dependency(Threads)
Expand Down
45 changes: 45 additions & 0 deletions housekeeping/copyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

import os
import itertools
import re

re_cpp = re.compile(r"\.(h|hpp|c|cpp)$")

copyright_cmake = """\
#
# Copyright Quadrivium LLC
# All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
"""
copyright = """\
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/
"""

for dir_, _, names in itertools.chain(*map(os.walk, ["include", "src", "test"])):
for name in names:
is_cmake = name == "CMakeLists.txt" or name.endswith(".cmake")
is_cpp = re_cpp.search(name)
if not is_cmake and not is_cpp:
continue
path = os.path.join(dir_, name)
txt = open(path).read()
txt0 = txt
if not txt.endswith("\n"):
print("EOL", path)
txt = txt + "\n"
if is_cmake and not txt.startswith(copyright_cmake):
print("COPYRIGHT", path)
txt = copyright_cmake + "\n" + txt
if is_cpp and not txt.startswith(copyright):
print("COPYRIGHT", path)
txt = copyright + "\n" + txt
if txt != txt0:
print("WRITE", path)
with open(path, "w") as f:
f.write(txt)
3 changes: 2 additions & 1 deletion include/libp2p/injector/network_injector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <libp2p/security/tls.hpp>
#include <libp2p/security/tls/ssl_context.hpp>
#include <libp2p/transport/impl/upgrader_impl.hpp>
#include <libp2p/transport/quic/transport.hpp>
#include <libp2p/transport/tcp.hpp>

// clang-format off
Expand Down Expand Up @@ -344,7 +345,7 @@ namespace libp2p::injector {
di::bind<layer::LayerAdaptor *[]>().template to<layer::WsAdaptor, layer::WssAdaptor>(), // NOLINT
di::bind<security::SecurityAdaptor *[]>().template to<security::Plaintext, security::Secio, security::Noise, security::TlsAdaptor>(), // NOLINT
di::bind<muxer::MuxerAdaptor *[]>().template to<muxer::Yamux, muxer::Mplex>(), // NOLINT
di::bind<transport::TransportAdaptor *[]>().template to<transport::TcpTransport>(), // NOLINT
di::bind<transport::TransportAdaptor *[]>().template to<transport::TcpTransport, transport::QuicTransport>(), // NOLINT

// user-defined overrides...
std::forward<decltype(args)>(args)...
Expand Down
90 changes: 90 additions & 0 deletions include/libp2p/transport/quic/connection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <libp2p/common/metrics/instance_count.hpp>
#include <libp2p/connection/capable_connection.hpp>

namespace boost::asio {
class io_context;
} // namespace boost::asio

namespace libp2p::transport::lsquic {
class Engine;
struct ConnCtx;
} // namespace libp2p::transport::lsquic

namespace libp2p::transport {
class QuicConnection : public connection::CapableConnection,
public std::enable_shared_from_this<QuicConnection> {
public:
QuicConnection(std::shared_ptr<boost::asio::io_context> io_context,
lsquic::ConnCtx *conn_ctx,
bool initiator,
Multiaddress local,
Multiaddress remote,
PeerId local_peer,
PeerId peer,
crypto::PublicKey key);
~QuicConnection() override;

// clang-tidy cppcoreguidelines-special-member-functions
QuicConnection(const QuicConnection &) = delete;
void operator=(const QuicConnection &) = delete;
QuicConnection(QuicConnection &&) = delete;
void operator=(QuicConnection &&) = delete;

// Reader
void read(BytesOut out, size_t bytes, ReadCallbackFunc cb) override;
void readSome(BytesOut out, size_t bytes, ReadCallbackFunc cb) override;
void deferReadCallback(outcome::result<size_t> res,
ReadCallbackFunc cb) override;

// Writer
void writeSome(BytesIn in, size_t bytes, WriteCallbackFunc cb) override;
void deferWriteCallback(std::error_code ec, WriteCallbackFunc cb) override;

// Closeable
bool isClosed() const override;
outcome::result<void> close() override;

// LayerConnection
bool isInitiator() const noexcept override;
outcome::result<Multiaddress> remoteMultiaddr() override;
outcome::result<Multiaddress> localMultiaddr() override;

// SecureConnection
outcome::result<PeerId> localPeer() const override;
outcome::result<PeerId> remotePeer() const override;
outcome::result<crypto::PublicKey> remotePublicKey() const override;

// CapableConnection
void start() override;
void stop() override;
void newStream(StreamHandlerFunc cb) override;
outcome::result<std::shared_ptr<libp2p::connection::Stream>> newStream()
override;
void onStream(NewStreamHandlerFunc cb) override;

void onClose();
auto &onStream() const {
return on_stream_;
}

private:
std::shared_ptr<boost::asio::io_context> io_context_;
lsquic::ConnCtx *conn_ctx_;
bool initiator_;
Multiaddress local_, remote_;
PeerId local_peer_, peer_;
crypto::PublicKey key_;
NewStreamHandlerFunc on_stream_;

public:
LIBP2P_METRICS_INSTANCE_COUNT_IF_ENABLED(libp2p::transport::QuicConnection);
};
} // namespace libp2p::transport
145 changes: 145 additions & 0 deletions include/libp2p/transport/quic/engine.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <lsquic.h>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <libp2p/multi/multiaddress.hpp>
#include <libp2p/peer/peer_id.hpp>
#include <memory>
#include <optional>
#include <qtils/bytes.hpp>
#include <qtils/outcome.hpp>

namespace boost::asio {
class io_context;
} // namespace boost::asio

namespace boost::asio::ssl {
class context;
} // namespace boost::asio::ssl

namespace libp2p::connection {
struct QuicStream;
} // namespace libp2p::connection

namespace libp2p::crypto::marshaller {
class KeyMarshaller;
} // namespace libp2p::crypto::marshaller

namespace libp2p::muxer {
struct MuxedConnectionConfig;
} // namespace libp2p::muxer

namespace libp2p::transport {
struct QuicConnection;
} // namespace libp2p::transport

namespace libp2p::transport::lsquic {
using connection::QuicStream;

struct Engine;
struct ConnCtx;
struct StreamCtx;

using OnConnect =
std::function<void(outcome::result<std::shared_ptr<QuicConnection>>)>;
/**
* Connect operation arguments.
*/
struct Connecting {
boost::asio::ip::udp::endpoint remote;
PeerId peer;
OnConnect cb;
};
/**
* `lsquic_conn_ctx_t` for libp2p connection.
*/
struct ConnCtx {
Engine *engine;
lsquic_conn_t *ls_conn;
std::optional<Connecting> connecting{};
std::optional<std::shared_ptr<QuicStream>> new_stream{};
std::weak_ptr<QuicConnection> conn{};
};

/**
* `lsquic_stream_ctx_t` for libp2p stream.
*/
struct StreamCtx {
Engine *engine;
lsquic_stream_t *ls_stream;
std::weak_ptr<QuicStream> stream{};
/**
* Stream read operation arguments.
*/
struct Reading {
BytesOut out;
std::function<void(outcome::result<size_t>)> cb;
};
std::optional<Reading> reading{};
};

using OnAccept = std::function<void(std::shared_ptr<QuicConnection>)>;

/**
* libp2p wrapper and adapter for lsquic server/client socket.
*/
class Engine : public std::enable_shared_from_this<Engine> {
public:
Engine(std::shared_ptr<boost::asio::io_context> io_context,
std::shared_ptr<boost::asio::ssl::context> ssl_context,
const muxer::MuxedConnectionConfig &mux_config,
PeerId local_peer,
std::shared_ptr<crypto::marshaller::KeyMarshaller> key_codec,
boost::asio::ip::udp::socket &&socket,
bool client);
~Engine();

// clang-tidy cppcoreguidelines-special-member-functions
Engine(const Engine &) = delete;
void operator=(const Engine &) = delete;
Engine(Engine &&) = delete;
void operator=(Engine &&) = delete;

auto &local() const {
return local_;
}
void start();
void connect(const boost::asio::ip::udp::endpoint &remote,
const PeerId &peer,
OnConnect cb);
outcome::result<std::shared_ptr<QuicStream>> newStream(ConnCtx *conn_ctx);
void onAccept(OnAccept cb) {
on_accept_ = std::move(cb);
}
void process();

private:
void readLoop();

std::shared_ptr<boost::asio::io_context> io_context_;
std::shared_ptr<boost::asio::ssl::context> ssl_context_;
PeerId local_peer_;
std::shared_ptr<crypto::marshaller::KeyMarshaller> key_codec_;
boost::asio::ip::udp::socket socket_;
boost::asio::steady_timer timer_;
boost::asio::ip::udp::endpoint socket_local_;
Multiaddress local_;
lsquic_engine_t *engine_ = nullptr;
OnAccept on_accept_;
bool started_ = false;
std::optional<Connecting> connecting_;
struct Reading {
static constexpr size_t kMaxUdpPacketSize = 64 << 10;
qtils::BytesN<kMaxUdpPacketSize> buf;
boost::asio::ip::udp::endpoint remote;
};
Reading reading_;
};
} // namespace libp2p::transport::lsquic
38 changes: 38 additions & 0 deletions include/libp2p/transport/quic/error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <qtils/enum_error_code.hpp>

namespace libp2p {
enum class QuicError {
HANDSHAKE_FAILED,
CONN_CLOSED,
STREAM_CLOSED,
TOO_MANY_STREAMS,
CANT_CREATE_CONNECTION,
CANT_OPEN_STREAM,
};
Q_ENUM_ERROR_CODE(QuicError) {
using E = decltype(e);
switch (e) {
case E::HANDSHAKE_FAILED:
return "HANDSHAKE_FAILED";
case E::CONN_CLOSED:
return "CONN_CLOSED";
case E::STREAM_CLOSED:
return "STREAM_CLOSED";
case E::TOO_MANY_STREAMS:
return "TOO_MANY_STREAMS";
case E::CANT_CREATE_CONNECTION:
return "CANT_CREATE_CONNECTION";
case E::CANT_OPEN_STREAM:
return "CANT_OPEN_STREAM";
}
abort();
}
} // namespace libp2p
22 changes: 22 additions & 0 deletions include/libp2p/transport/quic/init.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <lsquic.h>
#include <stdexcept>

namespace libp2p::transport {
inline void lsquicInit() {
static auto _ = [] {
if (lsquic_global_init(LSQUIC_GLOBAL_CLIENT | LSQUIC_GLOBAL_SERVER)
!= 0) {
throw std::logic_error{"lsquic_global_init"};
}
return 0;
}();
}
} // namespace libp2p::transport
Loading

0 comments on commit 8cb6fe2

Please sign in to comment.