summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaloian Manassiev <kaloian.manassiev@mongodb.com>2021-02-10 12:33:29 +0100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-02-10 12:11:44 +0000
commit0dc7dd13c6b5d99d6c22d44f7ef96750f3540e50 (patch)
tree9de22c8a63b9aae80d0c32316e7249d983a35e08
parenteab7770928e86e0e70a035da87aa3fa616b9cc42 (diff)
downloadmongo-0dc7dd13c6b5d99d6c22d44f7ef96750f3540e50.tar.gz
Revert "SERVER-53150 Specify input/output to hello command"
This reverts commit 92dfc822d41714b47bc20e260aafb54884909acc.
-rw-r--r--jstests/noPassthrough/awaitable_hello.js7
-rw-r--r--src/mongo/db/repl/SConscript17
-rw-r--r--src/mongo/db/repl/hello.idl247
-rw-r--r--src/mongo/db/repl/hello_auth.cpp28
-rw-r--r--src/mongo/db/repl/hello_auth.h3
-rw-r--r--src/mongo/db/repl/replication_info.cpp217
-rw-r--r--src/mongo/rpc/SConscript1
-rw-r--r--src/mongo/rpc/metadata/client_metadata.cpp109
-rw-r--r--src/mongo/rpc/metadata/client_metadata.h20
-rw-r--r--src/mongo/rpc/metadata/client_metadata.idl44
-rw-r--r--src/mongo/s/commands/SConscript1
-rw-r--r--src/mongo/s/commands/cluster_hello_cmd.cpp140
-rw-r--r--src/mongo/transport/message_compressor_manager.cpp41
-rw-r--r--src/mongo/transport/message_compressor_manager.h7
-rw-r--r--src/mongo/transport/message_compressor_manager_test.cpp41
15 files changed, 323 insertions, 600 deletions
diff --git a/jstests/noPassthrough/awaitable_hello.js b/jstests/noPassthrough/awaitable_hello.js
index b65b7d63476..871396ffc21 100644
--- a/jstests/noPassthrough/awaitable_hello.js
+++ b/jstests/noPassthrough/awaitable_hello.js
@@ -8,9 +8,6 @@
load("jstests/libs/fail_point_util.js");
load("jstests/libs/parallel_shell_helpers.js");
-// ErrorCodes
-const kIDLParserComparisonError = 51024;
-
// runTest takes in the hello command or its aliases, isMaster and ismaster.
function runTest(db, cmd, logFailpoint) {
// Check the command response contains a topologyVersion even if maxAwaitTimeMS and
@@ -113,7 +110,7 @@ function runTest(db, cmd, logFailpoint) {
// Check that passing a topologyVersion not of type object fails.
assert.commandFailedWithCode(
db.runCommand({[cmd]: 1, topologyVersion: "topology_version_string", maxAwaitTimeMS: 0}),
- ErrorCodes.TypeMismatch);
+ 10065);
// Check that a topologyVersion with an invalid processId and valid counter fails.
assert.commandFailedWithCode(db.runCommand({
@@ -191,7 +188,7 @@ function runTest(db, cmd, logFailpoint) {
topologyVersion: topologyVersionField,
maxAwaitTimeMS: -1,
}),
- [31373, 51759, kIDLParserComparisonError]);
+ [31373, 51759]);
// Check that the command fails if the awaitable flag is present but either the topologyVersion,
// the maxAwaitTimeMS, or both are missing.
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 7dcf76a2ff7..b2747bf9a24 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -1199,20 +1199,6 @@ env.Library(
)
env.Library(
- target="hello_command",
- source=[
- "hello.idl",
- ],
- LIBDEPS=[
- '$BUILD_DIR/mongo/base',
- ],
- LIBDEPS_PRIVATE=[
- '$BUILD_DIR/mongo/idl/idl_parser',
- '$BUILD_DIR/mongo/rpc/metadata',
- ],
-)
-
-env.Library(
target="replication_info",
source=[
"replication_info.cpp",
@@ -1222,7 +1208,6 @@ env.Library(
'$BUILD_DIR/mongo/client/clientdriver_network',
'$BUILD_DIR/mongo/db/auth/auth',
'$BUILD_DIR/mongo/db/auth/saslauth',
- '$BUILD_DIR/mongo/db/commands/test_commands_enabled',
'$BUILD_DIR/mongo/db/dbhelpers',
'$BUILD_DIR/mongo/db/query_exec',
"$BUILD_DIR/mongo/util/fail_point",
@@ -1236,7 +1221,6 @@ env.Library(
'$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/transport/message_compressor',
'hello_auth',
- 'hello_command',
'primary_only_service',
'replication_auth',
'split_horizon',
@@ -1760,7 +1744,6 @@ env.Library(
'$BUILD_DIR/mongo/base',
'$BUILD_DIR/mongo/db/auth/authentication_session',
'$BUILD_DIR/mongo/db/auth/authservercommon',
- 'hello_command',
],
)
diff --git a/src/mongo/db/repl/hello.idl b/src/mongo/db/repl/hello.idl
deleted file mode 100644
index 452b1423565..00000000000
--- a/src/mongo/db/repl/hello.idl
+++ /dev/null
@@ -1,247 +0,0 @@
-# Copyright (C) 2021-present MongoDB, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the Server Side Public License, version 1,
-# as published by MongoDB, Inc.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# Server Side Public License for more details.
-#
-# You should have received a copy of the Server Side Public License
-# along with this program. If not, see
-# <http://www.mongodb.com/licensing/server-side-public-license>.
-#
-# As a special exception, the copyright holders give permission to link the
-# code of portions of this program with the OpenSSL library under certain
-# conditions as described in each individual source file and distribute
-# linked combinations including the program with the OpenSSL library. You
-# must comply with the Server Side Public License in all respects for
-# all of the code used other than as permitted herein. If you modify file(s)
-# with this exception, you may extend this exception to your version of the
-# file(s), but you are not obligated to do so. If you do not wish to do so,
-# delete this exception statement from your version. If you delete this
-# exception statement from all source files in the program, then also delete
-# it in the license file.
-#
-
-global:
- cpp_namespace: "mongo"
-
-imports:
- - "mongo/db/auth/auth_types.idl"
- - "mongo/db/repl/replication_types.idl"
- - "mongo/idl/basic_types.idl"
- - "mongo/rpc/metadata/client_metadata.idl"
- - "mongo/rpc/topology_version.idl"
-
-structs:
- HelloInternalClientField:
- description: "Specifies min/max wire protocol versions"
- strict: true
- fields:
- minWireVersion:
- # Currently ignored
- type: safeInt
- default: 0
- maxWireVersion:
- type: safeInt
-
- HelloLastWrite:
- description: "Most recent op/write times for this node"
- strict: true
- fields:
- opTime:
- type: optime
- optional: true
- lastWriteDate:
- type: date
- optional: true
- majorityOpTime:
- type: optime
- optional: true
- majorityWriteDate:
- type: date
- optional: true
-
- HelloCommandReply:
- description: "Reply to 'hello' command"
- strict: true
- fields:
- helloOk:
- type: bool
- default: true
- clientSupportsHello:
- type: bool
- optional: true
- configsvr:
- type: safeInt
- optional: true
- maxBsonObjectSize:
- type: safeInt64
- optional: true
- maxMessageSizeBytes:
- type: safeInt64
- optional: true
- maxWriteBatchSize:
- type: safeInt64
- optional: true
- localTime:
- type: date
- optional: true
- logicalSessionTimeoutMinutes:
- type: safeInt
- optional: true
- connectionId:
- type: safeInt64
- optional: true
- minWireVersion:
- type: safeInt
- optional: true
- maxWireVersion:
- type: safeInt
- optional: true
- readOnly:
- type: bool
- optional: true
- compression:
- type: array<string>
- optional: true
- automationServiceDescriptor:
- type: string
- optional: true
- saslSupportedMechs:
- type: array<string>
- optional: true
- speculativeAuthenticate:
- type: object
- optional: true
- msg:
- type: string
- optional: true
- ##
- ## ReplicationInfo
- ##
- topologyVersion:
- type: TopologyVersion
- ismaster:
- # Replies will contain 'ismaster' OR 'isWritablePrimary', not both
- type: bool
- optional: true
- isWritablePrimary:
- type: bool
- optional: true
- ##
- ## Using ReplSets
- ##
- hosts:
- type: array<string>
- optional: true
- passives:
- type: array<string>
- optional: true
- arbiters:
- type: array<string>
- optional: true
- setName:
- type: string
- optional: true
- primary:
- type: string
- optional: true
- secondary:
- type: bool
- optional: true
- info:
- type: string
- optional: true
- isreplicaset:
- type: bool
- optional: true
- setVersion:
- type: safeInt
- optional: true
- arbiterOnly:
- type: bool
- optional: true
- passive:
- type: bool
- optional: true
- hidden:
- type: bool
- optional: true
- buildIndexes:
- type: bool
- optional: true
- slaveDelay:
- # Reply will contain either slaveDelay or secondaryDelaySecs, but not both.
- type: safeInt64
- optional: true
- secondaryDelaySecs:
- type: safeInt64
- optional: true
- tags:
- type: object
- optional: true
- me:
- type: string
- optional: true
- electionId:
- type: objectid
- optional: true
- lastWrite:
- type: HelloLastWrite
- optional: true
-
-commands:
- hello:
- # Aliases: 'isMaster', 'ismaster'
- description: "Check if this server is primary for a replica set { hello: 1 }"
- command_name: hello
- namespace: ignored
- cpp_name: HelloCommand
- api_version: "1"
- reply_type: HelloCommandReply
- strict: true
- fields:
- awaitable:
- type: safeBool
- optional: true
- unstable: true
- forShell:
- type: safeBool
- default: false
- unstable: true
- hostInfo:
- type: string
- default: false
- hangUpOnStepDown:
- type: safeBool
- default: true
- internalClient:
- type: HelloInternalClientField
- optional: true
- unstable: true
- client:
- type: ClientMetadata
- optional: true
- topologyVersion:
- type: TopologyVersion
- optional: true
- maxAwaitTimeMS:
- type: safeInt64
- optional: true
- validator: { gte: 0 }
- helloOk:
- type: safeBool
- optional: true
- compression:
- type: array<string>
- optional: true
- saslSupportedMechs:
- type: UserName
- optional: true
- speculativeAuthenticate:
- type: object
- optional: true
diff --git a/src/mongo/db/repl/hello_auth.cpp b/src/mongo/db/repl/hello_auth.cpp
index efc79fdc1dc..b88ea5b1325 100644
--- a/src/mongo/db/repl/hello_auth.cpp
+++ b/src/mongo/db/repl/hello_auth.cpp
@@ -39,33 +39,39 @@
namespace mongo {
-void handleHelloAuth(OperationContext* opCtx, const HelloCommand& cmd, BSONObjBuilder* result) {
- // saslSupportedMechs: UserName -> List[String]
- if (auto userName = cmd.getSaslSupportedMechs()) {
+void handleHelloAuth(OperationContext* opCtx, BSONObj cmdObj, BSONObjBuilder* result) {
+ auto ssme = cmdObj[auth::kSaslSupportedMechanisms];
+ if (ssme.type() == BSONType::String) {
AuthenticationSession::doStep(
opCtx, AuthenticationSession::StepType::kSaslSupportedMechanisms, [&](auto session) {
+ UserName userName = uassertStatusOK(UserName::parse(ssme.String()));
+
auto& saslMechanismRegistry =
SASLServerMechanismRegistry::get(opCtx->getServiceContext());
- saslMechanismRegistry.advertiseMechanismNamesForUser(opCtx, *userName, result);
+ saslMechanismRegistry.advertiseMechanismNamesForUser(opCtx, userName, result);
});
}
- // speculativeAuthenticate: SaslStart -> SaslReply or Authenticate -> AuthenticateReply
- auto specAuth = cmd.getSpeculativeAuthenticate();
- if (!specAuth) {
+ auto sae = cmdObj[auth::kSpeculativeAuthenticate];
+ if (sae.eoo()) {
return;
}
uassert(ErrorCodes::BadValue,
+ str::stream() << "hello." << auth::kSpeculativeAuthenticate << " must be an Object",
+ sae.type() == Object);
+ auto specAuth = sae.Obj();
+
+ uassert(ErrorCodes::BadValue,
str::stream() << "hello." << auth::kSpeculativeAuthenticate
<< " must be a non-empty Object",
- !specAuth->isEmpty());
- auto specCmd = specAuth->firstElementFieldNameStringData();
+ !specAuth.isEmpty());
+ auto specCmd = specAuth.firstElementFieldNameStringData();
if (specCmd == saslStartCommandName) {
- doSpeculativeSaslStart(opCtx, *specAuth, result);
+ doSpeculativeSaslStart(opCtx, specAuth, result);
} else if (specCmd == auth::kAuthenticateCommand) {
- doSpeculativeAuthenticate(opCtx, *specAuth, result);
+ doSpeculativeAuthenticate(opCtx, specAuth, result);
} else {
uasserted(51769,
str::stream() << "hello." << auth::kSpeculativeAuthenticate
diff --git a/src/mongo/db/repl/hello_auth.h b/src/mongo/db/repl/hello_auth.h
index 8f8a73269db..1ec1b4d1658 100644
--- a/src/mongo/db/repl/hello_auth.h
+++ b/src/mongo/db/repl/hello_auth.h
@@ -32,7 +32,6 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/operation_context.h"
-#include "mongo/db/repl/hello_gen.h"
namespace mongo {
@@ -42,6 +41,6 @@ namespace mongo {
* This will attach supported mechanisms or invoke the behavior of saslStart/authenticate commands
* as appropriate.
*/
-void handleHelloAuth(OperationContext* opCtx, const HelloCommand& cmd, BSONObjBuilder* result);
+void handleHelloAuth(OperationContext* opCtx, BSONObj cmdObj, BSONObjBuilder* result);
} // namespace mongo
diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp
index ba7c3131844..7c051a60ebd 100644
--- a/src/mongo/db/repl/replication_info.cpp
+++ b/src/mongo/db/repl/replication_info.cpp
@@ -39,7 +39,6 @@
#include "mongo/client/dbclient_connection.h"
#include "mongo/db/client.h"
#include "mongo/db/commands/server_status.h"
-#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/curop.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
@@ -51,7 +50,6 @@
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/query/internal_plans.h"
#include "mongo/db/repl/hello_auth.h"
-#include "mongo/db/repl/hello_gen.h"
#include "mongo/db/repl/hello_response.h"
#include "mongo/db/repl/primary_only_service.h"
#include "mongo/db/repl/replication_auth.h"
@@ -103,7 +101,7 @@ TopologyVersion appendReplicationInfo(OperationContext* opCtx,
bool appendReplicationProcess,
bool useLegacyResponseFields,
boost::optional<TopologyVersion> clientTopologyVersion,
- boost::optional<std::int64_t> maxAwaitTimeMS) {
+ boost::optional<long long> maxAwaitTimeMS) {
TopologyVersion topologyVersion;
ReplicationCoordinator* replCoord = ReplicationCoordinator::get(opCtx);
if (replCoord->getSettings().usingReplSets()) {
@@ -239,14 +237,11 @@ public:
}
} oplogInfoServerStatus;
-const std::string kAutomationServiceDescriptorFieldName =
- HelloCommandReply::kAutomationServiceDescriptorFieldName.toString();
-
class CmdHello : public BasicCommandWithReplyBuilderInterface {
public:
CmdHello() : CmdHello(kHelloString, {}) {}
- const std::set<std::string>& apiVersions() const final {
+ const std::set<std::string>& apiVersions() const {
return kApiVersions1;
}
@@ -276,15 +271,13 @@ public:
const BSONObj& cmdObj,
rpc::ReplyBuilderInterface* replyBuilder) final {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
- const bool apiStrict = APIParameters::get(opCtx).getAPIStrict().value_or(false);
- auto cmd = HelloCommand::parse({"hello", apiStrict}, cmdObj);
waitInHello.pauseWhileSet(opCtx);
/* currently request to arbiter is (somewhat arbitrarily) an ismaster request that is not
authenticated.
*/
- if (cmd.getForShell()) {
+ if (cmdObj["forShell"].trueValue()) {
LastError::get(opCtx->getClient()).disable();
}
@@ -292,7 +285,8 @@ public:
transport::Session::TagMask sessionTagsToUnset = 0;
// Tag connections to avoid closing them on stepdown.
- if (!cmd.getHangUpOnStepDown()) {
+ auto hangUpElement = cmdObj["hangUpOnStepDown"];
+ if (!hangUpElement.eoo() && !hangUpElement.trueValue()) {
sessionTagsToSet |= transport::Session::kKeepOpen;
}
@@ -305,18 +299,51 @@ public:
// Parse the optional 'internalClient' field. This is provided by incoming connections from
// mongod and mongos.
- if (auto internalClient = cmd.getInternalClient()) {
+ auto internalClientElement = cmdObj["internalClient"];
+ if (internalClientElement) {
sessionTagsToSet |= transport::Session::kInternalClient;
sessionTagsToUnset |= transport::Session::kExternalClientKeepOpen;
- // All incoming connections from mongod/mongos of earlier versions should be
- // closed if the featureCompatibilityVersion is bumped to 3.6.
- if (internalClient->getMaxWireVersion() >=
- WireSpec::instance().get()->incomingInternalClient.maxWireVersion) {
- sessionTagsToSet |= transport::Session::kLatestVersionInternalClientKeepOpen;
- } else {
- sessionTagsToUnset |= transport::Session::kLatestVersionInternalClientKeepOpen;
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "'internalClient' must be of type Object, but was of type "
+ << typeName(internalClientElement.type()),
+ internalClientElement.type() == BSONType::Object);
+
+ bool foundMaxWireVersion = false;
+ for (auto&& elem : internalClientElement.Obj()) {
+ auto fieldName = elem.fieldNameStringData();
+ if (fieldName == "minWireVersion") {
+ // We do not currently use 'internalClient.minWireVersion'.
+ continue;
+ } else if (fieldName == "maxWireVersion") {
+ foundMaxWireVersion = true;
+
+ uassert(ErrorCodes::TypeMismatch,
+ str::stream() << "'maxWireVersion' field of 'internalClient' must be "
+ "of type int, but was of type "
+ << typeName(elem.type()),
+ elem.type() == BSONType::NumberInt);
+
+ // All incoming connections from mongod/mongos of earlier versions should be
+ // closed if the featureCompatibilityVersion is bumped to 3.6.
+ if (elem.numberInt() >=
+ WireSpec::instance().get()->incomingInternalClient.maxWireVersion) {
+ sessionTagsToSet |=
+ transport::Session::kLatestVersionInternalClientKeepOpen;
+ } else {
+ sessionTagsToUnset |=
+ transport::Session::kLatestVersionInternalClientKeepOpen;
+ }
+ } else {
+ uasserted(ErrorCodes::BadValue,
+ str::stream() << "Unrecognized field of 'internalClient': '"
+ << fieldName << "'");
+ }
}
+
+ uassert(ErrorCodes::BadValue,
+ "Missing required field 'maxWireVersion' of 'internalClient'",
+ foundMaxWireVersion);
} else {
sessionTagsToUnset |= (transport::Session::kInternalClient |
transport::Session::kLatestVersionInternalClientKeepOpen);
@@ -342,36 +369,53 @@ public:
// If a client is following the awaitable hello protocol, maxAwaitTimeMS should be
// present if and only if topologyVersion is present in the request.
- auto clientTopologyVersion = cmd.getTopologyVersion();
- auto maxAwaitTimeMS = cmd.getMaxAwaitTimeMS();
+ auto topologyVersionElement = cmdObj["topologyVersion"];
+ auto maxAwaitTimeMSField = cmdObj["maxAwaitTimeMS"];
auto curOp = CurOp::get(opCtx);
+
+ // The awaitable field is optional, but if it exists, it requires both other fields.
+ // Note that the awaitable field only exists to make filtering out these operations
+ // via currentOp queries easier and is not otherwise explicitly used.
+ {
+ bool awaitable = true;
+ Status status = bsonExtractBooleanField(cmdObj, "awaitable", &awaitable);
+ if (status.isOK() && (!topologyVersionElement || !maxAwaitTimeMSField)) {
+ uassert(5135800,
+ "A request marked awaitable must contain both 'topologyVersion' and "
+ "'maxAwaitTimeMS'",
+ !awaitable);
+ }
+ }
+
+ boost::optional<TopologyVersion> clientTopologyVersion;
+ boost::optional<long long> maxAwaitTimeMS;
boost::optional<ScopeGuard<std::function<void()>>> timerGuard;
- if (clientTopologyVersion && maxAwaitTimeMS) {
+ if (topologyVersionElement && maxAwaitTimeMSField) {
+ clientTopologyVersion = TopologyVersion::parse(IDLParserErrorContext("TopologyVersion"),
+ topologyVersionElement.Obj());
uassert(31372,
"topologyVersion must have a non-negative counter",
clientTopologyVersion->getCounter() >= 0);
- LOGV2_DEBUG(23904,
- 3,
- "Using maxAwaitTimeMS for awaitable hello protocol",
- "maxAwaitTimeMS"_attr = maxAwaitTimeMS.get());
+ {
+ long long parsedMaxAwaitTimeMS;
+ uassertStatusOK(
+ bsonExtractIntegerField(cmdObj, "maxAwaitTimeMS", &parsedMaxAwaitTimeMS));
+ maxAwaitTimeMS = parsedMaxAwaitTimeMS;
+ }
+
+ uassert(31373, "maxAwaitTimeMS must be a non-negative integer", *maxAwaitTimeMS >= 0);
+
+ LOGV2_DEBUG(23904, 3, "Using maxAwaitTimeMS for awaitable hello protocol.");
curOp->pauseTimer();
timerGuard.emplace([curOp]() { curOp->resumeTimer(); });
} else {
- // The awaitable field is optional, but if it exists, it requires both other fields.
- // Note that the awaitable field only exists to make filtering out these operations
- // via currentOp queries easier and is not otherwise explicitly used.
- uassert(5135800,
- "A request marked awaitable must contain both 'topologyVersion' and "
- "'maxAwaitTimeMS'",
- !cmd.getAwaitable() || !cmd.getAwaitable());
-
uassert(31368,
- (clientTopologyVersion
+ (topologyVersionElement
? "A request with a 'topologyVersion' must include 'maxAwaitTimeMS'"
: "A request with 'maxAwaitTimeMS' must include a 'topologyVersion'"),
- !clientTopologyVersion && !maxAwaitTimeMS);
+ !topologyVersionElement && !maxAwaitTimeMSField);
}
auto result = replyBuilder->getBodyBuilder();
@@ -380,18 +424,21 @@ public:
// handshake for an incoming connection if the client supports the hello command. Clients
// that specify 'helloOk' do not rely on "not master" error message parsing, which means
// that we can safely return "not primary" error messages instead.
- if (auto helloOk = cmd.getHelloOk()) {
+ bool helloOk = client->supportsHello();
+ Status status = bsonExtractBooleanField(cmdObj, "helloOk", &helloOk);
+ if (status.isOK()) {
// If the hello request contains a "helloOk" field, set _supportsHello on the Client
// to the value.
- client->setSupportsHello(*helloOk);
+ client->setSupportsHello(helloOk);
// Attach helloOk: true to the response so that the client knows the server supports
// the hello command.
- result.append(HelloCommandReply::kHelloOkFieldName, true);
+ result.append("helloOk", true);
+ } else if (status.code() != ErrorCodes::NoSuchKey) {
+ uassertStatusOK(status);
}
if (MONGO_unlikely(appendHelloOkToHelloResponse.shouldFail())) {
- result.append(HelloCommandReply::kClientSupportsHelloFieldName,
- client->supportsHello());
+ result.append("clientSupportsHello", client->supportsHello());
}
auto currentTopologyVersion = appendReplicationInfo(
@@ -400,44 +447,35 @@ public:
timerGuard.reset(); // Resume curOp timer.
if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) {
- constexpr int kConfigServerModeNumber = 2;
- result.append(HelloCommandReply::kConfigsvrFieldName, kConfigServerModeNumber);
+ const int configServerModeNumber = 2;
+ result.append("configsvr", configServerModeNumber);
}
- result.appendNumber(HelloCommandReply::kMaxBsonObjectSizeFieldName, BSONObjMaxUserSize);
- result.appendNumber(HelloCommandReply::kMaxMessageSizeBytesFieldName, MaxMessageSizeBytes);
- result.appendNumber(HelloCommandReply::kMaxWriteBatchSizeFieldName,
- write_ops::kMaxWriteBatchSize);
- result.appendDate(HelloCommandReply::kLocalTimeFieldName, jsTime());
- result.append(HelloCommandReply::kLogicalSessionTimeoutMinutesFieldName,
- localLogicalSessionTimeoutMinutes);
- result.appendNumber(HelloCommandReply::kConnectionIdFieldName,
- opCtx->getClient()->getConnectionId());
-
-
- if (auto wireSpec = WireSpec::instance().get(); cmd.getInternalClient()) {
- result.append(HelloCommandReply::kMinWireVersionFieldName,
- wireSpec->incomingInternalClient.minWireVersion);
- result.append(HelloCommandReply::kMaxWireVersionFieldName,
- wireSpec->incomingInternalClient.maxWireVersion);
+ result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize);
+ result.appendNumber("maxMessageSizeBytes", MaxMessageSizeBytes);
+ result.appendNumber("maxWriteBatchSize", write_ops::kMaxWriteBatchSize);
+ result.appendDate("localTime", jsTime());
+ result.append("logicalSessionTimeoutMinutes", localLogicalSessionTimeoutMinutes);
+ result.appendNumber("connectionId", opCtx->getClient()->getConnectionId());
+
+ if (auto wireSpec = WireSpec::instance().get(); internalClientElement) {
+ result.append("minWireVersion", wireSpec->incomingInternalClient.minWireVersion);
+ result.append("maxWireVersion", wireSpec->incomingInternalClient.maxWireVersion);
} else {
- result.append(HelloCommandReply::kMinWireVersionFieldName,
- wireSpec->incomingExternalClient.minWireVersion);
- result.append(HelloCommandReply::kMaxWireVersionFieldName,
- wireSpec->incomingExternalClient.maxWireVersion);
+ result.append("minWireVersion", wireSpec->incomingExternalClient.minWireVersion);
+ result.append("maxWireVersion", wireSpec->incomingExternalClient.maxWireVersion);
}
- result.append(HelloCommandReply::kReadOnlyFieldName, storageGlobalParams.readOnly);
+ result.append("readOnly", storageGlobalParams.readOnly);
const auto& params = ServerParameterSet::getGlobal()->getMap();
- if (auto iter = params.find(kAutomationServiceDescriptorFieldName);
- iter != params.end() && iter->second) {
- iter->second->append(opCtx, result, kAutomationServiceDescriptorFieldName);
- }
+ if (auto iter = params.find("automationServiceDescriptor");
+ iter != params.end() && iter->second)
+ iter->second->append(opCtx, result, "automationServiceDescriptor");
if (opCtx->getClient()->session()) {
MessageCompressorManager::forSession(opCtx->getClient()->session())
- .serverNegotiate(cmd.getCompression(), &result);
+ .serverNegotiate(cmdObj, &result);
}
if (opCtx->isExhaust()) {
@@ -445,7 +483,7 @@ public:
uassert(51756,
"An isMaster or hello request with exhaust must specify 'maxAwaitTimeMS'",
- maxAwaitTimeMS);
+ maxAwaitTimeMSField);
invariant(clientTopologyVersion);
InExhaustHello::get(opCtx->getClient()->session().get())
@@ -457,45 +495,30 @@ public:
// command parameters should be reused as the next BSONObj command parameters.
replyBuilder->setNextInvocation(boost::none);
} else {
- BSONObjBuilder niBuilder;
- for (const auto& elem : cmdObj) {
- if (elem.fieldNameStringData() == HelloCommand::kTopologyVersionFieldName) {
- BSONObjBuilder tvBuilder(
- niBuilder.subobjStart(HelloCommand::kTopologyVersionFieldName));
- currentTopologyVersion.serialize(&tvBuilder);
+ BSONObjBuilder nextInvocationBuilder;
+ for (auto&& elt : cmdObj) {
+ if (elt.fieldNameStringData() == "topologyVersion"_sd) {
+ BSONObjBuilder topologyVersionBuilder(
+ nextInvocationBuilder.subobjStart("topologyVersion"));
+ currentTopologyVersion.serialize(&topologyVersionBuilder);
} else {
- niBuilder.append(elem);
+ nextInvocationBuilder.append(elt);
}
}
- replyBuilder->setNextInvocation(niBuilder.obj());
+ replyBuilder->setNextInvocation(nextInvocationBuilder.obj());
}
}
- handleHelloAuth(opCtx, cmd, &result);
+ handleHelloAuth(opCtx, cmdObj, &result);
- if (getTestCommandsEnabled()) {
- validateResult(&result);
- }
return true;
}
- void validateResult(BSONObjBuilder* result) {
- auto ret = result->asTempObj();
- if (ret[ErrorReply::kErrmsgFieldName].eoo()) {
- // Nominal success case, parse the object as-is.
- HelloCommandReply::parse({"hello.reply"}, ret);
- } else {
- // Something went wrong, still try to parse, but accept a few ignorable fields.
- StringDataSet ignorable({ErrorReply::kCodeFieldName, ErrorReply::kErrmsgFieldName});
- HelloCommandReply::parse({"hello.reply"}, ret.removeFields(ignorable));
- }
- }
-
protected:
CmdHello(const StringData cmdName, const std::initializer_list<StringData>& alias)
: BasicCommandWithReplyBuilderInterface(cmdName, alias) {}
- virtual bool useLegacyResponseFields() const {
+ virtual bool useLegacyResponseFields() {
return false;
}
@@ -505,7 +528,7 @@ class CmdIsMaster : public CmdHello {
public:
CmdIsMaster() : CmdHello(kCamelCaseIsMasterString, {kLowerCaseIsMasterString}) {}
- std::string help() const final {
+ std::string help() const override {
return "Check if this server is primary for a replica set\n"
"{ isMaster : 1 }";
}
@@ -514,7 +537,7 @@ protected:
// Parse the command name, which should be one of the following: hello, isMaster, or
// ismaster. If the command is "hello", we must attach an "isWritablePrimary" response field
// instead of "ismaster" and "secondaryDelaySecs" response field instead of "slaveDelay".
- bool useLegacyResponseFields() const final {
+ bool useLegacyResponseFields() override {
return true;
}
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index 257e556da03..957647e5721 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -135,7 +135,6 @@ env.Library(
target='client_metadata',
source=[
'metadata/client_metadata.cpp',
- 'metadata/client_metadata.idl',
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
diff --git a/src/mongo/rpc/metadata/client_metadata.cpp b/src/mongo/rpc/metadata/client_metadata.cpp
index 6ea09b44b02..a4a8767aadc 100644
--- a/src/mongo/rpc/metadata/client_metadata.cpp
+++ b/src/mongo/rpc/metadata/client_metadata.cpp
@@ -84,7 +84,7 @@ const auto getOperationState = OperationContext::declareDecoration<ClientMetadat
} // namespace
-StatusWith<boost::optional<ClientMetadata>> ClientMetadata::parse(const BSONElement& element) try {
+StatusWith<boost::optional<ClientMetadata>> ClientMetadata::parse(const BSONElement& element) {
if (element.eoo()) {
return {boost::none};
}
@@ -93,50 +93,84 @@ StatusWith<boost::optional<ClientMetadata>> ClientMetadata::parse(const BSONElem
return Status(ErrorCodes::TypeMismatch, "The client metadata document must be a document");
}
- return boost::make_optional(parseFromBSON(element.Obj()));
-} catch (const DBException& ex) {
- return ex.toStatus();
+ ClientMetadata clientMetadata;
+ Status s = clientMetadata.parseClientMetadataDocument(element.Obj());
+ if (!s.isOK()) {
+ return s;
+ }
+
+ return {std::move(clientMetadata)};
}
-ClientMetadata::ClientMetadata(BSONObj doc) {
+Status ClientMetadata::parseClientMetadataDocument(const BSONObj& doc) {
uint32_t maxLength = kMaxMongoDMetadataDocumentByteLength;
if (isMongos()) {
maxLength = kMaxMongoSMetadataDocumentByteLength;
}
- uassert(ErrorCodes::ClientMetadataDocumentTooLarge,
- str::stream() << "The client metadata document must be less then or equal to "
- << maxLength << "bytes",
- static_cast<uint32_t>(doc.objsize()) <= maxLength);
-
- const auto isobj = [](StringData name, const BSONElement& e) {
- uassert(ErrorCodes::TypeMismatch,
- str::stream()
- << "The '" << name
- << "' field is required to be a BSON document in the client metadata document",
- e.isABSONObj());
- };
+ if (static_cast<uint32_t>(doc.objsize()) > maxLength) {
+ return Status(ErrorCodes::ClientMetadataDocumentTooLarge,
+ str::stream() << "The client metadata document must be less then or equal to "
+ << maxLength << "bytes");
+ }
// Get a copy so that we can take a stable reference to the app name inside
- _document = doc.getOwned();
+ BSONObj docOwned = doc.getOwned();
+ StringData appName;
bool foundDriver = false;
bool foundOperatingSystem = false;
- for (const auto& e : _document) {
- auto name = e.fieldNameStringData();
+
+ BSONObjIterator i(docOwned);
+ while (i.more()) {
+ BSONElement e = i.next();
+ StringData name = e.fieldNameStringData();
if (name == kApplication) {
// Application is an optional sub-document, but we require it to be a document if
// specified.
- isobj(kApplication, e);
- _appName = uassertStatusOK(parseApplicationDocument(e.Obj()));
+ if (!e.isABSONObj()) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "The '" << kApplication
+ << "' field is required to be a BSON document in the "
+ "client metadata document");
+ }
+
+ auto swAppName = parseApplicationDocument(e.Obj());
+ if (!swAppName.getStatus().isOK()) {
+ return swAppName.getStatus();
+ }
+
+ appName = swAppName.getValue();
+
} else if (name == kDriver) {
- isobj(kDriver, e);
- uassertStatusOK(validateDriverDocument(e.Obj()));
+ if (!e.isABSONObj()) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "The '" << kDriver
+ << "' field is required to be a "
+ "BSON document in the client "
+ "metadata document");
+ }
+
+ Status s = validateDriverDocument(e.Obj());
+ if (!s.isOK()) {
+ return s;
+ }
+
foundDriver = true;
} else if (name == kOperatingSystem) {
- isobj(kOperatingSystem, e);
- uassertStatusOK(validateOperatingSystemDocument(e.Obj()));
+ if (!e.isABSONObj()) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "The '" << kOperatingSystem
+ << "' field is required to be a BSON document in the "
+ "client metadata document");
+ }
+
+ Status s = validateOperatingSystemDocument(e.Obj());
+ if (!s.isOK()) {
+ return s;
+ }
+
foundOperatingSystem = true;
}
@@ -144,16 +178,23 @@ ClientMetadata::ClientMetadata(BSONObj doc) {
}
// Driver is a required sub document.
- uassert(ErrorCodes::ClientMetadataMissingField,
- str::stream() << "Missing required sub-document '" << kDriver
- << "' in the client metadata document",
- foundDriver);
+ if (!foundDriver) {
+ return Status(ErrorCodes::ClientMetadataMissingField,
+ str::stream() << "Missing required sub-document '" << kDriver
+ << "' in the client metadata document");
+ }
// OS is a required sub document.
- uassert(ErrorCodes::ClientMetadataMissingField,
- str::stream() << "Missing required sub-document '" << kOperatingSystem
- << "' in the client metadata document",
- foundOperatingSystem);
+ if (!foundOperatingSystem) {
+ return Status(ErrorCodes::ClientMetadataMissingField,
+ str::stream() << "Missing required sub-document '" << kOperatingSystem
+ << "' in the client metadata document");
+ }
+
+ _document = std::move(docOwned);
+ _appName = std::move(appName);
+
+ return Status::OK();
}
StatusWith<StringData> ClientMetadata::parseApplicationDocument(const BSONObj& doc) {
diff --git a/src/mongo/rpc/metadata/client_metadata.h b/src/mongo/rpc/metadata/client_metadata.h
index 309eb5668e0..cc3d870a4f0 100644
--- a/src/mongo/rpc/metadata/client_metadata.h
+++ b/src/mongo/rpc/metadata/client_metadata.h
@@ -80,16 +80,10 @@ constexpr auto kMetadataDocumentName = "client"_sd;
* See Driver Specification: "MongoDB Handshake" for more information.
*/
class ClientMetadata {
-public:
- explicit ClientMetadata(BSONObj obj);
-
- ClientMetadata(const ClientMetadata& src) : ClientMetadata(src._document) {}
- ClientMetadata& operator=(const ClientMetadata& src) {
- ClientMetadata copy(src._document);
- *this = std::move(copy);
- return *this;
- }
+ ClientMetadata(const ClientMetadata&) = delete;
+ ClientMetadata& operator=(const ClientMetadata&) = delete;
+public:
ClientMetadata(ClientMetadata&&) = default;
ClientMetadata& operator=(ClientMetadata&&) = default;
@@ -151,13 +145,6 @@ public:
static StatusWith<boost::optional<ClientMetadata>> parse(const BSONElement& element);
/**
- * Wrapper for BSONObj constructor used by IDL parsers.
- */
- static ClientMetadata parseFromBSON(BSONObj obj) {
- return ClientMetadata(obj);
- }
-
- /**
* Create a new client metadata document with os information from the ProcessInfo class.
*
* This method outputs the "client" field, and client metadata sub-document in the
@@ -324,6 +311,7 @@ public:
private:
ClientMetadata() = default;
+ Status parseClientMetadataDocument(const BSONObj& doc);
static Status validateDriverDocument(const BSONObj& doc);
static Status validateOperatingSystemDocument(const BSONObj& doc);
static StatusWith<StringData> parseApplicationDocument(const BSONObj& doc);
diff --git a/src/mongo/rpc/metadata/client_metadata.idl b/src/mongo/rpc/metadata/client_metadata.idl
deleted file mode 100644
index d1726153d98..00000000000
--- a/src/mongo/rpc/metadata/client_metadata.idl
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2021-present MongoDB, Inc.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the Server Side Public License, version 1,
-# as published by MongoDB, Inc.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# Server Side Public License for more details.
-#
-# You should have received a copy of the Server Side Public License
-# along with this program. If not, see
-# <http://www.mongodb.com/licensing/server-side-public-license>.
-#
-# As a special exception, the copyright holders give permission to link the
-# code of portions of this program with the OpenSSL library under certain
-# conditions as described in each individual source file and distribute
-# linked combinations including the program with the OpenSSL library. You
-# must comply with the Server Side Public License in all respects for
-# all of the code used other than as permitted herein. If you modify file(s)
-# with this exception, you may extend this exception to your version of the
-# file(s), but you are not obligated to do so. If you do not wish to do so,
-# delete this exception statement from your version. If you delete this
-# exception statement from all source files in the program, then also delete
-# it in the license file.
-#
-
-global:
- cpp_namespace: "mongo"
- cpp_includes:
- - "mongo/rpc/metadata/client_metadata.h"
-
-imports:
- - "mongo/idl/basic_types.idl"
-
-types:
- ClientMetadata:
- description: "Client metadata document"
- bson_serialization_type: object
- cpp_type: ClientMetadata
- serializer: "mongo::ClientMetadata::getDocument"
- deserializer: "mongo::ClientMetadata::parseFromBSON"
-
diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript
index 0d20f144e62..275ae43565a 100644
--- a/src/mongo/s/commands/SConscript
+++ b/src/mongo/s/commands/SConscript
@@ -132,7 +132,6 @@ env.Library(
'$BUILD_DIR/mongo/db/query/map_reduce_output_format',
'$BUILD_DIR/mongo/db/read_write_concern_defaults',
'$BUILD_DIR/mongo/db/repl/hello_auth',
- '$BUILD_DIR/mongo/db/repl/hello_command',
'$BUILD_DIR/mongo/db/shared_request_handling',
'$BUILD_DIR/mongo/db/stats/api_version_metrics',
'$BUILD_DIR/mongo/db/stats/counters',
diff --git a/src/mongo/s/commands/cluster_hello_cmd.cpp b/src/mongo/s/commands/cluster_hello_cmd.cpp
index 69040ef342d..630e82d041c 100644
--- a/src/mongo/s/commands/cluster_hello_cmd.cpp
+++ b/src/mongo/s/commands/cluster_hello_cmd.cpp
@@ -34,13 +34,11 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
-#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/curop.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/ops/write_ops.h"
#include "mongo/db/repl/hello_auth.h"
-#include "mongo/db/repl/hello_gen.h"
#include "mongo/db/wire_version.h"
#include "mongo/logv2/log.h"
#include "mongo/rpc/metadata/client_metadata.h"
@@ -62,22 +60,21 @@ namespace {
constexpr auto kHelloString = "hello"_sd;
constexpr auto kCamelCaseIsMasterString = "isMaster"_sd;
constexpr auto kLowerCaseIsMasterString = "ismaster"_sd;
-const std::string kAutomationServiceDescriptorFieldName =
- HelloCommandReply::kAutomationServiceDescriptorFieldName.toString();
+
class CmdHello : public BasicCommandWithReplyBuilderInterface {
public:
CmdHello() : CmdHello(kHelloString, {}) {}
- const std::set<std::string>& apiVersions() const final {
+ const std::set<std::string>& apiVersions() const {
return kApiVersions1;
}
- bool supportsWriteConcern(const BSONObj& cmd) const final {
+ bool supportsWriteConcern(const BSONObj& cmd) const override {
return false;
}
- AllowedOnSecondary secondaryAllowed(ServiceContext*) const final {
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kAlways;
}
@@ -87,11 +84,11 @@ public:
void addRequiredPrivileges(const std::string& dbname,
const BSONObj& cmdObj,
- std::vector<Privilege>* out) const final {
+ std::vector<Privilege>* out) const override {
// No auth required
}
- bool requiresAuth() const final {
+ bool requiresAuth() const override {
return false;
}
@@ -100,8 +97,6 @@ public:
const BSONObj& cmdObj,
rpc::ReplyBuilderInterface* replyBuilder) final {
CommandHelpers::handleMarkKillOnClientDisconnect(opCtx);
- const bool apiStrict = APIParameters::get(opCtx).getAPIStrict().value_or(false);
- auto cmd = HelloCommand::parse({"hello", apiStrict}, cmdObj);
waitInHello.pauseWhileSet(opCtx);
@@ -110,38 +105,51 @@ public:
// If a client is following the awaitable hello protocol, maxAwaitTimeMS should be
// present if and only if topologyVersion is present in the request.
- auto clientTopologyVersion = cmd.getTopologyVersion();
- auto maxAwaitTimeMS = cmd.getMaxAwaitTimeMS();
+ auto topologyVersionElement = cmdObj["topologyVersion"];
+ auto maxAwaitTimeMSField = cmdObj["maxAwaitTimeMS"];
auto curOp = CurOp::get(opCtx);
+
+ // The awaitable field is optional, but if it exists, it requires both other fields.
+ // Note that the awaitable field only exists to make filtering out these operations
+ // via currentOp queries easier and is not otherwise explicitly used.
+ {
+ bool awaitable = true;
+ Status status = bsonExtractBooleanField(cmdObj, "awaitable", &awaitable);
+ if (status.isOK() && (!topologyVersionElement || !maxAwaitTimeMSField)) {
+ uassert(5135801,
+ "A request marked awaitable must contain both 'topologyVersion' and "
+ "'maxAwaitTimeMS'",
+ !awaitable);
+ }
+ }
+
+ boost::optional<TopologyVersion> clientTopologyVersion;
boost::optional<Date_t> deadline;
boost::optional<ScopeGuard<std::function<void()>>> timerGuard;
- if (clientTopologyVersion && maxAwaitTimeMS) {
+ if (topologyVersionElement && maxAwaitTimeMSField) {
+ clientTopologyVersion = TopologyVersion::parse(IDLParserErrorContext("TopologyVersion"),
+ topologyVersionElement.Obj());
uassert(51758,
"topologyVersion must have a non-negative counter",
clientTopologyVersion->getCounter() >= 0);
- uassert(51759, "maxAwaitTimeMS must be a non-negative integer", *maxAwaitTimeMS >= 0);
+ long long maxAwaitTimeMS;
+ uassertStatusOK(bsonExtractIntegerField(cmdObj, "maxAwaitTimeMS", &maxAwaitTimeMS));
+
+ uassert(51759, "maxAwaitTimeMS must be a non-negative integer", maxAwaitTimeMS >= 0);
deadline = opCtx->getServiceContext()->getPreciseClockSource()->now() +
- Milliseconds(*maxAwaitTimeMS);
+ Milliseconds(maxAwaitTimeMS);
LOGV2_DEBUG(23871, 3, "Using maxAwaitTimeMS for awaitable hello protocol.");
curOp->pauseTimer();
timerGuard.emplace([curOp]() { curOp->resumeTimer(); });
} else {
- // The awaitable field is optional, but if it exists, it requires both other fields.
- // Note that the awaitable field only exists to make filtering out these operations
- // via currentOp queries easier and is not otherwise explicitly used.
- uassert(5135801,
- "A request marked awaitable must contain both 'topologyVersion' and "
- "'maxAwaitTimeMS'",
- !cmd.getAwaitable() || !cmd.getAwaitable().get());
-
uassert(51760,
- (clientTopologyVersion
+ (topologyVersionElement
? "A request with a 'topologyVersion' must include 'maxAwaitTimeMS'"
: "A request with 'maxAwaitTimeMS' must include a 'topologyVersion'"),
- !clientTopologyVersion && !maxAwaitTimeMS);
+ !topologyVersionElement && !maxAwaitTimeMSField);
}
auto result = replyBuilder->getBodyBuilder();
@@ -158,54 +166,52 @@ public:
// Try to parse the optional 'helloOk' field. On mongos, if we see this field, we will
// respond with helloOk: true so the client knows that it can continue to send the hello
// command to mongos.
- if (auto helloOk = cmd.getHelloOk()) {
+ bool helloOk;
+ Status status = bsonExtractBooleanField(cmdObj, "helloOk", &helloOk);
+ if (status.isOK()) {
// If the hello request contains a "helloOk" field, set _supportsHello on the Client
// to the value.
- client->setSupportsHello(*helloOk);
+ client->setSupportsHello(helloOk);
// Attach helloOk: true to the response so that the client knows the server supports
// the hello command.
result.append("helloOk", true);
+ } else if (status.code() != ErrorCodes::NoSuchKey) {
+ uassertStatusOK(status);
}
if (MONGO_unlikely(appendHelloOkToHelloResponse.shouldFail())) {
result.append("clientSupportsHello", client->supportsHello());
}
- result.appendNumber(HelloCommandReply::kMaxBsonObjectSizeFieldName, BSONObjMaxUserSize);
- result.appendNumber(HelloCommandReply::kMaxMessageSizeBytesFieldName, MaxMessageSizeBytes);
- result.appendNumber(HelloCommandReply::kMaxWriteBatchSizeFieldName,
- write_ops::kMaxWriteBatchSize);
- result.appendDate(HelloCommandReply::kLocalTimeFieldName, jsTime());
- result.append(HelloCommandReply::kLogicalSessionTimeoutMinutesFieldName,
- localLogicalSessionTimeoutMinutes);
- result.appendNumber(HelloCommandReply::kConnectionIdFieldName,
- opCtx->getClient()->getConnectionId());
+ result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize);
+ result.appendNumber("maxMessageSizeBytes", MaxMessageSizeBytes);
+ result.appendNumber("maxWriteBatchSize", write_ops::kMaxWriteBatchSize);
+ result.appendDate("localTime", jsTime());
+ result.append("logicalSessionTimeoutMinutes", localLogicalSessionTimeoutMinutes);
+ result.appendNumber("connectionId", opCtx->getClient()->getConnectionId());
// Mongos tries to keep exactly the same version range of the server for which
// it is compiled.
auto wireSpec = WireSpec::instance().get();
- result.append(HelloCommandReply::kMaxWireVersionFieldName,
- wireSpec->incomingExternalClient.maxWireVersion);
- result.append(HelloCommandReply::kMinWireVersionFieldName,
- wireSpec->incomingExternalClient.minWireVersion);
+ result.append("maxWireVersion", wireSpec->incomingExternalClient.maxWireVersion);
+ result.append("minWireVersion", wireSpec->incomingExternalClient.minWireVersion);
{
const auto& serverParams = ServerParameterSet::getGlobal()->getMap();
- auto iter = serverParams.find(kAutomationServiceDescriptorFieldName);
- if (iter != serverParams.end() && iter->second) {
- iter->second->append(opCtx, result, kAutomationServiceDescriptorFieldName);
- }
+ auto iter = serverParams.find("automationServiceDescriptor");
+ if (iter != serverParams.end() && iter->second)
+ iter->second->append(opCtx, result, "automationServiceDescriptor");
}
MessageCompressorManager::forSession(opCtx->getClient()->session())
- .serverNegotiate(cmd.getCompression(), &result);
+ .serverNegotiate(cmdObj, &result);
if (opCtx->isExhaust()) {
LOGV2_DEBUG(23872, 3, "Using exhaust for hello protocol");
uassert(51763,
"A hello/isMaster request with exhaust must specify 'maxAwaitTimeMS'",
- maxAwaitTimeMS);
+ maxAwaitTimeMSField);
invariant(clientTopologyVersion);
InExhaustHello::get(opCtx->getClient()->session().get())
@@ -218,59 +224,45 @@ public:
// command parameters should be reused as the next BSONObj command parameters.
replyBuilder->setNextInvocation(boost::none);
} else {
- BSONObjBuilder niBuilder;
- for (const auto& elem : cmdObj) {
- if (elem.fieldNameStringData() == HelloCommand::kTopologyVersionFieldName) {
- BSONObjBuilder tvBuilder(
- niBuilder.subobjStart(HelloCommand::kTopologyVersionFieldName));
- currentMongosTopologyVersion.serialize(&tvBuilder);
+ BSONObjBuilder nextInvocationBuilder;
+ for (auto&& elt : cmdObj) {
+ if (elt.fieldNameStringData() == "topologyVersion"_sd) {
+ BSONObjBuilder topologyVersionBuilder(
+ nextInvocationBuilder.subobjStart("topologyVersion"));
+ currentMongosTopologyVersion.serialize(&topologyVersionBuilder);
} else {
- niBuilder.append(elem);
+ nextInvocationBuilder.append(elt);
}
}
- replyBuilder->setNextInvocation(niBuilder.obj());
+ replyBuilder->setNextInvocation(nextInvocationBuilder.obj());
}
}
- handleHelloAuth(opCtx, cmd, &result);
-
- if (getTestCommandsEnabled()) {
- validateResult(&result);
- }
+ handleHelloAuth(opCtx, cmdObj, &result);
return true;
}
- void validateResult(BSONObjBuilder* result) {
- auto ret = result->asTempObj();
- if (ret[ErrorReply::kErrmsgFieldName].eoo()) {
- // Nominal success case, parse the object as-is.
- HelloCommandReply::parse({"hello.reply"}, ret);
- } else {
- // Something went wrong, still try to parse, but accept a few ignorable fields.
- StringDataSet ignorable({ErrorReply::kCodeFieldName, ErrorReply::kErrmsgFieldName});
- HelloCommandReply::parse({"hello.reply"}, ret.removeFields(ignorable));
- }
- }
-
protected:
CmdHello(const StringData cmdName, const std::initializer_list<StringData>& alias)
: BasicCommandWithReplyBuilderInterface(cmdName, alias) {}
- virtual bool useLegacyResponseFields() const {
+ virtual bool useLegacyResponseFields() {
return false;
}
} hello;
class CmdIsMaster : public CmdHello {
+
public:
CmdIsMaster() : CmdHello(kCamelCaseIsMasterString, {kLowerCaseIsMasterString}) {}
protected:
- bool useLegacyResponseFields() const final {
+ bool useLegacyResponseFields() override {
return true;
}
+
} isMaster;
} // namespace
diff --git a/src/mongo/transport/message_compressor_manager.cpp b/src/mongo/transport/message_compressor_manager.cpp
index b6a60cdcebd..4b6f547b372 100644
--- a/src/mongo/transport/message_compressor_manager.cpp
+++ b/src/mongo/transport/message_compressor_manager.cpp
@@ -256,23 +256,24 @@ void MessageCompressorManager::clientFinish(const BSONObj& input) {
}
}
-void MessageCompressorManager::serverNegotiate(
- const boost::optional<std::vector<StringData>>& clientCompressors, BSONObjBuilder* result) {
+void MessageCompressorManager::serverNegotiate(const BSONObj& input, BSONObjBuilder* output) {
LOGV2_DEBUG(22934, 3, "Starting server-side compression negotiation");
- // No advertised compressions, just asking for the last negotiated result.
- if (!clientCompressors) {
+ auto elem = input.getField("compression");
+ // If the "compression" field is missing, then this isMaster request is requesting information
+ // rather than doing a negotiation
+ if (elem.eoo()) {
// If we haven't negotiated any compressors yet, then don't append anything to the
// output - this makes this compatible with older versions of MongoDB that don't
// support compression.
- std::vector<std::string> ret;
- if (_negotiated.empty()) {
- LOGV2_DEBUG(22935, 3, "Compression negotiation not requested by client");
- } else {
- BSONArrayBuilder sub(result->subarrayStart("compression"));
+ if (_negotiated.size() > 0) {
+ BSONArrayBuilder sub(output->subarrayStart("compression"));
for (const auto& algo : _negotiated) {
- sub << algo->getName();
+ sub.append(algo->getName());
}
+ sub.doneFast();
+ } else {
+ LOGV2_DEBUG(22935, 3, "Compression negotiation not requested by client");
}
return;
}
@@ -282,13 +283,16 @@ void MessageCompressorManager::serverNegotiate(
_negotiated.clear();
// First we go through all the compressor names that the client has requested support for
- if (clientCompressors->empty()) {
+ BSONObj theirObj = elem.Obj();
+
+ if (!theirObj.nFields()) {
LOGV2_DEBUG(22936, 3, "No compressors provided");
return;
}
- for (const auto& curName : *clientCompressors) {
+ for (const auto& elem : theirObj) {
MessageCompressorBase* cur;
+ auto curName = elem.checkAndGetStringData();
// If the MessageCompressorRegistry knows about a compressor with that name, then it is
// valid and we add it to our list of negotiated compressors.
if ((cur = _registry->getCompressor(curName))) {
@@ -309,13 +313,14 @@ void MessageCompressorManager::serverNegotiate(
// If the number of compressors that were eventually negotiated is greater than 0, then
// we should send that back to the client.
- if (_negotiated.empty()) {
- LOGV2_DEBUG(22939, 3, "Could not agree on compressor to use");
- } else {
- BSONArrayBuilder sub(result->subarrayStart("compression"));
- for (const auto& algo : _negotiated) {
- sub << algo->getName();
+ if (_negotiated.size() > 0) {
+ BSONArrayBuilder sub(output->subarrayStart("compression"));
+ for (auto algo : _negotiated) {
+ sub.append(algo->getName());
}
+ sub.doneFast();
+ } else {
+ LOGV2_DEBUG(22939, 3, "Could not agree on compressor to use");
}
}
diff --git a/src/mongo/transport/message_compressor_manager.h b/src/mongo/transport/message_compressor_manager.h
index 9f1a1de35e3..467d942b4c4 100644
--- a/src/mongo/transport/message_compressor_manager.h
+++ b/src/mongo/transport/message_compressor_manager.h
@@ -81,11 +81,14 @@ public:
/*
* Called by a server that has received an isMaster request.
*
+ * This looks for a BSON array called "compression" in input and appends the union of that
+ * array and the result of _registry->getCompressorNames(). The first name in the compression
+ * array in input will be used in subsequent calls to compressMessage
+ *
* If no compressors are configured that match those requested by the client, then it will
* not append anything to the BSONObjBuilder output.
*/
- void serverNegotiate(const boost::optional<std::vector<StringData>>& clientCompressors,
- BSONObjBuilder*);
+ void serverNegotiate(const BSONObj& input, BSONObjBuilder* output);
/*
* Returns a new Message containing the compressed contentx of 'msg'. If compressorId is null,
diff --git a/src/mongo/transport/message_compressor_manager_test.cpp b/src/mongo/transport/message_compressor_manager_test.cpp
index 24cff651933..979e75d0842 100644
--- a/src/mongo/transport/message_compressor_manager_test.cpp
+++ b/src/mongo/transport/message_compressor_manager_test.cpp
@@ -85,8 +85,7 @@ void checkNegotiationResult(const BSONObj& result, const std::vector<std::string
}
}
-void checkServerNegotiation(const boost::optional<std::vector<StringData>>& input,
- const std::vector<std::string>& expected) {
+void checkServerNegotiation(const BSONObj& input, const std::vector<std::string>& expected) {
auto registry = buildRegistry();
MessageCompressorManager manager(&registry);
@@ -106,8 +105,8 @@ void checkFidelity(const Message& msg, std::unique_ptr<MessageCompressorBase> co
registry.finalizeSupportedCompressors().transitional_ignore();
MessageCompressorManager mgr(&registry);
+ auto negotiator = BSON("isMaster" << 1 << "compression" << BSON_ARRAY(compressorName));
BSONObjBuilder negotiatorOut;
- std::vector<StringData> negotiator({compressorName});
mgr.serverNegotiate(negotiator, &negotiatorOut);
checkNegotiationResult(negotiatorOut.done(), {compressorName});
@@ -183,46 +182,26 @@ Message buildMessage() {
TEST(MessageCompressorManager, NoCompressionRequested) {
auto input = BSON("isMaster" << 1);
- checkServerNegotiation(boost::none, {});
+ checkServerNegotiation(input, {});
}
TEST(MessageCompressorManager, NormalCompressionRequested) {
- std::vector<StringData> input{"noop"_sd};
+ auto input = BSON("isMaster" << 1 << "compression" << BSON_ARRAY("noop"));
checkServerNegotiation(input, {"noop"});
}
TEST(MessageCompressorManager, BadCompressionRequested) {
- std::vector<StringData> input{"fakecompressor"_sd};
+ auto input = BSON("isMaster" << 1 << "compression" << BSON_ARRAY("fakecompressor"));
checkServerNegotiation(input, {});
}
TEST(MessageCompressorManager, BadAndGoodCompressionRequested) {
- std::vector<StringData> input{"fakecompressor"_sd, "noop"_sd};
+ auto input = BSON("isMaster" << 1 << "compression"
+ << BSON_ARRAY("fakecompressor"
+ << "noop"));
checkServerNegotiation(input, {"noop"});
}
-// Transitional: Parse BSON "isMaster"-like docs for compressor lists.
-boost::optional<std::vector<StringData>> parseBSON(BSONObj input) {
- auto elem = input["compression"];
- if (!elem) {
- return boost::none;
- }
-
- uassert(ErrorCodes::BadValue,
- str::stream() << "'compression' is not an array: " << elem,
- elem.type() == Array);
-
- std::vector<StringData> ret;
- for (const auto& e : elem.Obj()) {
- uassert(ErrorCodes::BadValue,
- str::stream() << "'compression' element is not a string: " << e,
- e.type() == String);
- ret.push_back(e.valueStringData());
- }
-
- return ret;
-}
-
TEST(MessageCompressorManager, FullNormalCompression) {
auto registry = buildRegistry();
MessageCompressorManager clientManager(&registry);
@@ -234,7 +213,7 @@ TEST(MessageCompressorManager, FullNormalCompression) {
checkNegotiationResult(clientObj, {"noop"});
BSONObjBuilder serverOutput;
- serverManager.serverNegotiate(parseBSON(clientObj), &serverOutput);
+ serverManager.serverNegotiate(clientObj, &serverOutput);
auto serverObj = serverOutput.done();
checkNegotiationResult(serverObj, {"noop"});
@@ -306,7 +285,7 @@ TEST(MessageCompressorManager, SERVER_28008) {
clientManager.clientBegin(&clientOutput);
auto clientObj = clientOutput.done();
BSONObjBuilder serverOutput;
- serverManager.serverNegotiate(parseBSON(clientObj), &serverOutput);
+ serverManager.serverNegotiate(clientObj, &serverOutput);
auto serverObj = serverOutput.done();
clientManager.clientFinish(serverObj);