void NetworkInterfaceASIO::_runConnectionHook(AsyncOp* op) { if (!_hook) { return _beginCommunication(op); } auto swOptionalRequest = callNoexcept(*_hook, &NetworkConnectionHook::makeRequest, op->request().target); if (!swOptionalRequest.isOK()) { return _completeOperation(op, swOptionalRequest.getStatus()); } auto optionalRequest = std::move(swOptionalRequest.getValue()); if (optionalRequest == boost::none) { return _beginCommunication(op); } auto beginStatus = op->beginCommand(*optionalRequest, _metadataHook.get()); if (!beginStatus.isOK()) { return _completeOperation(op, beginStatus); } auto finishHook = [this, op]() { auto response = op->command()->response(op->operationProtocol(), now(), _metadataHook.get()); if (!response.isOK()) { return _completeOperation(op, response.getStatus()); } auto handleStatus = callNoexcept(*_hook, &NetworkConnectionHook::handleReply, op->request().target, std::move(response.getValue())); if (!handleStatus.isOK()) { return _completeOperation(op, handleStatus); } return _beginCommunication(op); }; return _asyncRunCommand(op, [this, op, finishHook](std::error_code ec, std::size_t bytes) { _validateAndRun(op, ec, finishHook); }); }
void NetworkInterfaceASIO::_runIsMaster(AsyncOp* op) { // We use a legacy builder to create our ismaster request because we may // have to communicate with servers that do not support OP_COMMAND rpc::LegacyRequestBuilder requestBuilder{}; requestBuilder.setDatabase("admin"); requestBuilder.setCommandName("isMaster"); requestBuilder.setMetadata(rpc::makeEmptyMetadata()); requestBuilder.setCommandArgs(BSON("isMaster" << 1)); // Set current command to ismaster request and run auto beginStatus = op->beginCommand(std::move(*(requestBuilder.done()))); if (!beginStatus.isOK()) { return _completeOperation(op, beginStatus); } // Callback to parse protocol information out of received ismaster response auto parseIsMaster = [this, op]() { auto swCommandReply = op->command()->response(rpc::Protocol::kOpQuery, now()); if (!swCommandReply.isOK()) { return _completeOperation(op, swCommandReply.getStatus()); } auto commandReply = std::move(swCommandReply.getValue()); if (_hook) { // Run the validation hook. auto validHost = callNoexcept( *_hook, &NetworkConnectionHook::validateHost, op->request().target, commandReply); if (!validHost.isOK()) { return _completeOperation(op, validHost); } } auto protocolSet = rpc::parseProtocolSetFromIsMasterReply(commandReply.data); if (!protocolSet.isOK()) return _completeOperation(op, protocolSet.getStatus()); op->connection().setServerProtocols(protocolSet.getValue()); // Set the operation protocol auto negotiatedProtocol = rpc::negotiate(op->connection().serverProtocols(), op->connection().clientProtocols()); if (!negotiatedProtocol.isOK()) { return _completeOperation(op, negotiatedProtocol.getStatus()); } op->setOperationProtocol(negotiatedProtocol.getValue()); return _authenticate(op); }; _asyncRunCommand(op->command(), [this, op, parseIsMaster](std::error_code ec, size_t bytes) { _validateAndRun(op, ec, std::move(parseIsMaster)); }); }
void NetworkInterfaceASIO::_runIsMaster(AsyncOp* op) { // We use a legacy builder to create our ismaster request because we may // have to communicate with servers that do not support OP_COMMAND rpc::LegacyRequestBuilder requestBuilder{}; requestBuilder.setDatabase("admin"); requestBuilder.setCommandName("isMaster"); BSONObjBuilder bob; bob.append("isMaster", 1); bob.append("hangUpOnStepDown", false); const auto versionString = VersionInfoInterface::instance().version(); ClientMetadata::serialize(_options.instanceName, versionString, &bob); if (Command::testCommandsEnabled) { // Only include the host:port of this process in the isMaster command request if test // commands are enabled. mongobridge uses this field to identify the process opening a // connection to it. StringBuilder sb; sb << getHostName() << ':' << serverGlobalParams.port; bob.append("hostInfo", sb.str()); } op->connection().getCompressorManager().clientBegin(&bob); if (WireSpec::instance().isInternalClient) { WireSpec::appendInternalClientWireVersion(WireSpec::instance().outgoing, &bob); } requestBuilder.setCommandArgs(bob.done()); requestBuilder.setMetadata(rpc::makeEmptyMetadata()); // Set current command to ismaster request and run auto beginStatus = op->beginCommand(requestBuilder.done(), op->request().target); if (!beginStatus.isOK()) { return _completeOperation(op, beginStatus); } // Callback to parse protocol information out of received ismaster response auto parseIsMaster = [this, op]() { auto swCommandReply = op->command()->response(op, rpc::Protocol::kOpQuery, now()); if (!swCommandReply.isOK()) { return _completeOperation(op, swCommandReply); } auto commandReply = std::move(swCommandReply); // Ensure that the isMaster response is "ok:1". auto commandStatus = getStatusFromCommandResult(commandReply.data); if (!commandStatus.isOK()) { return _completeOperation(op, commandStatus); } auto protocolSet = rpc::parseProtocolSetFromIsMasterReply(commandReply.data); if (!protocolSet.isOK()) return _completeOperation(op, protocolSet.getStatus()); auto validateStatus = rpc::validateWireVersion(WireSpec::instance().outgoing, protocolSet.getValue().version); if (!validateStatus.isOK()) { warning() << "remote host has incompatible wire version: " << validateStatus; return _completeOperation(op, validateStatus); } op->connection().setServerProtocols(protocolSet.getValue().protocolSet); invariant(op->connection().clientProtocols() != rpc::supports::kNone); // Set the operation protocol auto negotiatedProtocol = rpc::negotiate(op->connection().serverProtocols(), op->connection().clientProtocols()); if (!negotiatedProtocol.isOK()) { // Add relatively verbose logging here, since this should not happen unless we are // mongos and we try to connect to a node that doesn't support OP_COMMAND. warning() << "failed to negotiate protocol with remote host: " << op->request().target; warning() << "request was: " << redact(op->request().cmdObj); warning() << "response was: " << redact(commandReply.data); auto clientProtos = rpc::toString(op->connection().clientProtocols()); if (clientProtos.isOK()) { warning() << "our (client) supported protocols: " << clientProtos.getValue(); } auto serverProtos = rpc::toString(op->connection().serverProtocols()); if (serverProtos.isOK()) { warning() << "remote server's supported protocols:" << serverProtos.getValue(); } return _completeOperation(op, negotiatedProtocol.getStatus()); } op->setOperationProtocol(negotiatedProtocol.getValue()); op->connection().getCompressorManager().clientFinish(commandReply.data); if (_hook) { // Run the validation hook. auto validHost = callNoexcept( *_hook, &NetworkConnectionHook::validateHost, op->request().target, commandReply); if (!validHost.isOK()) { return _completeOperation(op, validHost); } } return _authenticate(op); }; _asyncRunCommand(op, [this, op, parseIsMaster](std::error_code ec, size_t bytes) { _validateAndRun(op, ec, std::move(parseIsMaster)); }); }