diff options
author | Billy Donahue <billy.donahue@mongodb.com> | 2018-03-05 15:48:48 -0500 |
---|---|---|
committer | Billy Donahue <billy.donahue@mongodb.com> | 2018-03-06 10:04:53 -0500 |
commit | e3a3f54a6c0ec4f471e0aff7a48b8a30f8c0e9cb (patch) | |
tree | 9c2f4ac5a4d8751336397c8af59a08bf78f1c201 /src/mongo/db/commands.cpp | |
parent | 5dfa372b25b1a26bb12e09fdb029ff219b0f5343 (diff) | |
download | mongo-e3a3f54a6c0ec4f471e0aff7a48b8a30f8c0e9cb.tar.gz |
SERVER-33065 CommandReplyBuilder and CommandInvocation
Revert "Revert "SERVER-33065 CommandReplyBuilder and CommandInvocation""
This reverts commit 74177edb35b6ce7594e4751051010ceade592484.
Leave out the unintentional repl/ change this time.
Diffstat (limited to 'src/mongo/db/commands.cpp')
-rw-r--r-- | src/mongo/db/commands.cpp | 169 |
1 files changed, 127 insertions, 42 deletions
diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index fe1b6c0fc2d..17034ec2bf2 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -71,6 +71,23 @@ const WriteConcernOptions kMajorityWriteConcern( WriteConcernOptions::SyncMode::UNSET, Seconds(60)); +// A facade presenting CommandDefinition as an audit::CommandInterface. +class CommandAuditHook : public audit::CommandInterface { +public: + explicit CommandAuditHook(Command* command) : _command(command) {} + + void redactForLogging(mutablebson::Document* cmdObj) const final { + _command->redactForLogging(cmdObj); + } + + std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final { + return _command->parseNs(dbname, cmdObj); + } + +private: + Command* _command; +}; + } // namespace @@ -80,18 +97,22 @@ const WriteConcernOptions kMajorityWriteConcern( BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) { auto command = globalCommandRegistry()->findCommand(request.getCommandName()); invariant(command); - BSONObjBuilder out; + BufBuilder bb; + CommandReplyBuilder crb(BSONObjBuilder{bb}); try { - bool ok = command->publicRun(opCtx, request, out); - appendCommandStatus(out, ok); + auto invocation = command->parse(opCtx, request); + invocation->run(opCtx, &crb); + auto body = crb.getBodyBuilder(); + CommandHelpers::extractOrAppendOk(body); } catch (const StaleConfigException&) { // These exceptions are intended to be handled at a higher level. throw; } catch (const DBException& ex) { - out.resetToEmpty(); - appendCommandStatus(out, ex.toStatus()); + auto body = crb.getBodyBuilder(); + body.resetToEmpty(); + appendCommandStatus(body, ex.toStatus()); } - return out.obj(); + return BSONObj(bb.release()); } std::string CommandHelpers::parseNsFullyQualified(StringData dbname, const BSONObj& cmdObj) { @@ -167,6 +188,16 @@ void CommandHelpers::appendCommandStatus(BSONObjBuilder& result, } } +bool CommandHelpers::extractOrAppendOk(BSONObjBuilder& reply) { + if (auto okField = reply.asTempObj()["ok"]) { + // If ok is present, use its truthiness. + return okField.trueValue(); + } + // Missing "ok" field is an implied success. + CommandHelpers::appendCommandStatus(reply, true); + return true; +} + void CommandHelpers::appendCommandWCStatus(BSONObjBuilder& result, const Status& awaitReplicationStatus, const WriteConcernResult& wcResult) { @@ -306,10 +337,99 @@ bool CommandHelpers::isHelpRequest(const BSONElement& helpElem) { constexpr StringData CommandHelpers::kHelpFieldName; ////////////////////////////////////////////////////////////// +// CommandReplyBuilder + +CommandReplyBuilder::CommandReplyBuilder(BSONObjBuilder bodyObj) + : _bodyBuf(&bodyObj.bb()), _bodyOffset(bodyObj.offset()) { + // CommandReplyBuilder requires that bodyObj build into an externally-owned buffer. + invariant(!bodyObj.owned()); + bodyObj.doneFast(); +} + +BSONObjBuilder CommandReplyBuilder::getBodyBuilder() const { + return BSONObjBuilder(BSONObjBuilder::ResumeBuildingTag{}, *_bodyBuf, _bodyOffset); +} + +void CommandReplyBuilder::reset() { + getBodyBuilder().resetToEmpty(); +} + +////////////////////////////////////////////////////////////// +// CommandInvocation + +CommandInvocation::~CommandInvocation() = default; + +////////////////////////////////////////////////////////////// // Command +class Command::InvocationShim final : public CommandInvocation { +public: + InvocationShim(OperationContext*, const OpMsgRequest& request, Command* command) + : CommandInvocation(command), + _command(command), + _request(&request), + _dbName(_request->getDatabase().toString()) {} + +private: + void run(OperationContext* opCtx, CommandReplyBuilder* result) override { + try { + BSONObjBuilder bob = result->getBodyBuilder(); + bool ok = _command->enhancedRun(opCtx, *_request, bob); + CommandHelpers::appendCommandStatus(bob, ok); + } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) { + CommandAuditHook hook(_command); + audit::logCommandAuthzCheck( + opCtx->getClient(), *_request, &hook, ErrorCodes::Unauthorized); + throw; + } + } + + void explain(OperationContext* opCtx, + ExplainOptions::Verbosity verbosity, + BSONObjBuilder* result) override { + uassertStatusOK(_command->explain(opCtx, *_request, verbosity, result)); + } + + NamespaceString ns() const override { + return NamespaceString(_command->parseNs(_dbName, cmdObj())); + } + + bool supportsWriteConcern() const override { + return _command->supportsWriteConcern(cmdObj()); + } + + Command::AllowedOnSecondary secondaryAllowed(ServiceContext* context) const override { + return _command->secondaryAllowed(context); + } + + bool supportsReadConcern(repl::ReadConcernLevel level) const override { + return _command->supportsReadConcern(_dbName, cmdObj(), level); + } + + bool allowsAfterClusterTime() const override { + return _command->allowsAfterClusterTime(cmdObj()); + } + + void doCheckAuthorization(OperationContext* opCtx) const override { + uassertStatusOK(_command->checkAuthForRequest(opCtx, *_request)); + } + + const BSONObj& cmdObj() const { + return _request->body; + } + + Command* const _command; + const OpMsgRequest* const _request; + const std::string _dbName; +}; + Command::~Command() = default; +std::unique_ptr<CommandInvocation> Command::parse(OperationContext* opCtx, + const OpMsgRequest& request) { + return stdx::make_unique<InvocationShim>(opCtx, request, this); +} + std::string Command::parseNs(const std::string& dbname, const BSONObj& cmdObj) const { BSONElement first = cmdObj.firstElement(); if (first.type() != mongo::String) @@ -335,8 +455,7 @@ Command::Command(StringData name, StringData oldName) } Status Command::explain(OperationContext* opCtx, - const std::string& dbname, - const BSONObj& cmdObj, + const OpMsgRequest& request, ExplainOptions::Verbosity verbosity, BSONObjBuilder* out) const { return {ErrorCodes::IllegalOperation, str::stream() << "Cannot explain cmd: " << getName()}; @@ -396,25 +515,6 @@ static Status _checkAuthorizationImpl(Command* c, return Status::OK(); } -namespace { -// A facade presenting CommandDefinition as an audit::CommandInterface. -class CommandAuditHook : public audit::CommandInterface { -public: - explicit CommandAuditHook(Command* command) : _command(command) {} - - void redactForLogging(mutablebson::Document* cmdObj) const final { - _command->redactForLogging(cmdObj); - } - - std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const final { - return _command->parseNs(dbname, cmdObj); - } - -private: - Command* _command; -}; -} // namespace - Status Command::checkAuthorization(Command* c, OperationContext* opCtx, const OpMsgRequest& request) { @@ -427,21 +527,6 @@ Status Command::checkAuthorization(Command* c, return status; } -bool Command::publicRun(OperationContext* opCtx, - const OpMsgRequest& request, - BSONObjBuilder& result) { - try { - return enhancedRun(opCtx, request, result); - } catch (const DBException& e) { - if (e.code() == ErrorCodes::Unauthorized) { - CommandAuditHook hook(this); - audit::logCommandAuthzCheck( - opCtx->getClient(), request, &hook, ErrorCodes::Unauthorized); - } - throw; - } -} - void Command::generateHelpResponse(OperationContext* opCtx, rpc::ReplyBuilderInterface* replyBuilder, const Command& command) { |