Skip to content

Commit 8cb6fe2

Browse files
authored
lsquic (#254)
Signed-off-by: turuslan <[email protected]>
1 parent 66764ac commit 8cb6fe2

27 files changed

+1504
-22
lines changed

cmake/Hunter/config.cmake

+6
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,9 @@
1414
# SHA1 1234567890abcdef1234567890abcdef12345678
1515
# CMAKE_ARGS "CMAKE_VARIABLE=value"
1616
# )
17+
18+
hunter_config(lsquic
19+
URL https://github.com/qdrvm/lsquic/archive/b79ff5c4be9936089d32dc1bc2dac70d54651e80.zip
20+
SHA1 d628f8d7ec68ec33d7f404fbb7b433c62980aa13
21+
KEEP_PACKAGE_SOURCES
22+
)

cmake/Hunter/init.cmake

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ set(
3131
include(${CMAKE_CURRENT_LIST_DIR}/HunterGate.cmake)
3232

3333
HunterGate(
34-
URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm21.zip
35-
SHA1 5b52ab9a309771f172ca609a46e26dde60a8edd7
34+
URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm22.zip
35+
SHA1 72b046fe42baefb968898e46f9442a7e9bc4e1b0
3636
LOCAL
3737
)

cmake/dependencies.cmake

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ find_package(OpenSSL CONFIG REQUIRED)
2121
hunter_add_package(libsecp256k1)
2222
find_package(libsecp256k1 CONFIG REQUIRED)
2323

24+
hunter_add_package(lsquic)
25+
find_package(lsquic CONFIG REQUIRED)
26+
2427
# https://developers.google.com/protocol-buffers/
2528
hunter_add_package(Protobuf)
2629
find_package(Protobuf CONFIG REQUIRED)

cmake/libp2pConfig.cmake.in

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
include(CMakeFindDependencyMacro)
44

55
find_dependency(Boost CONFIG REQUIRED random filesystem program_options)
6+
find_dependency(lsquic CONFIG REQUIRED)
67
find_dependency(OpenSSL CONFIG REQUIRED)
78
find_dependency(Protobuf CONFIG REQUIRED)
89
find_dependency(Threads)

housekeeping/copyright.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import itertools
5+
import re
6+
7+
re_cpp = re.compile(r"\.(h|hpp|c|cpp)$")
8+
9+
copyright_cmake = """\
10+
#
11+
# Copyright Quadrivium LLC
12+
# All Rights Reserved
13+
# SPDX-License-Identifier: Apache-2.0
14+
#
15+
"""
16+
copyright = """\
17+
/**
18+
* Copyright Quadrivium LLC
19+
* All Rights Reserved
20+
* SPDX-License-Identifier: Apache-2.0
21+
*/
22+
"""
23+
24+
for dir_, _, names in itertools.chain(*map(os.walk, ["include", "src", "test"])):
25+
for name in names:
26+
is_cmake = name == "CMakeLists.txt" or name.endswith(".cmake")
27+
is_cpp = re_cpp.search(name)
28+
if not is_cmake and not is_cpp:
29+
continue
30+
path = os.path.join(dir_, name)
31+
txt = open(path).read()
32+
txt0 = txt
33+
if not txt.endswith("\n"):
34+
print("EOL", path)
35+
txt = txt + "\n"
36+
if is_cmake and not txt.startswith(copyright_cmake):
37+
print("COPYRIGHT", path)
38+
txt = copyright_cmake + "\n" + txt
39+
if is_cpp and not txt.startswith(copyright):
40+
print("COPYRIGHT", path)
41+
txt = copyright + "\n" + txt
42+
if txt != txt0:
43+
print("WRITE", path)
44+
with open(path, "w") as f:
45+
f.write(txt)

include/libp2p/injector/network_injector.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <libp2p/security/tls.hpp>
4343
#include <libp2p/security/tls/ssl_context.hpp>
4444
#include <libp2p/transport/impl/upgrader_impl.hpp>
45+
#include <libp2p/transport/quic/transport.hpp>
4546
#include <libp2p/transport/tcp.hpp>
4647

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

349350
// user-defined overrides...
350351
std::forward<decltype(args)>(args)...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright Quadrivium LLC
3+
* All Rights Reserved
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <libp2p/common/metrics/instance_count.hpp>
10+
#include <libp2p/connection/capable_connection.hpp>
11+
12+
namespace boost::asio {
13+
class io_context;
14+
} // namespace boost::asio
15+
16+
namespace libp2p::transport::lsquic {
17+
class Engine;
18+
struct ConnCtx;
19+
} // namespace libp2p::transport::lsquic
20+
21+
namespace libp2p::transport {
22+
class QuicConnection : public connection::CapableConnection,
23+
public std::enable_shared_from_this<QuicConnection> {
24+
public:
25+
QuicConnection(std::shared_ptr<boost::asio::io_context> io_context,
26+
lsquic::ConnCtx *conn_ctx,
27+
bool initiator,
28+
Multiaddress local,
29+
Multiaddress remote,
30+
PeerId local_peer,
31+
PeerId peer,
32+
crypto::PublicKey key);
33+
~QuicConnection() override;
34+
35+
// clang-tidy cppcoreguidelines-special-member-functions
36+
QuicConnection(const QuicConnection &) = delete;
37+
void operator=(const QuicConnection &) = delete;
38+
QuicConnection(QuicConnection &&) = delete;
39+
void operator=(QuicConnection &&) = delete;
40+
41+
// Reader
42+
void read(BytesOut out, size_t bytes, ReadCallbackFunc cb) override;
43+
void readSome(BytesOut out, size_t bytes, ReadCallbackFunc cb) override;
44+
void deferReadCallback(outcome::result<size_t> res,
45+
ReadCallbackFunc cb) override;
46+
47+
// Writer
48+
void writeSome(BytesIn in, size_t bytes, WriteCallbackFunc cb) override;
49+
void deferWriteCallback(std::error_code ec, WriteCallbackFunc cb) override;
50+
51+
// Closeable
52+
bool isClosed() const override;
53+
outcome::result<void> close() override;
54+
55+
// LayerConnection
56+
bool isInitiator() const noexcept override;
57+
outcome::result<Multiaddress> remoteMultiaddr() override;
58+
outcome::result<Multiaddress> localMultiaddr() override;
59+
60+
// SecureConnection
61+
outcome::result<PeerId> localPeer() const override;
62+
outcome::result<PeerId> remotePeer() const override;
63+
outcome::result<crypto::PublicKey> remotePublicKey() const override;
64+
65+
// CapableConnection
66+
void start() override;
67+
void stop() override;
68+
void newStream(StreamHandlerFunc cb) override;
69+
outcome::result<std::shared_ptr<libp2p::connection::Stream>> newStream()
70+
override;
71+
void onStream(NewStreamHandlerFunc cb) override;
72+
73+
void onClose();
74+
auto &onStream() const {
75+
return on_stream_;
76+
}
77+
78+
private:
79+
std::shared_ptr<boost::asio::io_context> io_context_;
80+
lsquic::ConnCtx *conn_ctx_;
81+
bool initiator_;
82+
Multiaddress local_, remote_;
83+
PeerId local_peer_, peer_;
84+
crypto::PublicKey key_;
85+
NewStreamHandlerFunc on_stream_;
86+
87+
public:
88+
LIBP2P_METRICS_INSTANCE_COUNT_IF_ENABLED(libp2p::transport::QuicConnection);
89+
};
90+
} // namespace libp2p::transport
+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/**
2+
* Copyright Quadrivium LLC
3+
* All Rights Reserved
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <lsquic.h>
10+
#include <boost/asio/ip/udp.hpp>
11+
#include <boost/asio/steady_timer.hpp>
12+
#include <libp2p/multi/multiaddress.hpp>
13+
#include <libp2p/peer/peer_id.hpp>
14+
#include <memory>
15+
#include <optional>
16+
#include <qtils/bytes.hpp>
17+
#include <qtils/outcome.hpp>
18+
19+
namespace boost::asio {
20+
class io_context;
21+
} // namespace boost::asio
22+
23+
namespace boost::asio::ssl {
24+
class context;
25+
} // namespace boost::asio::ssl
26+
27+
namespace libp2p::connection {
28+
struct QuicStream;
29+
} // namespace libp2p::connection
30+
31+
namespace libp2p::crypto::marshaller {
32+
class KeyMarshaller;
33+
} // namespace libp2p::crypto::marshaller
34+
35+
namespace libp2p::muxer {
36+
struct MuxedConnectionConfig;
37+
} // namespace libp2p::muxer
38+
39+
namespace libp2p::transport {
40+
struct QuicConnection;
41+
} // namespace libp2p::transport
42+
43+
namespace libp2p::transport::lsquic {
44+
using connection::QuicStream;
45+
46+
struct Engine;
47+
struct ConnCtx;
48+
struct StreamCtx;
49+
50+
using OnConnect =
51+
std::function<void(outcome::result<std::shared_ptr<QuicConnection>>)>;
52+
/**
53+
* Connect operation arguments.
54+
*/
55+
struct Connecting {
56+
boost::asio::ip::udp::endpoint remote;
57+
PeerId peer;
58+
OnConnect cb;
59+
};
60+
/**
61+
* `lsquic_conn_ctx_t` for libp2p connection.
62+
*/
63+
struct ConnCtx {
64+
Engine *engine;
65+
lsquic_conn_t *ls_conn;
66+
std::optional<Connecting> connecting{};
67+
std::optional<std::shared_ptr<QuicStream>> new_stream{};
68+
std::weak_ptr<QuicConnection> conn{};
69+
};
70+
71+
/**
72+
* `lsquic_stream_ctx_t` for libp2p stream.
73+
*/
74+
struct StreamCtx {
75+
Engine *engine;
76+
lsquic_stream_t *ls_stream;
77+
std::weak_ptr<QuicStream> stream{};
78+
/**
79+
* Stream read operation arguments.
80+
*/
81+
struct Reading {
82+
BytesOut out;
83+
std::function<void(outcome::result<size_t>)> cb;
84+
};
85+
std::optional<Reading> reading{};
86+
};
87+
88+
using OnAccept = std::function<void(std::shared_ptr<QuicConnection>)>;
89+
90+
/**
91+
* libp2p wrapper and adapter for lsquic server/client socket.
92+
*/
93+
class Engine : public std::enable_shared_from_this<Engine> {
94+
public:
95+
Engine(std::shared_ptr<boost::asio::io_context> io_context,
96+
std::shared_ptr<boost::asio::ssl::context> ssl_context,
97+
const muxer::MuxedConnectionConfig &mux_config,
98+
PeerId local_peer,
99+
std::shared_ptr<crypto::marshaller::KeyMarshaller> key_codec,
100+
boost::asio::ip::udp::socket &&socket,
101+
bool client);
102+
~Engine();
103+
104+
// clang-tidy cppcoreguidelines-special-member-functions
105+
Engine(const Engine &) = delete;
106+
void operator=(const Engine &) = delete;
107+
Engine(Engine &&) = delete;
108+
void operator=(Engine &&) = delete;
109+
110+
auto &local() const {
111+
return local_;
112+
}
113+
void start();
114+
void connect(const boost::asio::ip::udp::endpoint &remote,
115+
const PeerId &peer,
116+
OnConnect cb);
117+
outcome::result<std::shared_ptr<QuicStream>> newStream(ConnCtx *conn_ctx);
118+
void onAccept(OnAccept cb) {
119+
on_accept_ = std::move(cb);
120+
}
121+
void process();
122+
123+
private:
124+
void readLoop();
125+
126+
std::shared_ptr<boost::asio::io_context> io_context_;
127+
std::shared_ptr<boost::asio::ssl::context> ssl_context_;
128+
PeerId local_peer_;
129+
std::shared_ptr<crypto::marshaller::KeyMarshaller> key_codec_;
130+
boost::asio::ip::udp::socket socket_;
131+
boost::asio::steady_timer timer_;
132+
boost::asio::ip::udp::endpoint socket_local_;
133+
Multiaddress local_;
134+
lsquic_engine_t *engine_ = nullptr;
135+
OnAccept on_accept_;
136+
bool started_ = false;
137+
std::optional<Connecting> connecting_;
138+
struct Reading {
139+
static constexpr size_t kMaxUdpPacketSize = 64 << 10;
140+
qtils::BytesN<kMaxUdpPacketSize> buf;
141+
boost::asio::ip::udp::endpoint remote;
142+
};
143+
Reading reading_;
144+
};
145+
} // namespace libp2p::transport::lsquic
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright Quadrivium LLC
3+
* All Rights Reserved
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <qtils/enum_error_code.hpp>
10+
11+
namespace libp2p {
12+
enum class QuicError {
13+
HANDSHAKE_FAILED,
14+
CONN_CLOSED,
15+
STREAM_CLOSED,
16+
TOO_MANY_STREAMS,
17+
CANT_CREATE_CONNECTION,
18+
CANT_OPEN_STREAM,
19+
};
20+
Q_ENUM_ERROR_CODE(QuicError) {
21+
using E = decltype(e);
22+
switch (e) {
23+
case E::HANDSHAKE_FAILED:
24+
return "HANDSHAKE_FAILED";
25+
case E::CONN_CLOSED:
26+
return "CONN_CLOSED";
27+
case E::STREAM_CLOSED:
28+
return "STREAM_CLOSED";
29+
case E::TOO_MANY_STREAMS:
30+
return "TOO_MANY_STREAMS";
31+
case E::CANT_CREATE_CONNECTION:
32+
return "CANT_CREATE_CONNECTION";
33+
case E::CANT_OPEN_STREAM:
34+
return "CANT_OPEN_STREAM";
35+
}
36+
abort();
37+
}
38+
} // namespace libp2p
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright Quadrivium LLC
3+
* All Rights Reserved
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#pragma once
8+
9+
#include <lsquic.h>
10+
#include <stdexcept>
11+
12+
namespace libp2p::transport {
13+
inline void lsquicInit() {
14+
static auto _ = [] {
15+
if (lsquic_global_init(LSQUIC_GLOBAL_CLIENT | LSQUIC_GLOBAL_SERVER)
16+
!= 0) {
17+
throw std::logic_error{"lsquic_global_init"};
18+
}
19+
return 0;
20+
}();
21+
}
22+
} // namespace libp2p::transport

0 commit comments

Comments
 (0)