diff --git a/quic/api/test/QuicTransportBaseTest.cpp b/quic/api/test/QuicTransportBaseTest.cpp index 89f631af3..75eb0d8e2 100644 --- a/quic/api/test/QuicTransportBaseTest.cpp +++ b/quic/api/test/QuicTransportBaseTest.cpp @@ -165,7 +165,7 @@ class TestQuicTransport while (!cursor.isAtEnd()) { // create server chosen connId with processId = 0 and workerId = 0 ServerConnectionIdParams params(0, 0, 0); - conn_->serverConnectionId = connIdAlgo_->encodeConnectionId(params); + conn_->serverConnectionId = *connIdAlgo_->encodeConnectionId(params); auto type = static_cast(cursor.readBE()); if (type == TestFrameType::CRYPTO) { auto cryptoBuffer = decodeCryptoBuffer(cursor); @@ -338,7 +338,7 @@ class TestQuicTransport void setServerConnectionId() { // create server chosen connId with processId = 0 and workerId = 0 ServerConnectionIdParams params(0, 0, 0); - conn_->serverConnectionId = connIdAlgo_->encodeConnectionId(params); + conn_->serverConnectionId = *connIdAlgo_->encodeConnectionId(params); } void driveReadCallbacks() { diff --git a/quic/client/test/QuicClientTransportTest.cpp b/quic/client/test/QuicClientTransportTest.cpp index 9c39a2cbe..bdd950d6a 100644 --- a/quic/client/test/QuicClientTransportTest.cpp +++ b/quic/client/test/QuicClientTransportTest.cpp @@ -1418,7 +1418,7 @@ class QuicClientTransportTest : public Test { void setConnectionIds() { originalConnId = client->getConn().clientConnectionId; ServerConnectionIdParams params(0, 0, 0); - serverChosenConnId = connIdAlgo_->encodeConnectionId(params); + serverChosenConnId = *connIdAlgo_->encodeConnectionId(params); } void recvServerHello(const folly::SocketAddress& addr) { @@ -1712,7 +1712,7 @@ TEST_F(QuicClientTransportTest, FirstPacketProcessedCallback) { originalConnId = client->getConn().clientConnectionId; ServerConnectionIdParams params(0, 0, 0); client->getNonConstConn().serverConnectionId = - connIdAlgo_->encodeConnectionId(params); + *connIdAlgo_->encodeConnectionId(params); AckBlocks acks; acks.insert(0); @@ -2755,7 +2755,7 @@ class QuicClientTransportVersionAndRetryTest originalConnId = client->getConn().clientConnectionId; // create server chosen connId with processId = 0 and workerId = 0 ServerConnectionIdParams params(0, 0, 0); - serverChosenConnId = connIdAlgo_->encodeConnectionId(params); + serverChosenConnId = *connIdAlgo_->encodeConnectionId(params); // The tests that we do here create streams before crypto is finished, // so we initialize the peer streams, to allow for this behavior. TODO: when // 0-rtt support exists, remove this. diff --git a/quic/codec/ConnectionIdAlgo.h b/quic/codec/ConnectionIdAlgo.h index e9ec53642..1e82daf0e 100644 --- a/quic/codec/ConnectionIdAlgo.h +++ b/quic/codec/ConnectionIdAlgo.h @@ -8,7 +8,8 @@ #pragma once -#include +#include +#include #include namespace quic { @@ -28,19 +29,19 @@ class ConnectionIdAlgo { /** * Check if this implementation of algorithm can parse the given ConnectionId */ - virtual bool canParse(const ConnectionId& id) const = 0; + virtual bool canParse(const ConnectionId& id) const noexcept = 0; /** * Parses ServerConnectionIdParams from the given connection id. */ - virtual ServerConnectionIdParams parseConnectionId( - const ConnectionId& id) = 0; + virtual folly::Expected + parseConnectionId(const ConnectionId& id) noexcept = 0; /** * Encodes the given ServerConnectionIdParams into connection id */ - virtual ConnectionId encodeConnectionId( - const ServerConnectionIdParams& params) = 0; + virtual folly::Expected + encodeConnectionId(const ServerConnectionIdParams& params) noexcept = 0; }; /** diff --git a/quic/codec/DefaultConnectionIdAlgo.cpp b/quic/codec/DefaultConnectionIdAlgo.cpp index 447b6f52f..e305cb214 100644 --- a/quic/codec/DefaultConnectionIdAlgo.cpp +++ b/quic/codec/DefaultConnectionIdAlgo.cpp @@ -30,25 +30,28 @@ constexpr uint8_t kShortVersionBitsMask = 0xc0; /** * Sets the short version id bits (0 - 1) into the given ConnectionId */ -void setVersionBitsInConnId(quic::ConnectionId& connId, uint8_t version) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected +setVersionBitsInConnId(quic::ConnectionId& connId, uint8_t version) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for version", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } // clear 0-1 bits connId.data()[0] &= (~kShortVersionBitsMask); connId.data()[0] |= (kShortVersionBitsMask & (version << 6)); + return folly::unit; } /** * Extract the version id bits (0 - 1) from the given ConnectionId */ -uint8_t getVersionBitsFromConnId(const quic::ConnectionId& connId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected getVersionBitsFromConnId( + const quic::ConnectionId& connId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for version", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } uint8_t version = 0; version = (kShortVersionBitsMask & connId.data()[0]) >> 6; @@ -58,11 +61,13 @@ uint8_t getVersionBitsFromConnId(const quic::ConnectionId& connId) { /** * Sets the host id bits [2 - 17] bits into the given ConnectionId */ -void setHostIdBitsInConnId(quic::ConnectionId& connId, uint16_t hostId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected setHostIdBitsInConnId( + quic::ConnectionId& connId, + uint16_t hostId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for hostid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } // clear 2 - 7 bits connId.data()[0] &= ~kHostIdFirstByteMask; @@ -77,16 +82,18 @@ void setHostIdBitsInConnId(quic::ConnectionId& connId, uint16_t hostId) { connId.data()[1] |= (kHostIdSecondByteMask & (hostId >> 2)); // set 16 - 17 bits in the connId with the last 2 bits of the worker id connId.data()[2] |= (kHostIdThirdByteMask & (hostId << 6)); + return folly::unit; } /** * Extract the host id bits [2 - 17] bits from the given ConnectionId */ -uint16_t getHostIdBitsInConnId(const quic::ConnectionId& connId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected getHostIdBitsInConnId( + const quic::ConnectionId& connId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for hostid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } uint16_t hostId = 0; // get 2 - 7 bits from the connId and set first 6 bits of the host id @@ -103,11 +110,12 @@ uint16_t getHostIdBitsInConnId(const quic::ConnectionId& connId) { /** * Sets the given 8-bit workerId into the given connectionId's 18-25 bits */ -void setWorkerIdBitsInConnId(quic::ConnectionId& connId, uint8_t workerId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected +setWorkerIdBitsInConnId(quic::ConnectionId& connId, uint8_t workerId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for workerid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } // clear 18-23 bits connId.data()[2] &= 0xc0; @@ -117,16 +125,18 @@ void setWorkerIdBitsInConnId(quic::ConnectionId& connId, uint8_t workerId) { connId.data()[2] |= (kWorkerIdFirstByteMask & workerId) >> 2; // set 24 - 25 bits in the connId with the last 2 bits of the worker id connId.data()[3] |= (kWorkerIdSecondByteMask & workerId) << 6; + return folly::unit; } /** * Extracts the 'workerId' bits from the given ConnectionId */ -uint8_t getWorkerIdFromConnId(const quic::ConnectionId& connId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected getWorkerIdFromConnId( + const quic::ConnectionId& connId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for workerid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } // get 18 - 23 bits from the connId uint8_t workerId = connId.data()[2] << 2; @@ -138,25 +148,30 @@ uint8_t getWorkerIdFromConnId(const quic::ConnectionId& connId) { /** * Sets the server id bit (at 26th bit) into the given ConnectionId */ -void setProcessIdBitsInConnId(quic::ConnectionId& connId, uint8_t processId) { - if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( +folly::Expected +setProcessIdBitsInConnId( + quic::ConnectionId& connId, + uint8_t processId) noexcept { + if (UNLIKELY(connId.size() < quic::kMinSelfConnectionIdSize)) { + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for processid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } // clear the 26th bit connId.data()[3] &= (~kProcessIdBitMask); connId.data()[3] |= (kProcessIdBitMask & (processId << 5)); + return folly::unit; } /** * Extract the server id bit (at 26th bit) from the given ConnectionId */ -uint8_t getProcessIdBitsFromConnId(const quic::ConnectionId& connId) { +folly::Expected +getProcessIdBitsFromConnId(const quic::ConnectionId& connId) noexcept { if (connId.size() < quic::kMinSelfConnectionIdSize) { - throw quic::QuicInternalException( + return folly::makeUnexpected(quic::QuicInternalException( "ConnectionId is too small for processid", - quic::LocalErrorCode::INTERNAL_ERROR); + quic::LocalErrorCode::INTERNAL_ERROR)); } uint8_t processId = 0; processId = (kProcessIdBitMask & connId.data()[3]) >> 5; @@ -166,33 +181,53 @@ uint8_t getProcessIdBitsFromConnId(const quic::ConnectionId& connId) { namespace quic { -bool DefaultConnectionIdAlgo::canParse(const ConnectionId& id) const { +bool DefaultConnectionIdAlgo::canParse(const ConnectionId& id) const noexcept { if (id.size() < kMinSelfConnectionIdSize) { return false; } - return getVersionBitsFromConnId(id) == kShortVersionId; + return *getVersionBitsFromConnId(id) == kShortVersionId; } -ServerConnectionIdParams DefaultConnectionIdAlgo::parseConnectionId( - const ConnectionId& id) { +folly::Expected +DefaultConnectionIdAlgo::parseConnectionId(const ConnectionId& id) noexcept { + auto expectingVersion = getVersionBitsFromConnId(id); + if (UNLIKELY(!expectingVersion)) { + return folly::makeUnexpected(expectingVersion.error()); + } + auto expectingHost = getHostIdBitsInConnId(id); + if (UNLIKELY(!expectingHost)) { + return folly::makeUnexpected(expectingHost.error()); + } + auto expectingProcess = getProcessIdBitsFromConnId(id); + if (UNLIKELY(!expectingProcess)) { + return folly::makeUnexpected(expectingProcess.error()); + } + auto expectingWorker = getWorkerIdFromConnId(id); + if (UNLIKELY(!expectingWorker)) { + return folly::makeUnexpected(expectingWorker.error()); + } ServerConnectionIdParams serverConnIdParams( - getVersionBitsFromConnId(id), - getHostIdBitsInConnId(id), - getProcessIdBitsFromConnId(id), - getWorkerIdFromConnId(id)); + *expectingVersion, *expectingHost, *expectingProcess, *expectingWorker); return serverConnIdParams; } -ConnectionId DefaultConnectionIdAlgo::encodeConnectionId( - const ServerConnectionIdParams& params) { +folly::Expected +DefaultConnectionIdAlgo::encodeConnectionId( + const ServerConnectionIdParams& params) noexcept { // In case there is no client cid, create a random connection id. std::vector connIdData(kDefaultConnectionIdSize); folly::Random::secureRandom(connIdData.data(), connIdData.size()); ConnectionId connId = ConnectionId(std::move(connIdData)); - setVersionBitsInConnId(connId, params.version); - setHostIdBitsInConnId(connId, params.hostId); - setProcessIdBitsInConnId(connId, params.processId); - setWorkerIdBitsInConnId(connId, params.workerId); + auto expected = + setVersionBitsInConnId(connId, params.version) + .then([&](auto) { setHostIdBitsInConnId(connId, params.hostId); }) + .then( + [&](auto) { setProcessIdBitsInConnId(connId, params.processId); }) + .then( + [&](auto) { setWorkerIdBitsInConnId(connId, params.workerId); }); + if (UNLIKELY(expected.hasError())) { + return folly::makeUnexpected(expected.error()); + } return connId; } diff --git a/quic/codec/DefaultConnectionIdAlgo.h b/quic/codec/DefaultConnectionIdAlgo.h index 4985369d0..41fc72d98 100644 --- a/quic/codec/DefaultConnectionIdAlgo.h +++ b/quic/codec/DefaultConnectionIdAlgo.h @@ -8,7 +8,8 @@ #pragma once -#include +#include +#include #include #include @@ -37,18 +38,19 @@ class DefaultConnectionIdAlgo : public ConnectionIdAlgo { /** * Check if this implementation of algorithm can parse the given ConnectionId */ - bool canParse(const ConnectionId& id) const override; + bool canParse(const ConnectionId& id) const noexcept override; /** * Parses ServerConnectionIdParams from the given connection id. */ - ServerConnectionIdParams parseConnectionId(const ConnectionId& id) override; + folly::Expected + parseConnectionId(const ConnectionId& id) noexcept override; /** * Encodes the given ServerConnectionIdParams into connection id */ - ConnectionId encodeConnectionId( - const ServerConnectionIdParams& params) override; + folly::Expected encodeConnectionId( + const ServerConnectionIdParams& params) noexcept override; }; /** diff --git a/quic/codec/test/TypesTest.cpp b/quic/codec/test/TypesTest.cpp index a03d60e87..4b06402b0 100644 --- a/quic/codec/test/TypesTest.cpp +++ b/quic/codec/test/TypesTest.cpp @@ -201,14 +201,14 @@ TEST_F(TypesTest, TestConnIdWorkerId) { uint16_t hostId = folly::Random::rand32() % 4095; ServerConnectionIdParams params(hostId, processId, i); auto paramsAfterEncode = - connIdAlgo->parseConnectionId(connIdAlgo->encodeConnectionId(params)); - EXPECT_TRUE(connIdAlgo->canParse(connIdAlgo->encodeConnectionId(params))); - EXPECT_EQ(paramsAfterEncode.hostId, hostId); - EXPECT_EQ(paramsAfterEncode.workerId, i); - EXPECT_EQ(paramsAfterEncode.processId, processId); + connIdAlgo->parseConnectionId(*connIdAlgo->encodeConnectionId(params)); + EXPECT_TRUE(connIdAlgo->canParse(*connIdAlgo->encodeConnectionId(params))); + EXPECT_EQ(paramsAfterEncode->hostId, hostId); + EXPECT_EQ(paramsAfterEncode->workerId, i); + EXPECT_EQ(paramsAfterEncode->processId, processId); } ServerConnectionIdParams vParam(0x2, 7, 7, 7); - EXPECT_FALSE(connIdAlgo->canParse(connIdAlgo->encodeConnectionId(vParam))); + EXPECT_FALSE(connIdAlgo->canParse(*connIdAlgo->encodeConnectionId(vParam))); } TEST_F(TypesTest, ShortHeaderPacketNumberSpace) { diff --git a/quic/common/test/TestUtils.cpp b/quic/common/test/TestUtils.cpp index 43d08b5a7..1c08fe2a6 100644 --- a/quic/common/test/TestUtils.cpp +++ b/quic/common/test/TestUtils.cpp @@ -448,7 +448,7 @@ uint64_t computeExpectedDelay( ConnectionId getTestConnectionId(uint16_t hostId) { ServerConnectionIdParams params(hostId, 0, 0); DefaultConnectionIdAlgo connIdAlgo; - auto connId = connIdAlgo.encodeConnectionId(params); + auto connId = *connIdAlgo.encodeConnectionId(params); connId.data()[3] = 3; connId.data()[4] = 4; connId.data()[5] = 5; diff --git a/quic/loss/test/QuicLossFunctionsTest.cpp b/quic/loss/test/QuicLossFunctionsTest.cpp index 7a35df3d0..0691ebe51 100644 --- a/quic/loss/test/QuicLossFunctionsTest.cpp +++ b/quic/loss/test/QuicLossFunctionsTest.cpp @@ -82,7 +82,7 @@ class QuicLossFunctionsTest : public TestWithParam { // with bits for processId and workerId set to 0 ServerConnectionIdParams params(0, 0, 0); conn->connIdAlgo = connIdAlgo_.get(); - conn->serverConnectionId = connIdAlgo_->encodeConnectionId(params); + conn->serverConnectionId = *connIdAlgo_->encodeConnectionId(params); // for canSetLossTimerForAppData() conn->oneRttWriteCipher = createNoOpAead(); return conn; @@ -108,7 +108,7 @@ class QuicLossFunctionsTest : public TestWithParam { // create a serverConnectionId that is different from the client connId // with bits for processId and workerId set to 0 ServerConnectionIdParams params(0, 0, 0); - conn->serverConnectionId = connIdAlgo_.get()->encodeConnectionId(params); + conn->serverConnectionId = *connIdAlgo_.get()->encodeConnectionId(params); return conn; } diff --git a/quic/server/QuicServer.cpp b/quic/server/QuicServer.cpp index b98bb3775..d989cc34d 100644 --- a/quic/server/QuicServer.cpp +++ b/quic/server/QuicServer.cpp @@ -26,7 +26,8 @@ size_t getWorkerToRouteTo( const RoutingData& routingData, size_t numWorkers, ConnectionIdAlgo* connIdAlgo) { - return connIdAlgo->parseConnectionId(routingData.destinationConnId).workerId % + return connIdAlgo->parseConnectionId(routingData.destinationConnId) + ->workerId % numWorkers; } } // namespace diff --git a/quic/server/QuicServerWorker.cpp b/quic/server/QuicServerWorker.cpp index d6ad0570a..91c87613b 100644 --- a/quic/server/QuicServerWorker.cpp +++ b/quic/server/QuicServerWorker.cpp @@ -450,7 +450,7 @@ void QuicServerWorker::dispatchPacketData( return; } ServerConnectionIdParams connIdParam = - connIdAlgo_->parseConnectionId(routingData.destinationConnId); + *connIdAlgo_->parseConnectionId(routingData.destinationConnId); if (connIdParam.hostId != hostId_) { VLOG(3) << "Dropping packet routed to wrong host, CID=" << routingData.destinationConnId.hex() diff --git a/quic/server/state/ServerStateMachine.cpp b/quic/server/state/ServerStateMachine.cpp index e8878b9f6..c7bd04f8f 100644 --- a/quic/server/state/ServerStateMachine.cpp +++ b/quic/server/state/ServerStateMachine.cpp @@ -1193,9 +1193,12 @@ QuicServerConnectionState::createAndAddNewSelfConnId() { // TODO Possibly change this mechanism later // The default connectionId algo has 36 bits of randomness. + auto encodedCid = connIdAlgo->encodeConnectionId(*serverConnIdParams); + if (encodedCid.hasError()) { + return folly::none; + } auto newConnIdData = - ConnectionIdData{connIdAlgo->encodeConnectionId(*serverConnIdParams), - nextSelfConnectionIdSequence++}; + ConnectionIdData{std::move(*encodedCid), nextSelfConnectionIdSequence++}; newConnIdData.token = generator.generateToken(newConnIdData.connId); selfConnectionIds.push_back(newConnIdData); return newConnIdData; diff --git a/quic/server/test/QuicServerTest.cpp b/quic/server/test/QuicServerTest.cpp index 3458648dd..b0873f170 100644 --- a/quic/server/test/QuicServerTest.cpp +++ b/quic/server/test/QuicServerTest.cpp @@ -810,7 +810,7 @@ ConnectionId createConnIdForServer(ProcessId server) { auto connIdAlgo = std::make_unique(); uint8_t processId = (server == ProcessId::ONE) ? 1 : 0; ServerConnectionIdParams params(0, processId, 0); - return connIdAlgo->encodeConnectionId(params); + return *connIdAlgo->encodeConnectionId(params); } class QuicServerWorkerTakeoverTest : public Test { diff --git a/quic/server/test/ServerStateMachineTest.cpp b/quic/server/test/ServerStateMachineTest.cpp index 17352e22c..b2d974516 100644 --- a/quic/server/test/ServerStateMachineTest.cpp +++ b/quic/server/test/ServerStateMachineTest.cpp @@ -51,9 +51,9 @@ TEST(ServerStateMachineTest, TestAddConnId) { EXPECT_EQ(newConnId2->token->size(), kStatelessResetTokenLength); EXPECT_EQ(newConnId3->token->size(), kStatelessResetTokenLength); - auto params1 = serverState.connIdAlgo->parseConnectionId(newConnId1->connId); - auto params2 = serverState.connIdAlgo->parseConnectionId(newConnId2->connId); - auto params3 = serverState.connIdAlgo->parseConnectionId(newConnId3->connId); + auto params1 = *serverState.connIdAlgo->parseConnectionId(newConnId1->connId); + auto params2 = *serverState.connIdAlgo->parseConnectionId(newConnId2->connId); + auto params3 = *serverState.connIdAlgo->parseConnectionId(newConnId3->connId); // Server connection id params are correctly encoded/decoded. assertServerConnIdParamsEq(originalParams, params1); diff --git a/quic/state/stream/test/StreamStateMachineTest.cpp b/quic/state/stream/test/StreamStateMachineTest.cpp index 6e5583ce2..61d96d3e0 100644 --- a/quic/state/stream/test/StreamStateMachineTest.cpp +++ b/quic/state/stream/test/StreamStateMachineTest.cpp @@ -526,8 +526,7 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStream) { // create server chosen connId with processId = 0 and workerId = 0 ServerConnectionIdParams params(0, 0, 0); auto connIdAlgo = std::make_unique(); - folly::Optional serverChosenConnId = - connIdAlgo->encodeConnectionId(params); + auto serverChosenConnId = connIdAlgo->encodeConnectionId(params); auto stream = conn->streamManager->createNextBidirectionalStream().value(); stream->sendState = StreamSendState::Open_E; stream->recvState = StreamRecvState::Closed_E; @@ -565,8 +564,7 @@ TEST_F(QuicHalfClosedRemoteStateTest, AckStreamAfterSkip) { // create server chosen connId with processId = 0 and workerId = 0 ServerConnectionIdParams params(0, 0, 0); auto connIdAlgo = std::make_unique(); - folly::Optional serverChosenConnId = - connIdAlgo->encodeConnectionId(params); + auto serverChosenConnId = connIdAlgo->encodeConnectionId(params); auto stream = conn->streamManager->createNextBidirectionalStream().value(); stream->sendState = StreamSendState::Open_E; stream->recvState = StreamRecvState::Closed_E;