feat(listener): ListenerQUIC::Port() reads back the port it bound
Port 0 asks msquic for an ephemeral port, but nothing read that port
back, so a QUIC server had to pick a fixed one and hope nothing else held
it, and this suite has to hand every QUIC test a port of its own.
ListenerStart writes the port of the UDP socket it bound into the
listener's local address; the constructor now reads it with
QUIC_PARAM_LISTENER_LOCAL_ADDRESS right after the start, and Port()
returns it. It is valid as soon as the constructor returns and still
after Stop(), and 0 on a moved-from listener. ListenerTCP has had the
same since d6f8f4e.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ee938c98e
commit
7dace8bb03
2 changed files with 31 additions and 0 deletions
|
|
@ -150,6 +150,10 @@ struct ListenerQUIC::Impl {
|
||||||
HQUIC listener = nullptr;
|
HQUIC listener = nullptr;
|
||||||
HQUIC configuration = nullptr;
|
HQUIC configuration = nullptr;
|
||||||
std::vector<std::uint8_t> alpnBuf;
|
std::vector<std::uint8_t> alpnBuf;
|
||||||
|
// The port msquic bound, read back once ListenerStart returned: a port
|
||||||
|
// of 0 asks msquic for an ephemeral one, and only the listener knows
|
||||||
|
// which it got.
|
||||||
|
std::uint16_t port = 0;
|
||||||
|
|
||||||
ListenerQUIC* outer = nullptr;
|
ListenerQUIC* outer = nullptr;
|
||||||
|
|
||||||
|
|
@ -301,6 +305,27 @@ ListenerQUIC::ListenerQUIC(std::uint16_t port,
|
||||||
Runtime().api->ConfigurationClose(impl->configuration);
|
Runtime().api->ConfigurationClose(impl->configuration);
|
||||||
throw QUICException(std::format("ListenerStart failed: 0x{:x}", static_cast<unsigned>(s)));
|
throw QUICException(std::format("ListenerStart failed: 0x{:x}", static_cast<unsigned>(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenerStart writes the port of the UDP socket it bound into the
|
||||||
|
// listener's local address when it was asked for port 0, and only
|
||||||
|
// then; read it back rather than trusting `port`. The UNSPEC address
|
||||||
|
// still carries it: the port sits at the same offset in sockaddr_in
|
||||||
|
// and sockaddr_in6, and QuicAddrGetPort reads the latter for anything
|
||||||
|
// that is not INET.
|
||||||
|
QUIC_ADDR bound{};
|
||||||
|
std::uint32_t boundSize = sizeof(bound);
|
||||||
|
s = Runtime().api->GetParam(impl->listener, QUIC_PARAM_LISTENER_LOCAL_ADDRESS, &boundSize, &bound);
|
||||||
|
if (QUIC_FAILED(s)) {
|
||||||
|
Runtime().api->ListenerClose(impl->listener);
|
||||||
|
Runtime().api->ConfigurationClose(impl->configuration);
|
||||||
|
throw QUICException(std::format("GetParam(QUIC_PARAM_LISTENER_LOCAL_ADDRESS) failed: 0x{:x}",
|
||||||
|
static_cast<unsigned>(s)));
|
||||||
|
}
|
||||||
|
impl->port = QuicAddrGetPort(&bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t ListenerQUIC::Port() const {
|
||||||
|
return impl ? impl->port : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenerQUIC::ListenerQUIC(ListenerQUIC&& other) noexcept
|
ListenerQUIC::ListenerQUIC(ListenerQUIC&& other) noexcept
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,12 @@ namespace Crafter {
|
||||||
ListenerQUIC(const ListenerQUIC&) = delete;
|
ListenerQUIC(const ListenerQUIC&) = delete;
|
||||||
ListenerQUIC(ListenerQUIC&&) noexcept;
|
ListenerQUIC(ListenerQUIC&&) noexcept;
|
||||||
|
|
||||||
|
// The UDP port the listener is bound to: the one asked for, or the
|
||||||
|
// one the system picked when that was 0. Read from msquic once the
|
||||||
|
// listener has started, so it is valid as soon as the constructor
|
||||||
|
// returns, and still after Stop(). 0 on a moved-from listener.
|
||||||
|
std::uint16_t Port() const;
|
||||||
|
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
// Block on this thread, dispatch each accepted connection on this
|
// Block on this thread, dispatch each accepted connection on this
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue