void DBClientCursor::commandDataReceived() { int op = batch.m.operation(); invariant(op == opReply || op == dbCommandReply); batch.nReturned = 1; batch.pos = 0; auto commandReply = rpc::makeReply(&batch.m); auto commandStatus = getStatusFromCommandResult(commandReply->getCommandReply()); if (ErrorCodes::SendStaleConfig == commandStatus) { throw RecvStaleConfigException("stale config in DBClientCursor::dataReceived()", commandReply->getCommandReply()); } else if (!commandStatus.isOK()) { wasError = true; } if (_client->getReplyMetadataReader()) { uassertStatusOK(_client->getReplyMetadataReader()(commandReply->getMetadata(), _client->getServerAddress())); } // HACK: If we got an OP_COMMANDREPLY, take the reply object // and shove it in to an OP_REPLY message. if (op == dbCommandReply) { // Need to take ownership here as we destroy the underlying message. BSONObj reply = commandReply->getCommandReply().getOwned(); batch.m.reset(); replyToQuery(0, batch.m, reply); } QueryResult::View qr = batch.m.singleData().view2ptr(); batch.data = qr.data(); }
void NetworkInterfaceASIO::_completedWriteCallback(AsyncOp* op) { // If we were told to send an empty message, toRecv will be empty here. // TODO: handle metadata readers const auto elapsed = [this, op]() { return now() - op->start(); }; if (op->toRecv()->empty()) { LOG(3) << "received an empty message"; return _completeOperation(op, RemoteCommandResponse(BSONObj(), BSONObj(), elapsed())); } try { auto reply = rpc::makeReply(op->toRecv()); if (reply->getProtocol() != op->operationProtocol()) { return _completeOperation(op, Status(ErrorCodes::RPCProtocolNegotiationFailed, str::stream() << "Mismatched RPC protocols - request was '" << opToString(op->toSend()->operation()) << "' '" << " but reply was '" << opToString(op->toRecv()->operation()) << "'")); } _completeOperation(op, // unavoidable copy RemoteCommandResponse(reply->getCommandReply().getOwned(), reply->getMetadata().getOwned(), elapsed())); } catch (...) { // makeReply can throw if the reply was invalid. _completeOperation(op, exceptionToStatus()); } }
ResponseStatus NetworkInterfaceASIO::_responseFromMessage(const Message& received, rpc::Protocol protocol) { try { // TODO: elapsed isn't going to be correct here, SERVER-19697 auto start = now(); auto reply = rpc::makeReply(&received); if (reply->getProtocol() != protocol) { auto requestProtocol = rpc::toString(static_cast<rpc::ProtocolSet>(protocol)); if (!requestProtocol.isOK()) return requestProtocol.getStatus(); return Status(ErrorCodes::RPCProtocolNegotiationFailed, str::stream() << "Mismatched RPC protocols - request was '" << requestProtocol.getValue().toString() << "' '" << " but reply was '" << opToString(received.operation()) << "'"); } // unavoidable copy auto ownedCommandReply = reply->getCommandReply().getOwned(); auto ownedReplyMetadata = reply->getMetadata().getOwned(); return ResponseStatus( RemoteCommandResponse(ownedCommandReply, ownedReplyMetadata, now() - start)); } catch (...) { // makeReply can throw if the reply was invalid. return exceptionToStatus(); } }
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& cmd = op->beginCommand(std::move(*(requestBuilder.done()))); // Callback to parse protocol information out of received ismaster response auto parseIsMaster = [this, op]() { try { auto commandReply = rpc::makeReply(&(op->command().toRecv())); BSONObj isMasterReply = commandReply->getCommandReply(); auto protocolSet = rpc::parseProtocolSetFromIsMasterReply(isMasterReply); 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()); // Advance the state machine return _authenticate(op); } catch (...) { // makeReply will throw if the reply was invalid. return _completeOperation(op, exceptionToStatus()); } }; _asyncRunCommand(&cmd, [this, op, parseIsMaster](std::error_code ec, size_t bytes) { _validateAndRun(op, ec, std::move(parseIsMaster)); }); }
bool MockRemoteDBServer::runCommand(MockRemoteDBServer::InstanceID id, const string& dbname, const BSONObj& cmdObj, BSONObj& info, int options) { BSONObj upconvertedRequest; BSONObj upconvertedMetadata; std::tie(upconvertedRequest, upconvertedMetadata) = uassertStatusOK(rpc::upconvertRequestMetadata(cmdObj, options)); StringData commandName = upconvertedRequest.firstElementFieldName(); auto res = runCommandWithMetadata(id, dbname, commandName, upconvertedMetadata, upconvertedRequest); info = res->getCommandReply().getOwned(); return info["ok"].trueValue(); }
ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection( const HostAndPort& target, Date_t now, Milliseconds timeout) { stdx::unique_lock<stdx::mutex> lk(_mutex); // Clean up connections on stale/unused hosts _cleanUpStaleHosts_inlock(now); for (HostConnectionMap::iterator hostConns; (hostConns = _connections.find(target)) != _connections.end();) { // Clean up the requested host to remove stale/unused connections _cleanUpOlderThan_inlock(now, &hostConns->second); if (hostConns->second.empty()) { // prevent host from causing unnecessary cleanups _lastUsedHosts[hostConns->first] = kNeverTooStale; break; } _inUseConnections.splice( _inUseConnections.begin(), hostConns->second, hostConns->second.begin()); const ConnectionList::iterator candidate = _inUseConnections.begin(); lk.unlock(); try { if (candidate->conn->isStillConnected()) { // setSoTimeout takes a double representing the number of seconds for send and // receive timeouts. Thus, we must take count() and divide by // 1000.0 to get the number of seconds with a fractional part. candidate->conn->setSoTimeout(timeout.count() / 1000.0); return candidate; } } catch (...) { lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); throw; } lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); } // No idle connection in the pool; make a new one. lk.unlock(); std::unique_ptr<DBClientConnection> conn(new DBClientConnection()); // setSoTimeout takes a double representing the number of seconds for send and receive // timeouts. Thus, we must take count() and divide by 1000.0 to get the number // of seconds with a fractional part. conn->setSoTimeout(timeout.count() / 1000.0); if (_hook) { uassertStatusOK( conn->connect(target, [this, &target](const executor::RemoteCommandResponse& isMasterReply) { return _hook->validateHost(target, isMasterReply); })); auto postConnectRequest = uassertStatusOK(_hook->makeRequest(target)); // We might not have a postConnectRequest if (postConnectRequest != boost::none) { auto start = Date_t::now(); auto reply = conn->runCommandWithMetadata(postConnectRequest->dbname, postConnectRequest->cmdObj.firstElementFieldName(), postConnectRequest->metadata, postConnectRequest->cmdObj); auto rcr = executor::RemoteCommandResponse(reply->getCommandReply().getOwned(), reply->getMetadata().getOwned(), Date_t::now() - start); uassertStatusOK(_hook->handleReply(target, std::move(rcr))); } } else { uassertStatusOK(conn->connect(target)); } conn->port().tag |= _messagingPortTags; if (getGlobalAuthorizationManager()->isAuthEnabled()) { uassert(ErrorCodes::AuthenticationFailed, "Missing credentials for authenticating as internal user", isInternalAuthSet()); conn->auth(getInternalUserAuthParamsWithFallback()); } lk.lock(); return _inUseConnections.insert(_inUseConnections.begin(), ConnectionInfo(conn.release(), now)); }
ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection( const HostAndPort& target, Date_t now, Milliseconds timeout) { stdx::unique_lock<stdx::mutex> lk(_mutex); // Clean up connections on stale/unused hosts _cleanUpStaleHosts_inlock(now); for (HostConnectionMap::iterator hostConns; (hostConns = _connections.find(target)) != _connections.end();) { // Clean up the requested host to remove stale/unused connections _cleanUpOlderThan_inlock(now, &hostConns->second); if (hostConns->second.empty()) { // prevent host from causing unnecessary cleanups _lastUsedHosts[hostConns->first] = kNeverTooStale; break; } _inUseConnections.splice( _inUseConnections.begin(), hostConns->second, hostConns->second.begin()); const ConnectionList::iterator candidate = _inUseConnections.begin(); lk.unlock(); try { if (candidate->conn->isStillConnected()) { // setSoTimeout takes a double representing the number of seconds for send and // receive timeouts. Thus, we must express 'timeout' in milliseconds and divide by // 1000.0 to get the number of seconds with a fractional part. candidate->conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0); return candidate; } } catch (...) { lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); throw; } lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); } // No idle connection in the pool; make a new one. lk.unlock(); std::unique_ptr<DBClientConnection> conn; if (_hook) { conn.reset(new DBClientConnection( false, // auto reconnect 0, // socket timeout {}, // MongoURI [this, target](const executor::RemoteCommandResponse& isMasterReply) { return _hook->validateHost(target, BSONObj(), isMasterReply); })); } else { conn.reset(new DBClientConnection()); } // setSoTimeout takes a double representing the number of seconds for send and receive // timeouts. Thus, we must express 'timeout' in milliseconds and divide by 1000.0 to get // the number of seconds with a fractional part. conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0); uassertStatusOK(conn->connect(target, StringData())); conn->setTags(_messagingPortTags); if (isInternalAuthSet()) { conn->auth(getInternalUserAuthParams()); } if (_hook) { auto postConnectRequest = uassertStatusOK(_hook->makeRequest(target)); // We might not have a postConnectRequest if (postConnectRequest != boost::none) { auto start = Date_t::now(); auto reply = conn->runCommand(OpMsgRequest::fromDBAndBody(postConnectRequest->dbname, postConnectRequest->cmdObj, postConnectRequest->metadata)); auto rcr = executor::RemoteCommandResponse(reply->getCommandReply().getOwned(), Date_t::now() - start); uassertStatusOK(_hook->handleReply(target, std::move(rcr))); } } lk.lock(); return _inUseConnections.insert(_inUseConnections.begin(), ConnectionInfo(conn.release(), now)); }