diff options
Diffstat (limited to 'src/mongo/client')
-rw-r--r-- | src/mongo/client/authenticate.cpp | 9 | ||||
-rw-r--r-- | src/mongo/client/authenticate.h | 2 | ||||
-rw-r--r-- | src/mongo/client/authenticate_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/client/dbclient.cpp | 13 | ||||
-rw-r--r-- | src/mongo/client/fetcher.cpp | 12 | ||||
-rw-r--r-- | src/mongo/client/fetcher_test.cpp | 36 | ||||
-rw-r--r-- | src/mongo/client/remote_command_retry_scheduler.cpp | 2 | ||||
-rw-r--r-- | src/mongo/client/remote_command_retry_scheduler_test.cpp | 61 | ||||
-rw-r--r-- | src/mongo/client/remote_command_runner_impl.cpp | 219 | ||||
-rw-r--r-- | src/mongo/client/sasl_client_authenticate_impl.cpp | 2 |
10 files changed, 284 insertions, 74 deletions
diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp index 224517e00be..7d45da9ef08 100644 --- a/src/mongo/client/authenticate.cpp +++ b/src/mongo/client/authenticate.cpp @@ -159,7 +159,7 @@ void authMongoCR(RunCommandHook runCommand, const BSONObj& params, AuthCompletio // Ensure response was valid std::string nonce; - BSONObj nonceResponse = response.getValue().data; + BSONObj nonceResponse = response.data; auto valid = bsonExtractStringField(nonceResponse, "nonce", &nonce); if (!valid.isOK()) return handler({ErrorCodes::AuthenticationFailed, @@ -235,7 +235,8 @@ void authX509(RunCommandHook runCommand, // bool isFailedAuthOk(const AuthResponse& response) { - return (response == ErrorCodes::AuthenticationFailed && serverGlobalParams.transitionToAuth); + return (response.status == ErrorCodes::AuthenticationFailed && + serverGlobalParams.transitionToAuth); } void auth(RunCommandHook runCommand, @@ -305,9 +306,9 @@ void authenticateClient(const BSONObj& params, // NOTE: this assumes that runCommand executes synchronously. asyncAuth(runCommand, params, hostname, clientName, [](AuthResponse response) { // DBClient expects us to throw in case of an auth error. - uassertStatusOK(response); + uassertStatusOK(response.status); - auto serverResponse = response.getValue().data; + auto serverResponse = response.data; uassert(ErrorCodes::AuthenticationFailed, serverResponse["errmsg"].str(), isOk(serverResponse)); diff --git a/src/mongo/client/authenticate.h b/src/mongo/client/authenticate.h index 65c649c49c2..6ad596477bf 100644 --- a/src/mongo/client/authenticate.h +++ b/src/mongo/client/authenticate.h @@ -43,7 +43,7 @@ class BSONObj; namespace auth { -using AuthResponse = StatusWith<executor::RemoteCommandResponse>; +using AuthResponse = executor::RemoteCommandResponse; using AuthCompletionHandler = stdx::function<void(AuthResponse)>; using RunCommandResultHandler = AuthCompletionHandler; using RunCommandHook = diff --git a/src/mongo/client/authenticate_test.cpp b/src/mongo/client/authenticate_test.cpp index 3044da9f6cd..97b876cf814 100644 --- a/src/mongo/client/authenticate_test.cpp +++ b/src/mongo/client/authenticate_test.cpp @@ -92,7 +92,7 @@ public: // Then pop a response and call the handler ASSERT(!_responses.empty()); - handler(StatusWith<RemoteCommandResponse>(_responses.front())); + handler(_responses.front()); _responses.pop(); } diff --git a/src/mongo/client/dbclient.cpp b/src/mongo/client/dbclient.cpp index 254a537c255..cbf72c4ae7d 100644 --- a/src/mongo/client/dbclient.cpp +++ b/src/mongo/client/dbclient.cpp @@ -445,8 +445,7 @@ void DBClientWithCommands::_auth(const BSONObj& params) { Milliseconds millis(Date_t::now() - start); // Hand control back to authenticateClient() - handler(StatusWith<RemoteCommandResponse>( - RemoteCommandResponse(data, metadata, millis))); + handler({data, metadata, millis}); } catch (...) { handler(exceptionToStatus()); @@ -713,7 +712,7 @@ private: /** * Initializes the wire version of conn, and returns the isMaster reply. */ -StatusWith<executor::RemoteCommandResponse> initWireVersion(DBClientConnection* conn) { +executor::RemoteCommandResponse initWireVersion(DBClientConnection* conn) { try { // We need to force the usage of OP_QUERY on this command, even if we have previously // detected support for OP_COMMAND on a connection. This is necessary to handle the case @@ -773,16 +772,16 @@ Status DBClientConnection::connect(const HostAndPort& serverAddress) { auto swIsMasterReply = initWireVersion(this); if (!swIsMasterReply.isOK()) { _failed = true; - return swIsMasterReply.getStatus(); + return swIsMasterReply.status; } // Ensure that the isMaster response is "ok:1". - auto isMasterStatus = getStatusFromCommandResult(swIsMasterReply.getValue().data); + auto isMasterStatus = getStatusFromCommandResult(swIsMasterReply.data); if (!isMasterStatus.isOK()) { return isMasterStatus; } - auto swProtocolSet = rpc::parseProtocolSetFromIsMasterReply(swIsMasterReply.getValue().data); + auto swProtocolSet = rpc::parseProtocolSetFromIsMasterReply(swIsMasterReply.data); if (!swProtocolSet.isOK()) { return swProtocolSet.getStatus(); } @@ -799,7 +798,7 @@ Status DBClientConnection::connect(const HostAndPort& serverAddress) { } if (_hook) { - auto validationStatus = _hook(swIsMasterReply.getValue()); + auto validationStatus = _hook(swIsMasterReply); if (!validationStatus.isOK()) { // Disconnect and mark failed. _failed = true; diff --git a/src/mongo/client/fetcher.cpp b/src/mongo/client/fetcher.cpp index 5aa46e549cd..e2a8bdf2402 100644 --- a/src/mongo/client/fetcher.cpp +++ b/src/mongo/client/fetcher.cpp @@ -301,7 +301,7 @@ Status Fetcher::_scheduleGetMore(const BSONObj& cmdObj) { void Fetcher::_callback(const RemoteCommandCallbackArgs& rcbd, const char* batchFieldName) { if (!rcbd.response.isOK()) { - _work(StatusWith<Fetcher::QueryResponse>(rcbd.response.getStatus()), nullptr, nullptr); + _work(StatusWith<Fetcher::QueryResponse>(rcbd.response.status), nullptr, nullptr); _finishCallback(); return; } @@ -312,7 +312,7 @@ void Fetcher::_callback(const RemoteCommandCallbackArgs& rcbd, const char* batch return; } - const BSONObj& queryResponseObj = rcbd.response.getValue().data; + const BSONObj& queryResponseObj = rcbd.response.data; Status status = getStatusFromCommandResult(queryResponseObj); if (!status.isOK()) { _work(StatusWith<Fetcher::QueryResponse>(status), nullptr, nullptr); @@ -328,8 +328,8 @@ void Fetcher::_callback(const RemoteCommandCallbackArgs& rcbd, const char* batch return; } - batchData.otherFields.metadata = std::move(rcbd.response.getValue().metadata); - batchData.elapsedMillis = rcbd.response.getValue().elapsedMillis; + batchData.otherFields.metadata = std::move(rcbd.response.metadata); + batchData.elapsedMillis = rcbd.response.elapsedMillis.value_or(Milliseconds{0}); { stdx::lock_guard<stdx::mutex> lk(_mutex); batchData.first = _first; @@ -380,10 +380,10 @@ void Fetcher::_sendKillCursors(const CursorId id, const NamespaceString& nss) { if (id) { auto logKillCursorsResult = [](const RemoteCommandCallbackArgs& args) { if (!args.response.isOK()) { - warning() << "killCursors command task failed: " << args.response.getStatus(); + warning() << "killCursors command task failed: " << args.response.status; return; } - auto status = getStatusFromCommandResult(args.response.getValue().data); + auto status = getStatusFromCommandResult(args.response.data); if (!status.isOK()) { warning() << "killCursors command failed: " << status; } diff --git a/src/mongo/client/fetcher_test.cpp b/src/mongo/client/fetcher_test.cpp index b20cb9a099f..26778806b6c 100644 --- a/src/mongo/client/fetcher_test.cpp +++ b/src/mongo/client/fetcher_test.cpp @@ -44,6 +44,8 @@ using namespace mongo; using executor::NetworkInterfaceMock; using executor::TaskExecutor; +using ResponseStatus = TaskExecutor::ResponseStatus; + const HostAndPort source("localhost", -1); const BSONObj findCmdObj = BSON("find" << "coll"); @@ -61,12 +63,11 @@ public: void processNetworkResponse(const BSONObj& obj, ReadyQueueState readyQueueStateAfterProcessing, FetcherState fetcherStateAfterProcessing); - void processNetworkResponse(const BSONObj& obj, - Milliseconds elapsed, + void processNetworkResponse(const ResponseStatus, ReadyQueueState readyQueueStateAfterProcessing, FetcherState fetcherStateAfterProcessing); - void processNetworkResponse(ErrorCodes::Error code, - const std::string& reason, + void processNetworkResponse(const BSONObj& obj, + Milliseconds elapsed, ReadyQueueState readyQueueStateAfterProcessing, FetcherState fetcherStateAfterProcessing); @@ -148,12 +149,11 @@ void FetcherTest::processNetworkResponse(const BSONObj& obj, finishProcessingNetworkResponse(readyQueueStateAfterProcessing, fetcherStateAfterProcessing); } -void FetcherTest::processNetworkResponse(ErrorCodes::Error code, - const std::string& reason, +void FetcherTest::processNetworkResponse(ResponseStatus rs, ReadyQueueState readyQueueStateAfterProcessing, FetcherState fetcherStateAfterProcessing) { executor::NetworkInterfaceMock::InNetworkGuard guard(getNet()); - getNet()->scheduleErrorResponse({code, reason}); + getNet()->scheduleErrorResponse(rs); finishProcessingNetworkResponse(readyQueueStateAfterProcessing, fetcherStateAfterProcessing); } @@ -373,8 +373,8 @@ TEST_F(FetcherTest, ScheduleButShutdown) { TEST_F(FetcherTest, FindCommandFailed1) { ASSERT_OK(fetcher->schedule()); - processNetworkResponse( - ErrorCodes::BadValue, "bad hint", ReadyQueueState::kEmpty, FetcherState::kInactive); + auto rs = ResponseStatus(ErrorCodes::BadValue, "bad hint", Milliseconds(0)); + processNetworkResponse(rs, ReadyQueueState::kEmpty, FetcherState::kInactive); ASSERT_EQUALS(ErrorCodes::BadValue, status.code()); ASSERT_EQUALS("bad hint", status.reason()); ASSERT_FALSE(fetcher->inShutdown_forTest()); @@ -939,7 +939,7 @@ TEST_F(FetcherTest, UpdateNextActionAfterSecondBatch) { ASSERT_EQUALS(cursorId, cursors.front().numberLong()); // Failed killCursors command response should be logged. - getNet()->scheduleSuccessfulResponse(noi, {BSON("ok" << false), {}}); + getNet()->scheduleSuccessfulResponse(noi, {BSON("ok" << false), {}, Milliseconds(0)}); getNet()->runReadyNetworkOperations(); } @@ -1049,12 +1049,10 @@ TEST_F(FetcherTest, FetcherAppliesRetryPolicyToFirstCommandButNotToGetMoreReques // Retry policy is applied to find command. const BSONObj doc = BSON("_id" << 1); - processNetworkResponse( - ErrorCodes::BadValue, "first", ReadyQueueState::kHasReadyRequests, FetcherState::kActive); - processNetworkResponse(ErrorCodes::InternalError, - "second", - ReadyQueueState::kHasReadyRequests, - FetcherState::kActive); + auto rs = ResponseStatus(ErrorCodes::BadValue, "first", Milliseconds(0)); + processNetworkResponse(rs, ReadyQueueState::kHasReadyRequests, FetcherState::kActive); + rs = ResponseStatus(ErrorCodes::InternalError, "second", Milliseconds(0)); + processNetworkResponse(rs, ReadyQueueState::kHasReadyRequests, FetcherState::kActive); processNetworkResponse(BSON("cursor" << BSON("id" << 1LL << "ns" << "db.coll" << "firstBatch" @@ -1070,11 +1068,9 @@ TEST_F(FetcherTest, FetcherAppliesRetryPolicyToFirstCommandButNotToGetMoreReques ASSERT_EQUALS(doc, documents.front()); ASSERT_TRUE(Fetcher::NextAction::kGetMore == nextAction); + rs = ResponseStatus(ErrorCodes::OperationFailed, "getMore failed", Milliseconds(0)); // No retry policy for subsequent getMore commands. - processNetworkResponse(ErrorCodes::OperationFailed, - "getMore failed", - ReadyQueueState::kEmpty, - FetcherState::kInactive); + processNetworkResponse(rs, ReadyQueueState::kEmpty, FetcherState::kInactive); ASSERT_EQUALS(ErrorCodes::OperationFailed, status); ASSERT_FALSE(fetcher->inShutdown_forTest()); } diff --git a/src/mongo/client/remote_command_retry_scheduler.cpp b/src/mongo/client/remote_command_retry_scheduler.cpp index 8c20d0af925..ded4a10d964 100644 --- a/src/mongo/client/remote_command_retry_scheduler.cpp +++ b/src/mongo/client/remote_command_retry_scheduler.cpp @@ -207,7 +207,7 @@ Status RemoteCommandRetryScheduler::_schedule_inlock(std::size_t requestCount) { void RemoteCommandRetryScheduler::_remoteCommandCallback( const executor::TaskExecutor::RemoteCommandCallbackArgs& rcba, std::size_t requestCount) { - auto status = rcba.response.getStatus(); + auto status = rcba.response.status; if (status.isOK() || status == ErrorCodes::CallbackCanceled || requestCount == _retryPolicy->getMaximumAttempts() || diff --git a/src/mongo/client/remote_command_retry_scheduler_test.cpp b/src/mongo/client/remote_command_retry_scheduler_test.cpp index 6e2c70fe9dc..705324f94c5 100644 --- a/src/mongo/client/remote_command_retry_scheduler_test.cpp +++ b/src/mongo/client/remote_command_retry_scheduler_test.cpp @@ -48,6 +48,7 @@ namespace { using namespace mongo; +using ResponseStatus = executor::TaskExecutor::ResponseStatus; class CallbackResponseSaver; @@ -56,8 +57,8 @@ public: void start(RemoteCommandRetryScheduler* scheduler); void checkCompletionStatus(RemoteCommandRetryScheduler* scheduler, const CallbackResponseSaver& callbackResponseSaver, - const executor::TaskExecutor::ResponseStatus& response); - void processNetworkResponse(const executor::TaskExecutor::ResponseStatus& response); + const ResponseStatus& response); + void processNetworkResponse(const ResponseStatus& response); void runReadyNetworkOperations(); protected: @@ -76,10 +77,10 @@ public: */ void operator()(const executor::TaskExecutor::RemoteCommandCallbackArgs& rcba); - std::vector<StatusWith<executor::RemoteCommandResponse>> getResponses() const; + std::vector<ResponseStatus> getResponses() const; private: - std::vector<StatusWith<executor::RemoteCommandResponse>> _responses; + std::vector<ResponseStatus> _responses; }; /** @@ -119,20 +120,19 @@ void RemoteCommandRetrySchedulerTest::start(RemoteCommandRetryScheduler* schedul void RemoteCommandRetrySchedulerTest::checkCompletionStatus( RemoteCommandRetryScheduler* scheduler, const CallbackResponseSaver& callbackResponseSaver, - const executor::TaskExecutor::ResponseStatus& response) { + const ResponseStatus& response) { ASSERT_FALSE(scheduler->isActive()); auto responses = callbackResponseSaver.getResponses(); ASSERT_EQUALS(1U, responses.size()); if (response.isOK()) { - ASSERT_OK(responses.front().getStatus()); - ASSERT_EQUALS(response.getValue(), responses.front().getValue()); + ASSERT_OK(responses.front().status); + ASSERT_EQUALS(response, responses.front()); } else { - ASSERT_EQUALS(response.getStatus(), responses.front().getStatus()); + ASSERT_EQUALS(response.status, responses.front().status); } } -void RemoteCommandRetrySchedulerTest::processNetworkResponse( - const executor::TaskExecutor::ResponseStatus& response) { +void RemoteCommandRetrySchedulerTest::processNetworkResponse(const ResponseStatus& response) { auto net = getNet(); executor::NetworkInterfaceMock::InNetworkGuard guard(net); ASSERT_TRUE(net->hasReadyRequests()); @@ -163,8 +163,7 @@ void CallbackResponseSaver::operator()( _responses.push_back(rcba.response); } -std::vector<StatusWith<executor::RemoteCommandResponse>> CallbackResponseSaver::getResponses() - const { +std::vector<ResponseStatus> CallbackResponseSaver::getResponses() const { return _responses; } @@ -327,7 +326,6 @@ TEST_F(RemoteCommandRetrySchedulerTest, &scheduler, callback, {ErrorCodes::CallbackCanceled, "executor shutdown"}); } - TEST_F(RemoteCommandRetrySchedulerTest, ShuttingDownSchedulerAfterSchedulerStartupInvokesCallbackWithCallbackCanceledError) { CallbackResponseSaver callback; @@ -353,10 +351,9 @@ TEST_F(RemoteCommandRetrySchedulerTest, SchedulerInvokesCallbackOnNonRetryableEr start(&scheduler); // This should match one of the non-retryable error codes in the policy. - Status response(ErrorCodes::OperationFailed, "injected error"); - - processNetworkResponse(response); - checkCompletionStatus(&scheduler, callback, response); + ResponseStatus rs(ErrorCodes::OperationFailed, "injected error", Milliseconds(0)); + processNetworkResponse(rs); + checkCompletionStatus(&scheduler, callback, rs); } TEST_F(RemoteCommandRetrySchedulerTest, SchedulerInvokesCallbackOnFirstSuccessfulResponse) { @@ -368,8 +365,7 @@ TEST_F(RemoteCommandRetrySchedulerTest, SchedulerInvokesCallbackOnFirstSuccessfu start(&scheduler); // Elapsed time in response is ignored on successful responses. - executor::RemoteCommandResponse response( - BSON("ok" << 1 << "x" << 123), BSON("z" << 456), Milliseconds(100)); + ResponseStatus response(BSON("ok" << 1 << "x" << 123), BSON("z" << 456), Milliseconds(100)); processNetworkResponse(response); checkCompletionStatus(&scheduler, callback, response); @@ -386,11 +382,10 @@ TEST_F(RemoteCommandRetrySchedulerTest, SchedulerIgnoresEmbeddedErrorInSuccessfu // Scheduler does not parse document in a successful response for embedded errors. // This is the case with some commands (e.g. find) which do not always return errors using the // wire protocol. - executor::RemoteCommandResponse response( - BSON("ok" << 0 << "code" << int(ErrorCodes::FailedToParse) << "errmsg" - << "injected error"), - BSON("z" << 456), - Milliseconds(100)); + ResponseStatus response(BSON("ok" << 0 << "code" << int(ErrorCodes::FailedToParse) << "errmsg" + << "injected error"), + BSON("z" << 456), + Milliseconds(100)); processNetworkResponse(response); checkCompletionStatus(&scheduler, callback, response); @@ -406,14 +401,15 @@ TEST_F(RemoteCommandRetrySchedulerTest, &badExecutor, request, stdx::ref(callback), std::move(policy)); start(&scheduler); - processNetworkResponse({ErrorCodes::HostNotFound, "first"}); + processNetworkResponse({ErrorCodes::HostNotFound, "first", Milliseconds(0)}); // scheduleRemoteCommand() will fail with ErrorCodes::ShutdownInProgress when trying to send // third remote command request after processing second failed response. badExecutor.scheduleRemoteCommandFailPoint = true; - processNetworkResponse({ErrorCodes::HostNotFound, "second"}); + processNetworkResponse({ErrorCodes::HostNotFound, "second", Milliseconds(0)}); - checkCompletionStatus(&scheduler, callback, {ErrorCodes::ShutdownInProgress, ""}); + checkCompletionStatus( + &scheduler, callback, {ErrorCodes::ShutdownInProgress, "", Milliseconds(0)}); } TEST_F(RemoteCommandRetrySchedulerTest, @@ -427,10 +423,10 @@ TEST_F(RemoteCommandRetrySchedulerTest, &getExecutor(), request, stdx::ref(callback), std::move(policy)); start(&scheduler); - processNetworkResponse({ErrorCodes::HostNotFound, "first"}); - processNetworkResponse({ErrorCodes::HostUnreachable, "second"}); + processNetworkResponse({ErrorCodes::HostNotFound, "first", Milliseconds(0)}); + processNetworkResponse({ErrorCodes::HostUnreachable, "second", Milliseconds(0)}); - Status response(ErrorCodes::NetworkTimeout, "last"); + ResponseStatus response(ErrorCodes::NetworkTimeout, "last", Milliseconds(0)); processNetworkResponse(response); checkCompletionStatus(&scheduler, callback, response); } @@ -443,10 +439,9 @@ TEST_F(RemoteCommandRetrySchedulerTest, SchedulerShouldRetryUntilSuccessfulRespo &getExecutor(), request, stdx::ref(callback), std::move(policy)); start(&scheduler); - processNetworkResponse({ErrorCodes::HostNotFound, "first"}); + processNetworkResponse({ErrorCodes::HostNotFound, "first", Milliseconds(0)}); - executor::RemoteCommandResponse response( - BSON("ok" << 1 << "x" << 123), BSON("z" << 456), Milliseconds(100)); + ResponseStatus response(BSON("ok" << 1 << "x" << 123), BSON("z" << 456), Milliseconds(100)); processNetworkResponse(response); checkCompletionStatus(&scheduler, callback, response); } diff --git a/src/mongo/client/remote_command_runner_impl.cpp b/src/mongo/client/remote_command_runner_impl.cpp new file mode 100644 index 00000000000..2eef602d1f9 --- /dev/null +++ b/src/mongo/client/remote_command_runner_impl.cpp @@ -0,0 +1,219 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/client/remote_command_runner_impl.h" + +#include "mongo/base/status_with.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/query/cursor_response.h" +#include "mongo/db/query/getmore_request.h" +#include "mongo/executor/downconvert_find_and_getmore_commands.h" +#include "mongo/executor/network_connection_hook.h" +#include "mongo/rpc/get_status_from_command_result.h" +#include "mongo/rpc/protocol.h" +#include "mongo/util/assert_util.h" + +namespace mongo { +namespace { + +using executor::RemoteCommandRequest; +using ResponseStatus = executor::RemoteCommandResponse; + +/** + * Calculates the timeout for a network operation expiring at "expDate", given + * that it is now "nowDate". + * + * Returns 0ms to indicate no expiration date, a number of milliseconds until "expDate", or + * ErrorCodes::ExceededTimeLimit if "expDate" is not later than "nowDate". + */ +StatusWith<Milliseconds> getTimeoutMillis(const Date_t expDate, const Date_t nowDate) { + if (expDate == RemoteCommandRequest::kNoExpirationDate) { + return Milliseconds(0); + } + if (expDate <= nowDate) { + return {ErrorCodes::ExceededTimeLimit, + str::stream() << "Went to run command, but it was too late. " + "Expiration was set to " + << dateToISOStringUTC(expDate)}; + } + return expDate - nowDate; +} + +/** + * Peeks at error in cursor. If an error has occurred, converts the {$err: "...", code: N} + * cursor error to a Status. + */ +Status getStatusFromCursorResult(DBClientCursor& cursor) { + BSONObj error; + if (!cursor.peekError(&error)) { + return Status::OK(); + } + + BSONElement e = error.getField("code"); + return Status(e.isNumber() ? ErrorCodes::fromInt(e.numberInt()) : ErrorCodes::UnknownError, + getErrField(error).valuestrsafe()); +} + +using RequestDownconverter = StatusWith<Message> (*)(const RemoteCommandRequest&); +using ReplyUpconverter = ResponseStatus (*)(std::int32_t requestId, + StringData cursorNamespace, + const Message& response); + +template <RequestDownconverter downconvertRequest, ReplyUpconverter upconvertReply> +ResponseStatus runDownconvertedCommand(DBClientConnection* conn, + const RemoteCommandRequest& request) { + auto swDownconvertedRequest = downconvertRequest(request); + if (!swDownconvertedRequest.isOK()) { + return swDownconvertedRequest.getStatus(); + } + + Message requestMsg{std::move(swDownconvertedRequest.getValue())}; + Message responseMsg; + + try { + conn->call(requestMsg, responseMsg, true, nullptr); + } catch (...) { + return exceptionToStatus(); + } + + auto messageId = requestMsg.header().getId(); + + return upconvertReply(messageId, DbMessage(requestMsg).getns(), responseMsg); +} + +/** + * Downconverts the specified find command to a find protocol operation and sends it to the + * server on the specified connection. + */ +ResponseStatus runDownconvertedFindCommand(DBClientConnection* conn, + const RemoteCommandRequest& request) { + return runDownconvertedCommand<executor::downconvertFindCommandRequest, + executor::upconvertLegacyQueryResponse>(conn, request); +} + +/** + * Downconverts the specified getMore command to legacy getMore operation and sends it to the + * server on the specified connection. + */ +ResponseStatus runDownconvertedGetMoreCommand(DBClientConnection* conn, + const RemoteCommandRequest& request) { + return runDownconvertedCommand<executor::downconvertGetMoreCommandRequest, + executor::upconvertLegacyGetMoreResponse>(conn, request); +} + +} // namespace + +RemoteCommandRunnerImpl::RemoteCommandRunnerImpl( + int messagingTags, std::unique_ptr<executor::NetworkConnectionHook> hook) + : _connPool(messagingTags, std::move(hook)) {} + +RemoteCommandRunnerImpl::~RemoteCommandRunnerImpl() { + invariant(!_active); +} + +void RemoteCommandRunnerImpl::startup() { + _active = true; +} + +void RemoteCommandRunnerImpl::shutdown() { + if (!_active) { + return; + } + + _connPool.closeAllInUseConnections(); + _active = false; +} + +ResponseStatus RemoteCommandRunnerImpl::runCommand(const RemoteCommandRequest& request) { + try { + const Date_t requestStartDate = Date_t::now(); + const auto timeoutMillis = getTimeoutMillis(request.expirationDate, requestStartDate); + if (!timeoutMillis.isOK()) { + return {timeoutMillis.getStatus()}; + } + + ConnectionPool::ConnectionPtr conn( + &_connPool, request.target, requestStartDate, timeoutMillis.getValue()); + + BSONObj output; + BSONObj metadata; + + // If remote server does not support either find or getMore commands, down convert + // to using DBClientInterface::query()/getMore(). + // Perform down conversion based on wire protocol version. + + // 'commandName' will be an empty string if the command object is an empty BSON + // document. + StringData commandName = request.cmdObj.firstElement().fieldNameStringData(); + const auto isFindCmd = commandName == QueryRequest::kFindCommandName; + const auto isGetMoreCmd = commandName == GetMoreRequest::kGetMoreCommandName; + const auto isFindOrGetMoreCmd = isFindCmd || isGetMoreCmd; + + // We are using the wire version to check if we need to downconverting find/getMore + // requests because coincidentally, the find/getMore command is only supported by + // servers that also accept OP_COMMAND. + bool supportsFindAndGetMoreCommands = rpc::supportsWireVersionForOpCommandInMongod( + conn.get()->getMinWireVersion(), conn.get()->getMaxWireVersion()); + + if (!isFindOrGetMoreCmd || supportsFindAndGetMoreCommands) { + rpc::UniqueReply commandResponse = + conn.get()->runCommandWithMetadata(request.dbname, + request.cmdObj.firstElementFieldName(), + request.metadata, + request.cmdObj); + + output = commandResponse->getCommandReply().getOwned(); + metadata = commandResponse->getMetadata().getOwned(); + } else if (isFindCmd) { + return runDownconvertedFindCommand(conn.get(), request); + } else if (isGetMoreCmd) { + return runDownconvertedGetMoreCommand(conn.get(), request); + } + + const Date_t requestFinishDate = Date_t::now(); + conn.done(requestFinishDate); + + return {std::move(output), + std::move(metadata), + Milliseconds(requestFinishDate - requestStartDate)}; + } catch (const DBException& ex) { + return {ex.toStatus()}; + } catch (const std::exception& ex) { + return {ErrorCodes::UnknownError, + str::stream() << "Sending command " << request.cmdObj << " on database " + << request.dbname + << " over network to " + << request.target.toString() + << " received exception " + << ex.what()}; + } +} + +} // namespace mongo diff --git a/src/mongo/client/sasl_client_authenticate_impl.cpp b/src/mongo/client/sasl_client_authenticate_impl.cpp index aa4d7e64838..8a06acedb31 100644 --- a/src/mongo/client/sasl_client_authenticate_impl.cpp +++ b/src/mongo/client/sasl_client_authenticate_impl.cpp @@ -210,7 +210,7 @@ void asyncSaslConversation(auth::RunCommandHook runCommand, return handler(std::move(response)); } - auto serverResponse = response.getValue().data.getOwned(); + auto serverResponse = response.data.getOwned(); auto code = getStatusFromCommandResult(serverResponse).code(); // Server versions 2.3.2 and earlier may return "ok: 1" with a non-zero |