Browse Source

Added low level RunCommand method to MongoConnection to encapsulate repeated logic. Implemented verification of replica set name in ReplicaSetConnector.

pull/27/head
rstam 15 years ago
parent
commit
92ff7e8757
  1. 10
      Driver/Exceptions/MongoCommandException.cs
  2. 22
      Driver/Internal/DirectConnector.cs
  3. 116
      Driver/Internal/MongoConnection.cs
  4. 43
      Driver/Internal/ReplicaSetConnector.cs

10
Driver/Exceptions/MongoCommandException.cs

@ -19,6 +19,8 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using MongoDB.Bson;
namespace MongoDB.Driver {
[Serializable]
public class MongoCommandException : MongoException {
@ -36,6 +38,14 @@ namespace MongoDB.Driver {
: base(message, innerException) {
}
public MongoCommandException(
string message,
BsonDocument commandResult
)
: this(message) {
Data.Add("CommandResult", commandResult);
}
// this constructor needed to support deserialization
public MongoCommandException(
SerializationInfo info,

22
Driver/Internal/DirectConnector.cs

@ -84,24 +84,10 @@ namespace MongoDB.Driver.Internal {
bool isPrimary;
try {
var command = new BsonDocument("ismaster", 1);
using (
var message = new MongoQueryMessage<BsonDocument>(
"admin.$cmd",
QueryFlags.SlaveOk,
0, // numberToSkip
1, // numberToReturn
command,
null // fields
)
) {
connection.SendMessage(message, SafeMode.False);
}
var reply = connection.ReceiveMessage<BsonDocument>();
var commandResult = reply.Documents[0];
isPrimary =
commandResult["ok", false].ToBoolean() &&
commandResult["ismaster", false].ToBoolean();
var isMasterCommand = new BsonDocument("ismaster", 1);
var isMasterResult = connection.RunCommand("admin.$cmd", QueryFlags.SlaveOk, isMasterCommand);
isPrimary = isMasterResult["ismaster", false].ToBoolean();
if (!isPrimary && !url.SlaveOk) {
throw new MongoConnectionException("Server is not a primary and SlaveOk is false");
}

116
Driver/Internal/MongoConnection.cs

@ -78,24 +78,14 @@ namespace MongoDB.Driver.Internal {
if (closed) { throw new InvalidOperationException("Connection is closed"); }
lock (connectionLock) {
var nonceCommand = new BsonDocument("getnonce", 1);
using (
var nonceMessage = new MongoQueryMessage<BsonDocument>(
string.Format("{0}.$cmd", databaseName), // collectionFullName
QueryFlags.None,
0, // numberToSkip
1, // numberToReturn
nonceCommand, // query
null // fields
)
) {
SendMessage(nonceMessage, SafeMode.False);
}
var nonceReply = ReceiveMessage<BsonDocument>();
var nonceCommandResult = nonceReply.Documents[0];
if (!nonceCommandResult["ok", false].ToBoolean()) {
throw new MongoAuthenticationException("Error getting nonce for authentication");
var commandCollectionName = string.Format("{0}.$cmd", databaseName);
string nonce;
try {
var nonceResult = RunCommand(commandCollectionName, QueryFlags.None, nonceCommand);
nonce = nonceResult["nonce"].AsString;
} catch (MongoCommandException ex) {
throw new MongoAuthenticationException("Error getting nonce for authentication", ex);
}
var nonce = nonceCommandResult["nonce"].AsString;
var passwordDigest = MongoUtils.Hash(credentials.Username + ":mongo:" + credentials.Password);
var digest = MongoUtils.Hash(nonce + credentials.Username + passwordDigest);
@ -105,22 +95,11 @@ namespace MongoDB.Driver.Internal {
{ "nonce", nonce },
{ "key", digest }
};
using (
var authenticateMessage = new MongoQueryMessage<BsonDocument>(
string.Format("{0}.$cmd", databaseName), // collectionFullName
QueryFlags.None,
0, // numberToSkip
1, // numberToReturn
authenticateCommand, // query
null // fields
)
) {
SendMessage(authenticateMessage, SafeMode.False);
}
var authenticationReply = ReceiveMessage<BsonDocument>();
var authenticationResult = authenticationReply.Documents[0];
if (!authenticationResult["ok", false].ToBoolean()) {
throw new MongoAuthenticationException("Invalid credentials for database");
try {
RunCommand(commandCollectionName, QueryFlags.None, authenticateCommand);
} catch (MongoCommandException ex) {
var message = string.Format("Invalid credentials for database: {0}", databaseName);
throw new MongoAuthenticationException(message, ex);
}
var authentication = new Authentication(credentials);
@ -259,28 +238,67 @@ namespace MongoDB.Driver.Internal {
if (closed) { throw new InvalidOperationException("Connection is closed"); }
lock (connectionLock) {
var logoutCommand = new BsonDocument("logout", 1);
using (
var logoutMessage = new MongoQueryMessage<BsonDocument>(
string.Format("{0}.$cmd", databaseName), // collectionFullName
QueryFlags.None,
0, // numberToSkip
1, // numberToReturn
logoutCommand,
null // fields
)
) {
SendMessage(logoutMessage, SafeMode.False);
}
var logoutReply = ReceiveMessage<BsonDocument>();
var logoutCommandResult = logoutReply.Documents[0];
if (!logoutCommandResult["ok", false].ToBoolean()) {
throw new MongoAuthenticationException("Error in logout");
var commandCollectionName = string.Format("{0}.$cmd", databaseName);
try {
var logoutCommandResult = RunCommand(commandCollectionName, QueryFlags.None, logoutCommand);
} catch (MongoCommandException ex) {
throw new MongoAuthenticationException("Error logging off", ex);
}
authentications.Remove(databaseName);
}
}
// this is a low level method that doesn't require a MongoServer
// so it can be used while connecting to a MongoServer
internal BsonDocument RunCommand(
string collectionName,
QueryFlags queryFlags,
BsonDocument command
) {
var commandName = command.GetElement(0).Name;
using (
var message = new MongoQueryMessage<BsonDocument>(
collectionName,
queryFlags,
0, // numberToSkip
1, // numberToReturn
command,
null // fields
)
) {
SendMessage(message, SafeMode.False);
}
var reply = ReceiveMessage<BsonDocument>();
if ((reply.ResponseFlags & ResponseFlags.QueryFailure) != 0) {
var message = string.Format("Command '{0}' failed (QueryFailure flag set)", commandName);
throw new MongoCommandException(message);
}
if (reply.NumberReturned != 1) {
var message = string.Format("Command '{0}' failed (wrong number of documents returned: {1})", commandName, reply.NumberReturned);
throw new MongoCommandException(message);
}
var commandResult = reply.Documents[0];
if (!commandResult.Contains("ok")) {
var message = string.Format("Command '{0}' failed (ok element missing in result)", commandName);
}
if (!commandResult["ok"].ToBoolean()) {
string message;
var err = commandResult["err", null];
if (err == null || err.IsBsonNull) {
message = string.Format("Command '{0}' failed (no error message found)", commandName);
} else {
message = string.Format("Command '{0}' failed ({1})", commandName, err.ToString());
}
throw new MongoCommandException(message, commandResult);
}
return commandResult;
}
internal MongoReplyMessage<TDocument> ReceiveMessage<TDocument>() {
if (closed) { throw new InvalidOperationException("Connection is closed"); }
lock (connectionLock) {

43
Driver/Internal/ReplicaSetConnector.cs

@ -122,16 +122,13 @@ namespace MongoDB.Driver.Internal {
private List<MongoServerAddress> GetHostAddresses(
QueryNodeResponse response
) {
if (!response.CommandResult.Contains("hosts")) {
if (!response.IsMasterResult.Contains("hosts")) {
var message = string.Format("Server is not a member of a replica set: {0}", response.Address);
throw new MongoConnectionException(message);
}
if (url.ReplicaSetName != null) {
// TODO: check replica set name
}
var nodes = new List<MongoServerAddress>();
foreach (BsonString host in response.CommandResult["hosts"].AsBsonArray.Values) {
foreach (BsonString host in response.IsMasterResult["hosts"].AsBsonArray.Values) {
var address = MongoServerAddress.Parse(host.Value);
nodes.Add(address);
}
@ -162,25 +159,23 @@ namespace MongoDB.Driver.Internal {
try {
var connection = new MongoConnection(null, args.Address); // no connection pool
try {
var command = new BsonDocument("ismaster", 1);
using (
var message = new MongoQueryMessage<BsonDocument>(
"admin.$cmd",
QueryFlags.SlaveOk,
0, // numberToSkip
1, // numberToReturn
command,
null // fields
)
) {
connection.SendMessage(message, SafeMode.False);
}
var reply = connection.ReceiveMessage<BsonDocument>();
response.CommandResult = reply.Documents[0];
var isMasterCommand = new BsonDocument("ismaster", 1);
var isMasterResult = connection.RunCommand("admin.$cmd", QueryFlags.SlaveOk, isMasterCommand);
response.IsMasterResult = isMasterResult;
response.Connection = connection; // might become the first connection in the connection pool
response.IsPrimary =
response.CommandResult["ok", false].ToBoolean() &&
response.CommandResult["ismaster", false].ToBoolean();
response.IsPrimary = isMasterResult["ismaster", false].ToBoolean();
if (url.ReplicaSetName != null) {
var getStatusCommand = new BsonDocument("replSetGetStatus", 1);
var getStatusResult = connection.RunCommand("admin.$cmd", QueryFlags.SlaveOk, getStatusCommand);
var replicaSetName = getStatusResult["set"].AsString;
if (replicaSetName != url.ReplicaSetName) {
var message = string.Format("Host {0} belongs to a different replica set: {1}", args.Address, replicaSetName);
throw new MongoConnectionException(message);
}
}
} catch {
try { connection.Close(); } catch { } // ignore exceptions
throw;
@ -203,7 +198,7 @@ namespace MongoDB.Driver.Internal {
// note: OK to use automatic properties on private helper class
private class QueryNodeResponse {
public MongoServerAddress Address { get; set; }
public BsonDocument CommandResult { get; set; }
public BsonDocument IsMasterResult { get; set; }
public bool IsPrimary { get; set; }
public MongoConnection Connection { get; set; }
public Exception Exception { get; set; }

Loading…
Cancel
Save