mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
Fix/refactor stream closing
Make stream closing with expiring connections work better. Fixes an issue where the stream's uv_async could outlive the stream and/or connection and segfault.
This commit is contained in:
parent
60c813d306
commit
ac34835c12
|
@ -537,6 +537,8 @@ namespace llarp::quic
|
|||
retransmit_timer->stop();
|
||||
retransmit_timer->close();
|
||||
}
|
||||
for (auto& [id, str] : streams)
|
||||
str->hard_close();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -820,7 +822,7 @@ namespace llarp::quic
|
|||
" data callback raised exception (",
|
||||
e.what(),
|
||||
"); closing stream with app code ",
|
||||
STREAM_EXCEPTION_ERROR_CODE);
|
||||
STREAM_ERROR_EXCEPTION);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -828,11 +830,11 @@ namespace llarp::quic
|
|||
"Stream ",
|
||||
str->id(),
|
||||
" data callback raised an unknown exception; closing stream with app code ",
|
||||
STREAM_EXCEPTION_ERROR_CODE);
|
||||
STREAM_ERROR_EXCEPTION);
|
||||
}
|
||||
if (!good)
|
||||
{
|
||||
str->close(STREAM_EXCEPTION_ERROR_CODE);
|
||||
str->close(STREAM_ERROR_EXCEPTION);
|
||||
return NGTCP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,11 @@ namespace llarp::quic
|
|||
: Stream{conn, nullptr, nullptr, buffer_size, std::move(id)}
|
||||
{}
|
||||
|
||||
Stream::~Stream()
|
||||
{
|
||||
hard_close();
|
||||
}
|
||||
|
||||
void
|
||||
Stream::set_buffer_size(size_t size)
|
||||
{
|
||||
|
@ -257,6 +262,8 @@ namespace llarp::quic
|
|||
void
|
||||
Stream::handle_unblocked()
|
||||
{
|
||||
if (is_closing)
|
||||
return;
|
||||
if (buffer.empty())
|
||||
{
|
||||
while (!unblocked_callbacks.empty() && unblocked_callbacks.front()(*this))
|
||||
|
@ -281,7 +288,8 @@ namespace llarp::quic
|
|||
void
|
||||
Stream::available_ready()
|
||||
{
|
||||
avail_trigger->send();
|
||||
if (avail_trigger)
|
||||
avail_trigger->send();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -322,6 +330,19 @@ namespace llarp::quic
|
|||
conn.io_ready();
|
||||
}
|
||||
|
||||
void
|
||||
Stream::hard_close()
|
||||
{
|
||||
if (avail_trigger)
|
||||
{
|
||||
avail_trigger->close();
|
||||
avail_trigger.reset();
|
||||
}
|
||||
if (!is_closing && close_callback)
|
||||
close_callback(*this, STREAM_ERROR_CONNECTION_EXPIRED);
|
||||
is_closing = is_shutdown = true;
|
||||
}
|
||||
|
||||
void
|
||||
Stream::data(std::shared_ptr<void> data)
|
||||
{
|
||||
|
|
|
@ -73,7 +73,11 @@ namespace llarp::quic
|
|||
};
|
||||
|
||||
// Application error code we close with if the data handle throws
|
||||
constexpr uint64_t STREAM_EXCEPTION_ERROR_CODE = (1ULL << 62) - 2;
|
||||
inline constexpr uint64_t STREAM_ERROR_EXCEPTION = (1ULL << 62) - 2;
|
||||
|
||||
// Error code we send to a stream close callback if the stream's connection expires; this is *not*
|
||||
// sent over quic, hence using a value >= 2^62 (quic's maximum serializable integer).
|
||||
inline constexpr uint64_t STREAM_ERROR_CONNECTION_EXPIRED = (1ULL << 62) + 1;
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, const StreamID& s);
|
||||
|
@ -266,6 +270,8 @@ namespace llarp::quic
|
|||
return conn;
|
||||
}
|
||||
|
||||
~Stream();
|
||||
|
||||
private:
|
||||
friend class Connection;
|
||||
|
||||
|
@ -310,6 +316,11 @@ namespace llarp::quic
|
|||
void
|
||||
wrote(size_t bytes);
|
||||
|
||||
// Called by the owning Connection to do a "hard" close of a stream during Connection
|
||||
// destruction: unlike a regular close this doesn't try to transmit a close over the wire (which
|
||||
// won't work since the Connection is dead), it just fires the close callback and cleans up.
|
||||
void hard_close();
|
||||
|
||||
// ngtcp2 stream_id, assigned during stream creation
|
||||
StreamID stream_id{-1};
|
||||
|
||||
|
|
|
@ -78,6 +78,11 @@ namespace llarp::quic
|
|||
}
|
||||
}
|
||||
|
||||
void close_tcp_pair(quic::Stream& st, std::optional<uint64_t> /*errcode*/) {
|
||||
if (auto tcp = st.data<uvw::TCPHandle>())
|
||||
tcp->close();
|
||||
};
|
||||
|
||||
// Creates a new tcp handle that forwards incoming data/errors/closes into appropriate actions
|
||||
// on the given quic stream.
|
||||
void
|
||||
|
@ -124,6 +129,7 @@ namespace llarp::quic
|
|||
});
|
||||
tcp.on<uvw::DataEvent>(on_outgoing_data);
|
||||
stream.data_callback = on_incoming_data;
|
||||
stream.close_callback = close_tcp_pair;
|
||||
}
|
||||
// This initial data handler is responsible for pulling off the initial stream data that comes
|
||||
// back, confirming that the tunnel is opened on the other end. Currently this is a null byte
|
||||
|
@ -236,12 +242,7 @@ namespace llarp::quic
|
|||
|
||||
server_ = std::make_unique<Server>(service_endpoint_);
|
||||
server_->stream_open_callback = [this](Stream& stream, uint16_t port) -> bool {
|
||||
stream.close_callback = [](quic::Stream& st,
|
||||
[[maybe_unused]] std::optional<uint64_t> errcode) {
|
||||
auto tcp = st.data<uvw::TCPHandle>();
|
||||
if (tcp)
|
||||
tcp->close();
|
||||
};
|
||||
stream.close_callback = close_tcp_pair;
|
||||
|
||||
auto& conn = stream.get_connection();
|
||||
auto remote = service_endpoint_.GetEndpointWithConvoTag(conn.path.remote);
|
||||
|
|
Loading…
Reference in a new issue