diff options
-rw-r--r-- | src/mongo/rpc/metadata/client_metadata.cpp | 10 | ||||
-rw-r--r-- | src/mongo/rpc/metadata/client_metadata_test.cpp | 66 |
2 files changed, 60 insertions, 16 deletions
diff --git a/src/mongo/rpc/metadata/client_metadata.cpp b/src/mongo/rpc/metadata/client_metadata.cpp index bc997989b6c..f3bddd47a4b 100644 --- a/src/mongo/rpc/metadata/client_metadata.cpp +++ b/src/mongo/rpc/metadata/client_metadata.cpp @@ -517,15 +517,19 @@ void ClientMetadata::setAndFinalize(Client* client, boost::optional<ClientMetada } void ClientMetadata::setFromMetadataForOperation(OperationContext* opCtx, BSONElement& elem) { + if (MONGO_unlikely(elem.eoo())) { + return; + } auto lk = stdx::lock_guard(*opCtx->getClient()); auto& state = getOperationState(opCtx); - auto wasFinalized = std::exchange(state.isFinalized, true); uassert(ErrorCodes::ClientMetadataCannotBeMutated, "The client metadata document may only be set once per operation", - !state.meta && !wasFinalized); + !state.meta && !state.isFinalized); + auto inputMetadata = ClientMetadata::readFromMetadata(elem); - state.meta = ClientMetadata::readFromMetadata(elem); + state.isFinalized = true; + state.meta = std::move(inputMetadata); } void ClientMetadata::setFromMetadata(Client* client, BSONElement& elem) { diff --git a/src/mongo/rpc/metadata/client_metadata_test.cpp b/src/mongo/rpc/metadata/client_metadata_test.cpp index 815df3ad87c..9a32f1d4bc4 100644 --- a/src/mongo/rpc/metadata/client_metadata_test.cpp +++ b/src/mongo/rpc/metadata/client_metadata_test.cpp @@ -36,8 +36,12 @@ #include <boost/filesystem.hpp> #include <map> +#include "mongo/bson/bsonelement.h" #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/client.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" #include "mongo/s/is_mongos.h" #include "mongo/unittest/unittest.h" #include "mongo/util/scopeguard.h" @@ -73,7 +77,7 @@ constexpr auto kUnknown = "unkown"_sd; } while (0) -TEST(ClientMetadatTest, TestLoopbackTest) { +TEST(ClientMetadataTest, TestLoopbackTest) { // Serialize without application name { BSONObjBuilder builder; @@ -124,7 +128,7 @@ TEST(ClientMetadatTest, TestLoopbackTest) { } // Mixed: no client metadata document -TEST(ClientMetadatTest, TestEmptyDoc) { +TEST(ClientMetadataTest, TestEmptyDoc) { { auto parseStatus = ClientMetadata::parse(BSONElement()); @@ -140,7 +144,7 @@ TEST(ClientMetadatTest, TestEmptyDoc) { } // Positive: test with only required fields -TEST(ClientMetadatTest, TestRequiredOnlyFields) { +TEST(ClientMetadataTest, TestRequiredOnlyFields) { // Without app name ASSERT_DOC_OK(kDriver << BSON(kName << "n1" << kVersion << "v1") << kOperatingSystem << BSON(kType << kUnknown)); @@ -153,7 +157,7 @@ TEST(ClientMetadatTest, TestRequiredOnlyFields) { // Positive: test with app_name spelled wrong fields -TEST(ClientMetadatTest, TestWithAppNameSpelledWrong) { +TEST(ClientMetadataTest, TestWithAppNameSpelledWrong) { ASSERT_DOC_OK(kApplication << BSON("extra" << "1") << kDriver << BSON(kName << "n1" << kVersion << "v1") @@ -161,19 +165,19 @@ TEST(ClientMetadatTest, TestWithAppNameSpelledWrong) { } // Positive: test with empty application document -TEST(ClientMetadatTest, TestWithEmptyApplication) { +TEST(ClientMetadataTest, TestWithEmptyApplication) { ASSERT_DOC_OK(kApplication << BSONObj() << kDriver << BSON(kName << "n1" << kVersion << "v1") << kOperatingSystem << BSON(kType << kUnknown)); } // Negative: test with appplication wrong type -TEST(ClientMetadatTest, TestNegativeWithAppNameWrongType) { +TEST(ClientMetadataTest, TestNegativeWithAppNameWrongType) { ASSERT_DOC_NOT_OK(kApplication << "1" << kDriver << BSON(kName << "n1" << kVersion << "v1") << kOperatingSystem << BSON(kType << kUnknown)); } // Positive: test with extra fields -TEST(ClientMetadatTest, TestExtraFields) { +TEST(ClientMetadataTest, TestExtraFields) { ASSERT_DOC_OK(kApplication << BSON(kName << "1" << "extra" << "v1") @@ -203,14 +207,14 @@ TEST(ClientMetadatTest, TestExtraFields) { } // Negative: only application specified -TEST(ClientMetadatTest, TestNegativeOnlyApplication) { +TEST(ClientMetadataTest, TestNegativeOnlyApplication) { ASSERT_DOC_NOT_OK(kApplication << BSON(kName << "1" << "extra" << "v1")); } // Negative: all combinations of only missing 1 required field -TEST(ClientMetadatTest, TestNegativeMissingRequiredOneField) { +TEST(ClientMetadataTest, TestNegativeMissingRequiredOneField) { ASSERT_DOC_NOT_OK(kDriver << BSON(kVersion << "v1") << kOperatingSystem << BSON(kType << kUnknown)); ASSERT_DOC_NOT_OK(kDriver << BSON(kName << "n1") << kOperatingSystem @@ -219,7 +223,7 @@ TEST(ClientMetadatTest, TestNegativeMissingRequiredOneField) { } // Negative: document with wrong types for required fields -TEST(ClientMetadatTest, TestNegativeWrongTypes) { +TEST(ClientMetadataTest, TestNegativeWrongTypes) { ASSERT_DOC_NOT_OK(kApplication << BSON(kName << 1) << kDriver << BSON(kName << "n1" << kVersion << "v1") << kOperatingSystem << BSON(kType << kUnknown)); @@ -235,7 +239,7 @@ TEST(ClientMetadatTest, TestNegativeWrongTypes) { } // Negative: document larger than 512 bytes -TEST(ClientMetadatTest, TestNegativeLargeDocument) { +TEST(ClientMetadataTest, TestNegativeLargeDocument) { bool savedMongos = isMongos(); auto unsetMongoS = makeGuard([&] { setMongos(savedMongos); }); @@ -255,7 +259,7 @@ TEST(ClientMetadatTest, TestNegativeLargeDocument) { } // Negative: document with app_name larger than 128 bytes -TEST(ClientMetadatTest, TestNegativeLargeAppName) { +TEST(ClientMetadataTest, TestNegativeLargeAppName) { { std::string str(128, 'x'); ASSERT_DOC_OK(kApplication << BSON(kName << str) << kDriver @@ -277,7 +281,7 @@ TEST(ClientMetadatTest, TestNegativeLargeAppName) { } // Serialize and attach mongos information -TEST(ClientMetadatTest, TestMongoSAppend) { +TEST(ClientMetadataTest, TestMongoSAppend) { BSONObjBuilder builder; ASSERT_OK(ClientMetadata::serializePrivate("a", "b", "c", "d", "e", "f", "g", &builder)); @@ -304,4 +308,40 @@ TEST(ClientMetadatTest, TestMongoSAppend) { ASSERT_BSONOBJ_EQ(doc, outDoc); } +TEST(ClientMetadataTest, TestInvalidDocWhileSettingOpCtxMetadata) { + auto svcCtx = ServiceContext::make(); + auto client = svcCtx->makeClient("ClientMetadataTest"); + auto opCtx = client->makeOperationContext(); + // metadataElem is of BSON type int + auto obj = BSON("a" << 4); + auto metadataElem = obj["a"]; + + // We expect parsing to fail because metadata must be of BSON type "document" + ASSERT_THROWS_CODE(ClientMetadata::setFromMetadataForOperation(opCtx.get(), metadataElem), + DBException, + ErrorCodes::TypeMismatch); + + // Ensure we can still safely retrieve a ClientMetadata* from the opCtx, and that it was left + // unset + auto clientMetaDataPtr = ClientMetadata::getForOperation(opCtx.get()); + ASSERT_FALSE(clientMetaDataPtr); +} + +TEST(ClientMetadataTest, TestEooElemAsValueToSetOpCtxMetadata) { + auto svcCtx = ServiceContext::make(); + auto client = svcCtx->makeClient("ClientMetadataTest"); + auto opCtx = client->makeOperationContext(); + // metadataElem is of BSON type eoo + auto metadataElem = BSONElement(); + + // BSON type "eoo" represents no data; we expect setting the metadata to be a no-op in this + // case. + ClientMetadata::setFromMetadataForOperation(opCtx.get(), metadataElem); + + // Ensure we can still safely retrieve a ClientMetadata* from the opCtx, and that it was left + // unset + auto clientMetaDataPtr = ClientMetadata::getForOperation(opCtx.get()); + ASSERT_FALSE(clientMetaDataPtr); +} + } // namespace mongo |