summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorAdam Midvidy <amidvidy@gmail.com>2015-05-15 19:22:07 -0400
committerAdam Midvidy <amidvidy@gmail.com>2015-05-18 09:52:09 -0400
commit4e0acbc7b67c5386a3a9ed60795f9cab672a3262 (patch)
treea57f1fd8e7021f088f8bfcc91f53bd47aee81e50 /src/mongo
parent4f0e70b66182cbb872c4e5eefda23f1c58bdaab7 (diff)
downloadmongo-4e0acbc7b67c5386a3a9ed60795f9cab672a3262.tar.gz
SERVER-18288 implement a common interface for OP_COMMAND and OP_QUERY
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/base/error_codes.err2
-rw-r--r--src/mongo/rpc/SConscript91
-rw-r--r--src/mongo/rpc/command_reply.cpp (renamed from src/mongo/rpc/reply.cpp)22
-rw-r--r--src/mongo/rpc/command_reply.h (renamed from src/mongo/rpc/reply.h)17
-rw-r--r--src/mongo/rpc/command_reply_builder.cpp (renamed from src/mongo/rpc/reply_builder.cpp)36
-rw-r--r--src/mongo/rpc/command_reply_builder.h (renamed from src/mongo/rpc/reply_builder.h)35
-rw-r--r--src/mongo/rpc/command_reply_builder_test.cpp (renamed from src/mongo/rpc/reply_builder_test.cpp)13
-rw-r--r--src/mongo/rpc/command_reply_test.cpp (renamed from src/mongo/rpc/reply_test.cpp)14
-rw-r--r--src/mongo/rpc/command_request.cpp (renamed from src/mongo/rpc/request.cpp)20
-rw-r--r--src/mongo/rpc/command_request.h (renamed from src/mongo/rpc/request.h)23
-rw-r--r--src/mongo/rpc/command_request_builder.cpp (renamed from src/mongo/rpc/request_builder.cpp)51
-rw-r--r--src/mongo/rpc/command_request_builder.h (renamed from src/mongo/rpc/request_builder.h)44
-rw-r--r--src/mongo/rpc/command_request_builder_test.cpp (renamed from src/mongo/rpc/request_builder_test.cpp)12
-rw-r--r--src/mongo/rpc/command_request_test.cpp (renamed from src/mongo/rpc/request_test.cpp)7
-rw-r--r--src/mongo/rpc/document_range.h2
-rw-r--r--src/mongo/rpc/factory.cpp70
-rw-r--r--src/mongo/rpc/factory.h60
-rw-r--r--src/mongo/rpc/legacy_reply.cpp82
-rw-r--r--src/mongo/rpc/legacy_reply.h87
-rw-r--r--src/mongo/rpc/legacy_reply_builder.cpp101
-rw-r--r--src/mongo/rpc/legacy_reply_builder.h65
-rw-r--r--src/mongo/rpc/legacy_request.cpp78
-rw-r--r--src/mongo/rpc/legacy_request.h103
-rw-r--r--src/mongo/rpc/legacy_request_builder.cpp121
-rw-r--r--src/mongo/rpc/legacy_request_builder.h75
-rw-r--r--src/mongo/rpc/metadata.cpp69
-rw-r--r--src/mongo/rpc/metadata.h93
-rw-r--r--src/mongo/rpc/protocol.cpp117
-rw-r--r--src/mongo/rpc/protocol.h99
-rw-r--r--src/mongo/rpc/protocol_test.cpp66
-rw-r--r--src/mongo/rpc/reply_builder_interface.cpp85
-rw-r--r--src/mongo/rpc/reply_builder_interface.h125
-rw-r--r--src/mongo/rpc/reply_interface.h76
-rw-r--r--src/mongo/rpc/request_builder_interface.h118
-rw-r--r--src/mongo/rpc/request_interface.h88
-rw-r--r--src/mongo/util/net/message.h2
36 files changed, 2014 insertions, 155 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 8bb00b55e39..d40b671d1df 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -124,6 +124,8 @@ error_code("DocumentValidationFailure", 121) # Only for the document validator o
error_code("ReadAfterOptimeTimeout", 122)
error_code("NotAReplicaSet", 123)
error_code("IncompatibleElectionProtocol", 124)
+error_code("CommandFailed", 125)
+error_code("RPCProtocolNegotiationFailed", 126)
# Non-sequential error codes (for compatibility only)
error_code("NotMaster", 10107) #this comes from assert_util.h
diff --git a/src/mongo/rpc/SConscript b/src/mongo/rpc/SConscript
index 3bff42a61e6..20279380881 100644
--- a/src/mongo/rpc/SConscript
+++ b/src/mongo/rpc/SConscript
@@ -26,11 +26,38 @@ env.Library(
env.Library(
target=[
- 'request',
+ 'protocol',
],
source=[
- 'request.cpp',
- 'request_builder.cpp',
+ 'protocol.cpp',
+ ],
+)
+
+env.Library(
+ target=[
+ 'rpc',
+ ],
+ source=[
+ 'factory.cpp',
+ ],
+ LIBDEPS=[
+ 'command_reply',
+ 'command_request',
+ 'legacy_reply',
+ 'legacy_request',
+ 'metadata',
+ 'protocol',
+ '$BUILD_DIR/mongo/db/server_parameters',
+ ],
+)
+
+env.Library(
+ target=[
+ 'command_request',
+ ],
+ source=[
+ 'command_request.cpp',
+ 'command_request_builder.cpp',
],
LIBDEPS=[
'document_range',
@@ -40,11 +67,11 @@ env.Library(
env.Library(
target=[
- 'reply',
+ 'legacy_request',
],
source=[
- 'reply.cpp',
- 'reply_builder.cpp'
+ 'legacy_request.cpp',
+ 'legacy_request_builder.cpp',
],
LIBDEPS=[
'document_range',
@@ -52,18 +79,58 @@ env.Library(
],
)
+env.Library(
+ target=[
+ 'command_reply',
+ ],
+ source=[
+ 'command_reply.cpp',
+ 'command_reply_builder.cpp',
+ 'reply_builder_interface.cpp',
+ ],
+ LIBDEPS=[
+ 'document_range',
+ '$BUILD_DIR/mongo/util/net/network',
+ ],
+)
+
+env.Library(
+ target=[
+ 'legacy_reply',
+ ],
+ source=[
+ 'legacy_reply.cpp',
+ 'legacy_reply_builder.cpp'
+ ],
+ LIBDEPS=[
+ 'document_range',
+ '$BUILD_DIR/mongo/util/net/network',
+ ],
+)
+
+env.Library(
+ target=[
+ 'metadata',
+ ],
+ source=[
+ 'metadata.cpp'
+ ],
+)
+
env.CppUnitTest(
target=[
'rpc_test',
],
source=[
- 'reply_builder_test.cpp',
- 'reply_test.cpp',
- 'request_builder_test.cpp',
- 'request_test.cpp',
+ 'command_reply_builder_test.cpp',
+ 'command_reply_test.cpp',
+ 'command_request_builder_test.cpp',
+ 'command_request_test.cpp',
+ 'protocol_test.cpp',
],
LIBDEPS=[
- 'request',
- 'reply',
+ 'command_request',
+ 'command_reply',
+ 'protocol',
],
)
diff --git a/src/mongo/rpc/reply.cpp b/src/mongo/rpc/command_reply.cpp
index e45f72091b9..449308fbed3 100644
--- a/src/mongo/rpc/reply.cpp
+++ b/src/mongo/rpc/command_reply.cpp
@@ -28,7 +28,7 @@
#include "mongo/platform/basic.h"
-#include "mongo/rpc/reply.h"
+#include "mongo/rpc/command_reply.h"
#include <tuple>
@@ -38,15 +38,17 @@
namespace mongo {
namespace rpc {
- Reply::Reply(const Message* message)
+ CommandReply::CommandReply(const Message* message)
: _message(message) {
- char* begin = _message->singleData().data();
+ const char* begin = _message->singleData().data();
std::size_t length = _message->singleData().dataLen();
- invariant(length <= MaxMessageSizeBytes); // checked by message_port.cpp
+ // This check failing would normally be operation fatal, but we expect it to have been
+ // done earlier in the network layer, so we make it an invariant.
+ invariant(length <= MaxMessageSizeBytes);
- char* messageEnd = begin + length;
+ const char* messageEnd = begin + length;
ConstDataRangeCursor cur(begin, messageEnd);
// TODO(amidvidy): we don't currently handle BSON validation.
@@ -57,24 +59,24 @@ namespace rpc {
_outputDocs = DocumentRange(cur.data(), messageEnd);
}
- const BSONObj& Reply::getMetadata() const {
+ const BSONObj& CommandReply::getMetadata() const {
return _metadata;
}
- const BSONObj& Reply::getCommandReply() const {
+ const BSONObj& CommandReply::getCommandReply() const {
return _commandReply;
}
- DocumentRange Reply::getOutputDocs() const {
+ DocumentRange CommandReply::getOutputDocs() const {
return _outputDocs;
}
- bool operator==(const Reply& lhs, const Reply& rhs) {
+ bool operator==(const CommandReply& lhs, const CommandReply& rhs) {
return std::tie(lhs._metadata, lhs._commandReply, lhs._outputDocs) ==
std::tie(rhs._metadata, rhs._commandReply, rhs._outputDocs);
}
- bool operator!=(const Reply& lhs, const Reply& rhs) {
+ bool operator!=(const CommandReply& lhs, const CommandReply& rhs) {
return !(lhs == rhs);
}
diff --git a/src/mongo/rpc/reply.h b/src/mongo/rpc/command_reply.h
index befd6d70719..8f888e6190d 100644
--- a/src/mongo/rpc/reply.h
+++ b/src/mongo/rpc/command_reply.h
@@ -28,8 +28,9 @@
#pragma once
-#include "mongo/bson/bsonobj.h"
+#include "mongo/db/jsobj.h"
#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/reply_interface.h"
namespace mongo {
class Message;
@@ -45,7 +46,7 @@ namespace rpc {
* explicit.
* See SERVER-16730 for additional details.
*/
- class Reply {
+ class CommandReply : public ReplyInterface {
public:
/**
* Construct a Reply from a Message.
@@ -54,18 +55,18 @@ namespace rpc {
*
* The underlying Message also handles the wire-protocol header.
*/
- explicit Reply(const Message* message);
+ explicit CommandReply(const Message* message);
/**
* Accessor for the metadata object. Metadata is generally used for information
* that is independent of any specific command, e.g. auditing information.
*/
- const BSONObj& getMetadata() const;
+ const BSONObj& getMetadata() const final;
/**
* The result of executing the command.
*/
- const BSONObj& getCommandReply() const;
+ const BSONObj& getCommandReply() const final;
/**
* A variable number of BSON documents returned by the command. It is valid for the
@@ -77,10 +78,10 @@ namespace rpc {
* ... do stuff with doc
* }
*/
- DocumentRange getOutputDocs() const;
+ DocumentRange getOutputDocs() const final;
- friend bool operator==(const Reply& lhs, const Reply& rhs);
- friend bool operator!=(const Reply& lhs, const Reply& rhs);
+ friend bool operator==(const CommandReply& lhs, const CommandReply& rhs);
+ friend bool operator!=(const CommandReply& lhs, const CommandReply& rhs);
private:
const Message* _message;
diff --git a/src/mongo/rpc/reply_builder.cpp b/src/mongo/rpc/command_reply_builder.cpp
index cd214b33864..09d4156e09f 100644
--- a/src/mongo/rpc/reply_builder.cpp
+++ b/src/mongo/rpc/command_reply_builder.cpp
@@ -30,55 +30,59 @@
#include <utility>
-#include "mongo/rpc/reply_builder.h"
+#include "mongo/rpc/command_reply_builder.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/assert_util.h"
namespace mongo {
namespace rpc {
- ReplyBuilder::ReplyBuilder()
+ CommandReplyBuilder::CommandReplyBuilder()
: _message{stdx::make_unique<Message>()}
{}
- ReplyBuilder::ReplyBuilder(std::unique_ptr<Message> message)
+ CommandReplyBuilder::CommandReplyBuilder(std::unique_ptr<Message> message)
: _message{std::move(message)}
{}
- ReplyBuilder& ReplyBuilder::setMetadata(const BSONObj& metadata) {
- invariant(_buildState == BuildState::kMetadata);
+ CommandReplyBuilder& CommandReplyBuilder::setMetadata(BSONObj metadata) {
+ invariant(_state == State::kMetadata);
metadata.appendSelfToBufBuilder(_builder);
- _buildState = BuildState::kCommandReply;
+ _state = State::kCommandReply;
return *this;
}
- ReplyBuilder& ReplyBuilder::setCommandReply(const BSONObj& commandReply) {
- invariant(_buildState == BuildState::kCommandReply);
+ CommandReplyBuilder& CommandReplyBuilder::setRawCommandReply(BSONObj commandReply) {
+ invariant(_state == State::kCommandReply);
commandReply.appendSelfToBufBuilder(_builder);
- _buildState = BuildState::kOutputDocs;
+ _state = State::kOutputDocs;
return *this;
}
- ReplyBuilder& ReplyBuilder::addOutputDocs(DocumentRange outputDocs) {
- invariant(_buildState == BuildState::kOutputDocs);
+ CommandReplyBuilder& CommandReplyBuilder::addOutputDocs(DocumentRange outputDocs) {
+ invariant(_state == State::kOutputDocs);
auto rangeData = outputDocs.data();
_builder.appendBuf(rangeData.data(), rangeData.length());
// leave state as is as we can add as many outputDocs as we want.
return *this;
}
- ReplyBuilder& ReplyBuilder::addOutputDoc(const BSONObj& outputDoc) {
- invariant(_buildState == BuildState::kOutputDocs);
+ CommandReplyBuilder& CommandReplyBuilder::addOutputDoc(BSONObj outputDoc) {
+ invariant(_state == State::kOutputDocs);
outputDoc.appendSelfToBufBuilder(_builder);
return *this;
}
- std::unique_ptr<Message> ReplyBuilder::done() {
- invariant(_buildState == BuildState::kOutputDocs);
+ ReplyBuilderInterface::State CommandReplyBuilder::getState() const {
+ return _state;
+ }
+
+ std::unique_ptr<Message> CommandReplyBuilder::done() {
+ invariant(_state == State::kOutputDocs);
// TODO: we can elide a large copy here by transferring the internal buffer of
// the BufBuilder to the Message.
_message->setData(dbCommandReply, _builder.buf(), _builder.len());
- _buildState = BuildState::kDone;
+ _state = State::kDone;
return std::move(_message);
}
diff --git a/src/mongo/rpc/reply_builder.h b/src/mongo/rpc/command_reply_builder.h
index 129edbc81f4..cf2cf01ee93 100644
--- a/src/mongo/rpc/reply_builder.h
+++ b/src/mongo/rpc/command_reply_builder.h
@@ -30,9 +30,11 @@
#include <memory>
-#include "mongo/bson/bsonobj.h"
+#include "mongo/base/status_with.h"
+#include "mongo/db/jsobj.h"
#include "mongo/rpc/document_range.h"
-#include "mongo/util/net/message.h" // need Message destructor for unique_ptr
+#include "mongo/rpc/reply_builder_interface.h"
+#include "mongo/util/net/message.h"
namespace mongo {
namespace rpc {
@@ -40,46 +42,41 @@ namespace rpc {
/**
* Constructs an OP_COMMANDREPLY message.
*/
- class ReplyBuilder {
+ class CommandReplyBuilder : public ReplyBuilderInterface {
public:
/**
* Constructs an OP_COMMANDREPLY in a new buffer.
*/
- ReplyBuilder();
+ CommandReplyBuilder();
/*
* Constructs an OP_COMMANDREPLY in an existing buffer. Ownership of the buffer
- * will be transfered to the ReplyBuilder.
+ * will be transfered to the CommandReplyBuilder.
*/
- ReplyBuilder(std::unique_ptr<Message> message);
+ CommandReplyBuilder(std::unique_ptr<Message> message);
- ReplyBuilder& setMetadata(const BSONObj& metadata);
- ReplyBuilder& setCommandReply(const BSONObj& commandReply);
+ CommandReplyBuilder& setMetadata(BSONObj metadata) final;
+ CommandReplyBuilder& setRawCommandReply(BSONObj commandReply) final;
- ReplyBuilder& addOutputDocs(DocumentRange outputDocs);
- ReplyBuilder& addOutputDoc(const BSONObj& outputDoc);
+ CommandReplyBuilder& addOutputDocs(DocumentRange outputDocs) final;
+ CommandReplyBuilder& addOutputDoc(BSONObj outputDoc) final;
+
+ State getState() const final;
/**
* Writes data then transfers ownership of the message to the caller.
* The behavior of calling any methods on the object is subsequently
* undefined.
*/
- std::unique_ptr<Message> done();
+ std::unique_ptr<Message> done() final;
private:
// Default values are all empty.
BufBuilder _builder{};
std::unique_ptr<Message> _message;
- enum class BuildState {
- kMetadata,
- kCommandReply,
- kOutputDocs,
- kDone
- };
-
- BuildState _buildState{BuildState::kMetadata};
+ State _state{State::kMetadata};
};
} // namespace rpc
diff --git a/src/mongo/rpc/reply_builder_test.cpp b/src/mongo/rpc/command_reply_builder_test.cpp
index 7302036fe4e..8771bf3691d 100644
--- a/src/mongo/rpc/reply_builder_test.cpp
+++ b/src/mongo/rpc/command_reply_builder_test.cpp
@@ -28,12 +28,11 @@
#include "mongo/platform/basic.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/util/builder.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/command_reply.h"
+#include "mongo/rpc/command_reply_builder.h"
#include "mongo/rpc/document_range.h"
-#include "mongo/rpc/reply.h"
-#include "mongo/rpc/reply_builder.h"
#include "mongo/unittest/unittest.h"
namespace {
@@ -47,7 +46,7 @@ namespace {
auto metadata = metadataBob.done();
BSONObjBuilder commandReplyBob{};
- commandReplyBob.append("bar", "baz");
+ commandReplyBob.append("bar", "baz").append("ok", 1.0);
auto commandReply = commandReplyBob.done();
BSONObjBuilder outputDoc1Bob{};
@@ -69,14 +68,14 @@ namespace {
rpc::DocumentRange outputDocRange{outputDocs.buf(), outputDocs.buf() + outputDocs.len()};
- rpc::ReplyBuilder r;
+ rpc::CommandReplyBuilder r;
auto msg = r.setMetadata(metadata)
.setCommandReply(commandReply)
.addOutputDocs(outputDocRange)
.done();
- rpc::Reply parsed(msg.get());
+ rpc::CommandReply parsed(msg.get());
ASSERT_EQUALS(parsed.getMetadata(), metadata);
ASSERT_EQUALS(parsed.getCommandReply(), commandReply);
diff --git a/src/mongo/rpc/reply_test.cpp b/src/mongo/rpc/command_reply_test.cpp
index 44c0ad7896e..48930bbe1b9 100644
--- a/src/mongo/rpc/reply_test.cpp
+++ b/src/mongo/rpc/command_reply_test.cpp
@@ -34,9 +34,9 @@
#include "mongo/base/data_type_endian.h"
#include "mongo/base/data_view.h"
-#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/db/jsobj.h"
#include "mongo/platform/cstdint.h"
-#include "mongo/rpc/reply.h"
+#include "mongo/rpc/command_reply.h"
#include "mongo/stdx/memory.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/net/message.h"
@@ -105,7 +105,7 @@ namespace {
auto outputDoc2 = outputDoc2Bob.done();
writeObj(outputDoc2);
- rpc::Reply opCmdReply{buildMessage()};
+ rpc::CommandReply opCmdReply{buildMessage()};
ASSERT_EQUALS(opCmdReply.getMetadata(), metadata);
ASSERT_EQUALS(opCmdReply.getCommandReply(), commandReply);
@@ -125,7 +125,7 @@ namespace {
}
TEST_F(ReplyTest, EmptyMessageThrows) {
- ASSERT_THROWS(rpc::Reply{buildMessage()}, UserException);
+ ASSERT_THROWS(rpc::CommandReply{buildMessage()}, UserException);
}
TEST_F(ReplyTest, MetadataOnlyThrows) {
@@ -134,7 +134,7 @@ namespace {
auto metadata = metadataBob.done();
writeObj(metadata);
- ASSERT_THROWS(rpc::Reply{buildMessage()}, UserException);
+ ASSERT_THROWS(rpc::CommandReply{buildMessage()}, UserException);
}
TEST_F(ReplyTest, MetadataInvalidLengthThrows) {
@@ -151,7 +151,7 @@ namespace {
auto commandReply = commandReplyBob.done();
writeObj(commandReply);
- ASSERT_THROWS(rpc::Reply{buildMessage()}, UserException);
+ ASSERT_THROWS(rpc::CommandReply{buildMessage()}, UserException);
}
TEST_F(ReplyTest, InvalidLengthThrows) {
@@ -169,7 +169,7 @@ namespace {
DataView(const_cast<char*>(commandReply.objdata())).write<LittleEndian<int32_t>>(100000);
writeObj(commandReply, trueSize);
- ASSERT_THROWS(rpc::Reply{buildMessage()}, UserException);
+ ASSERT_THROWS(rpc::CommandReply{buildMessage()}, UserException);
}
}
diff --git a/src/mongo/rpc/request.cpp b/src/mongo/rpc/command_request.cpp
index b5491928729..00a9fb7b477 100644
--- a/src/mongo/rpc/request.cpp
+++ b/src/mongo/rpc/command_request.cpp
@@ -28,7 +28,7 @@
#include "mongo/platform/basic.h"
-#include "mongo/rpc/request.h"
+#include "mongo/rpc/command_request.h"
#include <string>
#include <utility>
@@ -36,7 +36,7 @@
#include "mongo/base/data_range_cursor.h"
#include "mongo/base/data_type_string_data.h"
#include "mongo/base/data_type_terminated.h"
-#include "mongo/bson/bsonobj.h"
+#include "mongo/db/jsobj.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/message.h"
@@ -54,7 +54,7 @@ namespace rpc {
} // namespace
- Request::Request(const Message* message)
+ CommandRequest::CommandRequest(const Message* message)
: _message(message) {
char* begin = _message->singleData().data();
std::size_t length = _message->singleData().dataLen();
@@ -87,27 +87,27 @@ namespace rpc {
_inputDocs = DocumentRange{cur.data(), messageEnd};
}
- StringData Request::getDatabase() const {
+ StringData CommandRequest::getDatabase() const {
return _database;
}
- StringData Request::getCommandName() const {
+ StringData CommandRequest::getCommandName() const {
return _commandName;
}
- const BSONObj& Request::getMetadata() const {
+ const BSONObj& CommandRequest::getMetadata() const {
return _metadata;
}
- const BSONObj& Request::getCommandArgs() const {
+ const BSONObj& CommandRequest::getCommandArgs() const {
return _commandArgs;
}
- DocumentRange Request::getInputDocs() const {
+ DocumentRange CommandRequest::getInputDocs() const {
return _inputDocs;
}
- bool operator==(const Request& lhs, const Request& rhs) {
+ bool operator==(const CommandRequest& lhs, const CommandRequest& rhs) {
return std::tie(lhs._database,
lhs._commandName,
lhs._metadata,
@@ -120,7 +120,7 @@ namespace rpc {
rhs._inputDocs);
}
- bool operator!=(const Request& lhs, const Request& rhs) {
+ bool operator!=(const CommandRequest& lhs, const CommandRequest& rhs) {
return !(lhs == rhs);
}
diff --git a/src/mongo/rpc/request.h b/src/mongo/rpc/command_request.h
index 12891354958..b96323477c2 100644
--- a/src/mongo/rpc/request.h
+++ b/src/mongo/rpc/command_request.h
@@ -29,8 +29,9 @@
#pragma once
#include "mongo/base/string_data.h"
-#include "mongo/bson/bsonobj.h"
+#include "mongo/db/jsobj.h"
#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/request_interface.h"
namespace mongo {
class Message;
@@ -43,34 +44,36 @@ namespace rpc {
*
* TODO: BSON validation. See SERVER-18167 for details.
*/
- class Request {
+ class CommandRequest : public RequestInterface {
public:
/**
* Construct a Request from a Message. Underlying message MUST outlive the Request.
* Required fields are parsed eagerly, inputDocs are parsed lazily.
*/
- explicit Request(const Message* message);
+ explicit CommandRequest(const Message* message);
+
+ ~CommandRequest() = default;
/**
* The database that the command is to be executed on.
*/
- StringData getDatabase() const;
+ StringData getDatabase() const final;
/**
* The name of the command to execute.
*/
- StringData getCommandName() const;
+ StringData getCommandName() const final;
/**
* The metadata associated with the command request. This is information that is
* independent of any specific command, i.e. auditing information.
*/
- const BSONObj& getMetadata() const;
+ const BSONObj& getMetadata() const final;
/**
* The arguments to the command - this is passed to the command's run() method.
*/
- const BSONObj& getCommandArgs() const;
+ const BSONObj& getCommandArgs() const final;
/**
* A variable number of BSON documents to pass to the command. It is valid for
@@ -82,10 +85,10 @@ namespace rpc {
* ... do stuff with doc
* }
*/
- DocumentRange getInputDocs() const;
+ DocumentRange getInputDocs() const final;
- friend bool operator==(const Request& lhs, const Request& rhs);
- friend bool operator!=(const Request& lhs, const Request& rhs);
+ friend bool operator==(const CommandRequest& lhs, const CommandRequest& rhs);
+ friend bool operator!=(const CommandRequest& lhs, const CommandRequest& rhs);
private:
const Message* _message;
diff --git a/src/mongo/rpc/request_builder.cpp b/src/mongo/rpc/command_request_builder.cpp
index bbbc842ab96..b65ddc3f0b3 100644
--- a/src/mongo/rpc/request_builder.cpp
+++ b/src/mongo/rpc/command_request_builder.cpp
@@ -28,7 +28,7 @@
#include "mongo/platform/basic.h"
-#include "mongo/rpc/request_builder.h"
+#include "mongo/rpc/command_request_builder.h"
#include <utility>
@@ -38,61 +38,68 @@
namespace mongo {
namespace rpc {
- RequestBuilder::RequestBuilder()
+ CommandRequestBuilder::CommandRequestBuilder()
: _message{stdx::make_unique<Message>()}
{}
- RequestBuilder::RequestBuilder(std::unique_ptr<Message> message)
+ CommandRequestBuilder::~CommandRequestBuilder()
+ {}
+
+ CommandRequestBuilder::CommandRequestBuilder(std::unique_ptr<Message> message)
: _message{std::move(message)}
{}
- RequestBuilder& RequestBuilder::setDatabase(StringData database) {
- invariant(_buildState == BuildState::kDatabase);
+ CommandRequestBuilder& CommandRequestBuilder::setDatabase(StringData database) {
+ invariant(_state == State::kDatabase);
_builder.appendStr(database);
- _buildState = BuildState::kCommandName;
+ _state = State::kCommandName;
return *this;
}
- RequestBuilder& RequestBuilder::setCommandName(StringData commandName) {
- invariant(_buildState == BuildState::kCommandName);
+ CommandRequestBuilder& CommandRequestBuilder::setCommandName(StringData commandName) {
+ invariant(_state == State::kCommandName);
_builder.appendStr(commandName);
- _buildState = BuildState::kMetadata;
+ _state = State::kMetadata;
return *this;
}
- RequestBuilder& RequestBuilder::setMetadata(const BSONObj& metadata) {
- invariant(_buildState == BuildState::kMetadata);
+ CommandRequestBuilder& CommandRequestBuilder::setMetadata(BSONObj metadata) {
+ invariant(_state == State::kMetadata);
metadata.appendSelfToBufBuilder(_builder);
- _buildState = BuildState::kCommandArgs;
+ _state = State::kCommandArgs;
return *this;
}
- RequestBuilder& RequestBuilder::setCommandArgs(const BSONObj& commandArgs) {
- invariant(_buildState == BuildState::kCommandArgs);
+ CommandRequestBuilder& CommandRequestBuilder::setCommandArgs(BSONObj commandArgs) {
+ invariant(_state == State::kCommandArgs);
commandArgs.appendSelfToBufBuilder(_builder);
- _buildState = BuildState::kInputDocs;
+ _state = State::kInputDocs;
return *this;
}
- RequestBuilder& RequestBuilder::addInputDocs(DocumentRange inputDocs) {
- invariant(_buildState == BuildState::kInputDocs);
+ CommandRequestBuilder& CommandRequestBuilder::addInputDocs(DocumentRange inputDocs) {
+ invariant(_state == State::kInputDocs);
auto rangeData = inputDocs.data();
_builder.appendBuf(rangeData.data(), rangeData.length());
return *this;
}
- RequestBuilder& RequestBuilder::addInputDoc(const BSONObj& inputDoc) {
- invariant(_buildState == BuildState::kInputDocs);
+ CommandRequestBuilder& CommandRequestBuilder::addInputDoc(BSONObj inputDoc) {
+ invariant(_state == State::kInputDocs);
inputDoc.appendSelfToBufBuilder(_builder);
return *this;
}
- std::unique_ptr<Message> RequestBuilder::done() {
- invariant(_buildState == BuildState::kInputDocs);
+ RequestBuilderInterface::State CommandRequestBuilder::getState() const {
+ return _state;
+ }
+
+ std::unique_ptr<Message> CommandRequestBuilder::done() {
+ invariant(_state == State::kInputDocs);
// TODO: we can elide a large copy here by transferring the internal buffer of
// the BufBuilder to the Message.
_message->setData(dbCommand, _builder.buf(), _builder.len());
- _buildState = BuildState::kDone;
+ _state = State::kDone;
return std::move(_message);
}
diff --git a/src/mongo/rpc/request_builder.h b/src/mongo/rpc/command_request_builder.h
index 5715667ab35..12298a48f3c 100644
--- a/src/mongo/rpc/request_builder.h
+++ b/src/mongo/rpc/command_request_builder.h
@@ -31,8 +31,9 @@
#include <memory>
#include "mongo/base/string_data.h"
-#include "mongo/bson/bsonobj.h"
+#include "mongo/db/jsobj.h"
#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/request_builder_interface.h"
#include "mongo/util/net/message.h"
namespace mongo {
@@ -41,49 +42,48 @@ namespace rpc {
/**
* Constructs an OP_COMMAND message.
*/
- class RequestBuilder {
+ class CommandRequestBuilder : public RequestBuilderInterface {
public:
/**
* Constructs an OP_COMMAND in a new buffer.
*/
- RequestBuilder();
+ CommandRequestBuilder();
+
+ ~CommandRequestBuilder() final;
/**
* Construct an OP_COMMAND in an existing buffer. Ownership of the buffer will be
- * transfered to the RequestBuilder.
+ * transfered to the CommandRequestBuilder.
*/
- RequestBuilder(std::unique_ptr<Message> message);
+ CommandRequestBuilder(std::unique_ptr<Message> message);
+
+ CommandRequestBuilder& setDatabase(StringData database) final;
+
+ CommandRequestBuilder& setCommandName(StringData commandName) final;
+
+ CommandRequestBuilder& setMetadata(BSONObj metadata) final;
- RequestBuilder& setDatabase(StringData database);
- RequestBuilder& setCommandName(StringData commandName);
- RequestBuilder& setMetadata(const BSONObj& metadata);
- RequestBuilder& setCommandArgs(const BSONObj& commandArgs);
+ CommandRequestBuilder& setCommandArgs(BSONObj commandArgs) final;
- RequestBuilder& addInputDocs(DocumentRange inputDocs);
- RequestBuilder& addInputDoc(const BSONObj& inputDoc);
+ CommandRequestBuilder& addInputDocs(DocumentRange inputDocs) final;
+
+ CommandRequestBuilder& addInputDoc(BSONObj inputDoc) final;
+
+ State getState() const final;
/**
* Writes data then transfers ownership of the message to the caller.
* The behavior of calling any methods on the object is subsequently
* undefined.
*/
- std::unique_ptr<Message> done();
+ std::unique_ptr<Message> done() final;
private:
BufBuilder _builder{};
std::unique_ptr<Message> _message;
- enum class BuildState {
- kDatabase,
- kCommandName,
- kMetadata,
- kCommandArgs,
- kInputDocs,
- kDone
- };
-
- BuildState _buildState{BuildState::kDatabase};
+ State _state{State::kDatabase};
};
} // rpc
diff --git a/src/mongo/rpc/request_builder_test.cpp b/src/mongo/rpc/command_request_builder_test.cpp
index 1d47ad79c9b..10a4f2d8795 100644
--- a/src/mongo/rpc/request_builder_test.cpp
+++ b/src/mongo/rpc/command_request_builder_test.cpp
@@ -28,12 +28,10 @@
#include "mongo/platform/basic.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/bson/util/builder.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/command_request.h"
+#include "mongo/rpc/command_request_builder.h"
#include "mongo/rpc/document_range.h"
-#include "mongo/rpc/request.h"
-#include "mongo/rpc/request_builder.h"
#include "mongo/unittest/unittest.h"
namespace {
@@ -72,7 +70,7 @@ namespace {
rpc::DocumentRange inputDocRange{inputDocs.buf(), inputDocs.buf() + inputDocs.len()};
- rpc::RequestBuilder r;
+ rpc::CommandRequestBuilder r;
auto msg = r.setDatabase(databaseName)
.setCommandName(commandName)
@@ -81,7 +79,7 @@ namespace {
.addInputDocs(inputDocRange)
.done();
- rpc::Request parsed(msg.get());
+ rpc::CommandRequest parsed(msg.get());
ASSERT_EQUALS(parsed.getDatabase(), databaseName);
ASSERT_EQUALS(parsed.getCommandName(), commandName);
diff --git a/src/mongo/rpc/request_test.cpp b/src/mongo/rpc/command_request_test.cpp
index da7c2d316db..7f5c5b52214 100644
--- a/src/mongo/rpc/request_test.cpp
+++ b/src/mongo/rpc/command_request_test.cpp
@@ -32,8 +32,8 @@
#include <string>
#include <vector>
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/rpc/request.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/command_request.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/net/message.h"
@@ -86,7 +86,7 @@ namespace {
Message toSend;
toSend.setData(dbCommand, opCommandData.data(), opCommandData.size());
- rpc::Request opCmd{&toSend};
+ rpc::CommandRequest opCmd{&toSend};
ASSERT_EQUALS(opCmd.getCommandName(), commandName);
ASSERT_EQUALS(opCmd.getDatabase(), database);
@@ -107,4 +107,3 @@ namespace {
ASSERT_TRUE(inputDocRangeIter == inputDocRange.end());
}
} // namespace
-
diff --git a/src/mongo/rpc/document_range.h b/src/mongo/rpc/document_range.h
index 04f00179fdd..aa8d3131b13 100644
--- a/src/mongo/rpc/document_range.h
+++ b/src/mongo/rpc/document_range.h
@@ -33,7 +33,7 @@
#include "mongo/base/data_range.h"
#include "mongo/base/data_range_cursor.h"
-#include "mongo/bson/bsonobj.h"
+#include "mongo/db/jsobj.h"
namespace mongo {
namespace rpc {
diff --git a/src/mongo/rpc/factory.cpp b/src/mongo/rpc/factory.cpp
new file mode 100644
index 00000000000..1edca3950b3
--- /dev/null
+++ b/src/mongo/rpc/factory.cpp
@@ -0,0 +1,70 @@
+/**
+ * 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/rpc/factory.h"
+
+#include "mongo/rpc/command_reply.h"
+#include "mongo/rpc/command_request_builder.h"
+#include "mongo/rpc/legacy_reply.h"
+#include "mongo/rpc/legacy_request_builder.h"
+#include "mongo/rpc/protocol.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace rpc {
+
+ std::unique_ptr<RequestBuilderInterface> makeRequestBuilder(ProtocolSet clientProtos,
+ ProtocolSet serverProtos) {
+ switch (uassertStatusOK(negotiate(clientProtos, serverProtos))) {
+ case Protocol::kOpQuery:
+ return stdx::make_unique<LegacyRequestBuilder>();
+ case Protocol::kOpCommandV1:
+ return stdx::make_unique<CommandRequestBuilder>();
+ default:
+ MONGO_UNREACHABLE;
+ }
+ }
+
+ std::unique_ptr<ReplyInterface> makeReply(const Message* unownedMessage,
+ ProtocolSet clientProtos,
+ ProtocolSet serverProtos) {
+ switch (uassertStatusOK(negotiate(clientProtos, serverProtos))) {
+ case Protocol::kOpQuery:
+ return stdx::make_unique<LegacyReply>(unownedMessage);
+ case Protocol::kOpCommandV1:
+ return stdx::make_unique<CommandReply>(unownedMessage);
+ default:
+ MONGO_UNREACHABLE;
+ }
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/factory.h b/src/mongo/rpc/factory.h
new file mode 100644
index 00000000000..0e178a54ecb
--- /dev/null
+++ b/src/mongo/rpc/factory.h
@@ -0,0 +1,60 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/rpc/protocol.h"
+
+#include <memory>
+
+/**
+ * Utilities to construct the correct concrete rpc class based on what the remote server
+ * supports, and what the client has been configured to do.
+ */
+
+namespace mongo {
+ class Message;
+
+namespace rpc {
+ class ReplyInterface;
+ class RequestBuilderInterface;
+
+ /**
+ * Returns the appropriate concrete RequestBuilder. Throws if one cannot be chosen.
+ */
+ std::unique_ptr<RequestBuilderInterface> makeRequestBuilder(ProtocolSet clientProtos,
+ ProtocolSet serverProtos);
+ /**
+ * Returns the appropriate concrete Reply. Throws if one cannot be chosen.
+ */
+ std::unique_ptr<ReplyInterface> makeReply(const Message* unownedMessage,
+ ProtocolSet clientProtos,
+ ProtocolSet serverProtos);
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_reply.cpp b/src/mongo/rpc/legacy_reply.cpp
new file mode 100644
index 00000000000..1dc40c83cd6
--- /dev/null
+++ b/src/mongo/rpc/legacy_reply.cpp
@@ -0,0 +1,82 @@
+/**
+ * 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/rpc/legacy_reply.h"
+
+#include <utility>
+
+#include "mongo/util/assert_util.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace rpc {
+
+ LegacyReply::LegacyReply(const Message* message)
+ : _message(std::move(message)) {
+ invariant(message->operation() == opReply);
+
+ QueryResult::View qr = _message->singleData().view2ptr();
+
+ // should be checked by caller.
+ invariant(qr.msgdata().getOperation() == opReply);
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "Got legacy command reply with a bad cursorId field,"
+ << " expected a value of 0 but got " << qr.getCursorId(),
+ qr.getCursorId() == 0);
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "Got legacy command reply with a bad nReturned field,"
+ << " expected a value of 1 but got " << qr.getNReturned(),
+ qr.getNReturned() == 1);
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "Got legacy command reply with a bad startingFrom field,"
+ << " expected a value of 0 but got " << qr.getStartingFrom(),
+ qr.getStartingFrom() == 0);
+ // TODO bson validation
+ _commandReply = BSONObj(qr.data());
+ }
+
+ const BSONObj& LegacyReply::getMetadata() const {
+ return _metadataPlaceholder;
+ }
+
+ const BSONObj& LegacyReply::getCommandReply() const {
+ return _commandReply;
+ }
+
+ DocumentRange LegacyReply::getOutputDocs() const {
+ // return empty range
+ return DocumentRange{};
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_reply.h b/src/mongo/rpc/legacy_reply.h
new file mode 100644
index 00000000000..fddc050a71b
--- /dev/null
+++ b/src/mongo/rpc/legacy_reply.h
@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/db/dbmessage.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/reply_interface.h"
+
+namespace mongo {
+ class Message;
+
+namespace rpc {
+
+ /**
+ * Immutable view of an OP_REPLY legacy-style command reply.
+ *
+ * TODO: BSON validation (SERVER-18167)
+ */
+ class LegacyReply : public ReplyInterface {
+ public:
+
+ /**
+ * Construct a Reply from a Message.
+ * The underlying message MUST outlive the Reply.
+ */
+ explicit LegacyReply(const Message* message);
+
+ /**
+ * Accessor for the metadata object. Metadata is generally used for information
+ * that is independent of any specific command, e.g. auditing information.
+ */
+ const BSONObj& getMetadata() const final;
+
+ /**
+ * The result of executing the command.
+ */
+ const BSONObj& getCommandReply() const final;
+
+ /**
+ * A variable number of BSON documents returned by the command. It is valid for the
+ * returned range to be empty.
+ *
+ * Example usage:
+ *
+ * for (auto&& doc : reply.getOutputDocs()) {
+ * ... do stuff with doc
+ * }
+ */
+ DocumentRange getOutputDocs() const final;
+
+ private:
+ const Message* _message;
+
+ // TODO: SERVER-18236
+ BSONObj _metadataPlaceholder{};
+ BSONObj _commandReply{}; // will hold unowned
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_reply_builder.cpp b/src/mongo/rpc/legacy_reply_builder.cpp
new file mode 100644
index 00000000000..4ed4ff88886
--- /dev/null
+++ b/src/mongo/rpc/legacy_reply_builder.cpp
@@ -0,0 +1,101 @@
+/**
+ * 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/db/dbmessage.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/legacy_reply_builder.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace rpc {
+
+ LegacyReplyBuilder::LegacyReplyBuilder()
+ : LegacyReplyBuilder(stdx::make_unique<Message>())
+ {}
+
+ LegacyReplyBuilder::LegacyReplyBuilder(std::unique_ptr<Message> message)
+ : _message{std::move(message)} {
+ _builder.skip(sizeof(QueryResult::Value));
+ }
+
+ LegacyReplyBuilder::~LegacyReplyBuilder() {}
+
+ LegacyReplyBuilder& LegacyReplyBuilder::setMetadata(BSONObj metadata) {
+ invariant(_state == State::kMetadata);
+ // no op for now: SERVER-18236
+ _state = State::kCommandReply;
+ return *this;
+ }
+
+ LegacyReplyBuilder& LegacyReplyBuilder::setRawCommandReply(BSONObj commandReply) {
+ invariant(_state == State::kCommandReply);
+ commandReply.appendSelfToBufBuilder(_builder);
+ _state = State::kOutputDocs;
+ return *this;
+ }
+
+ LegacyReplyBuilder& LegacyReplyBuilder::addOutputDocs(DocumentRange outputDocs) {
+ invariant(_state == State::kOutputDocs);
+ // no op
+ return *this;
+ }
+
+ LegacyReplyBuilder& LegacyReplyBuilder::addOutputDoc(BSONObj outputDoc) {
+ invariant(_state == State::kOutputDocs);
+ // no op
+ return *this;
+ }
+
+ ReplyBuilderInterface::State LegacyReplyBuilder::getState() const {
+ return _state;
+ }
+
+ std::unique_ptr<Message> LegacyReplyBuilder::done() {
+ invariant(_state == State::kOutputDocs);
+ std::unique_ptr<Message> message = stdx::make_unique<Message>();
+
+ QueryResult::View qr = _builder.buf();
+ qr.setResultFlagsToOk();
+ qr.msgdata().setLen(_builder.len());
+ qr.msgdata().setOperation(opReply);
+ qr.setCursorId(0);
+ qr.setStartingFrom(0);
+ qr.setNReturned(1);
+ _builder.decouple();
+
+ message->setData(qr.view2ptr(), true);
+
+ _state = State::kDone;
+ return std::move(message);
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_reply_builder.h b/src/mongo/rpc/legacy_reply_builder.h
new file mode 100644
index 00000000000..35dc49a911e
--- /dev/null
+++ b/src/mongo/rpc/legacy_reply_builder.h
@@ -0,0 +1,65 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "mongo/base/status_with.h"
+#include "mongo/bson/util/builder.h"
+#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/reply_builder_interface.h"
+
+namespace mongo {
+namespace rpc {
+
+ class LegacyReplyBuilder : public ReplyBuilderInterface {
+ public:
+
+ LegacyReplyBuilder();
+ LegacyReplyBuilder(std::unique_ptr<Message>);
+ ~LegacyReplyBuilder() final;
+
+ LegacyReplyBuilder& setMetadata(BSONObj metadata) final;
+ LegacyReplyBuilder& setRawCommandReply(BSONObj commandReply) final;
+
+ LegacyReplyBuilder& addOutputDocs(DocumentRange outputDocs) final;
+ LegacyReplyBuilder& addOutputDoc(BSONObj outputDoc) final;
+
+ State getState() const final;
+
+ std::unique_ptr<Message> done() final;
+
+ private:
+ BufBuilder _builder{};
+ std::unique_ptr<Message> _message;
+ State _state{State::kMetadata};
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_request.cpp b/src/mongo/rpc/legacy_request.cpp
new file mode 100644
index 00000000000..bb79928abb9
--- /dev/null
+++ b/src/mongo/rpc/legacy_request.cpp
@@ -0,0 +1,78 @@
+/**
+ * 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 <utility>
+
+#include "mongo/db/namespace_string.h"
+#include "mongo/rpc/legacy_request.h"
+#include "mongo/rpc/metadata.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace rpc {
+
+ LegacyRequest::LegacyRequest(const Message *message)
+ : _message(std::move(message))
+ , _dbMessage(*message)
+ , _queryMessage(_dbMessage)
+ , _database(NamespaceString(_queryMessage.ns).db().toString()) {
+
+ std::tie(_upconvertedCommandArgs, _upconvertedMetadata) = uassertStatusOK(
+ metadata::upconvertRequest(std::move(_queryMessage.query),
+ std::move(_queryMessage.queryOptions))
+ );
+ }
+
+ LegacyRequest::~LegacyRequest() = default;
+
+ StringData LegacyRequest::getDatabase() const {
+ return _database;
+ }
+
+ StringData LegacyRequest::getCommandName() const {
+ return _upconvertedCommandArgs.firstElement().fieldNameStringData();
+ }
+
+ const BSONObj& LegacyRequest::getMetadata() const {
+ // TODO SERVER-18236
+ return _upconvertedMetadata;
+ }
+
+ const BSONObj& LegacyRequest::getCommandArgs() const {
+ return _upconvertedCommandArgs;
+ }
+
+ DocumentRange LegacyRequest::getInputDocs() const {
+ // return an empty document range.
+ return DocumentRange{};
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_request.h b/src/mongo/rpc/legacy_request.h
new file mode 100644
index 00000000000..93567a6fe6c
--- /dev/null
+++ b/src/mongo/rpc/legacy_request.h
@@ -0,0 +1,103 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/base/string_data.h"
+#include "mongo/db/dbmessage.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/request_interface.h"
+
+namespace mongo {
+ class Message;
+
+namespace rpc {
+
+ /**
+ * An immutable view of an OP_QUERY command request. The underlying bytes are owned
+ * by a mongo::Message, which must outlive any LegacyRequest instances created from it.
+ *
+ */
+ class LegacyRequest : public RequestInterface {
+ public:
+ /**
+ * Construct a Request from a Message. Underlying message MUST outlive the Request.
+ * Required fields are parsed eagerly, inputDocs are parsed lazily.
+ */
+ explicit LegacyRequest(const Message* message);
+
+ ~LegacyRequest() final;
+
+ /**
+ * The database that the command is to be executed on.
+ */
+ StringData getDatabase() const final;
+
+ /**
+ * The name of the command to execute.
+ */
+ StringData getCommandName() const final;
+
+ /**
+ * The metadata associated with the command request. This is information that is
+ * independent of any specific command, i.e. auditing information.
+ */
+ const BSONObj& getMetadata() const final;
+
+ /**
+ * The arguments to the command - this is passed to the command's run() method.
+ */
+ const BSONObj& getCommandArgs() const final;
+
+ /**
+ * A variable number of BSON documents to pass to the command. It is valid for
+ * the returned range to be empty.
+ *
+ * Example usage:
+ *
+ * for (auto&& doc : req.getInputDocs()) {
+ * ... do stuff with doc
+ * }
+ */
+ DocumentRange getInputDocs() const final;
+
+ private:
+ const Message* _message;
+ // TODO: metadata will be handled in SERVER-18236
+ // for now getMetadata() is a no op
+ DbMessage _dbMessage;
+ QueryMessage _queryMessage;
+ std::string _database;
+
+ BSONObj _upconvertedMetadata;
+ BSONObj _upconvertedCommandArgs;
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_request_builder.cpp b/src/mongo/rpc/legacy_request_builder.cpp
new file mode 100644
index 00000000000..f1978583df8
--- /dev/null
+++ b/src/mongo/rpc/legacy_request_builder.cpp
@@ -0,0 +1,121 @@
+/**
+ * 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/rpc/legacy_request_builder.h"
+
+#include <utility>
+#include <tuple>
+
+#include "mongo/db/namespace_string.h"
+#include "mongo/rpc/metadata.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+namespace rpc {
+
+ LegacyRequestBuilder::LegacyRequestBuilder()
+ : _message{stdx::make_unique<Message>()}
+ {}
+
+ LegacyRequestBuilder::~LegacyRequestBuilder()
+ {}
+
+ LegacyRequestBuilder::LegacyRequestBuilder(std::unique_ptr<Message> message)
+ : _message{std::move(message)}
+ {}
+
+ LegacyRequestBuilder& LegacyRequestBuilder::setDatabase(StringData database) {
+ invariant(_state == State::kDatabase);
+ _ns = NamespaceString(database, "$cmd").toString();
+ _state = State::kCommandName;
+ return *this;
+ }
+
+ LegacyRequestBuilder& LegacyRequestBuilder::setCommandName(StringData commandName) {
+ invariant(_state == State::kCommandName);
+ // no op, as commandName is the first element of commandArgs
+ _state = State::kMetadata;
+ return *this;
+ }
+
+ LegacyRequestBuilder& LegacyRequestBuilder::setMetadata(BSONObj metadata) {
+ invariant(_state == State::kMetadata);
+ _metadata = std::move(metadata);
+ _state = State::kCommandArgs;
+ return *this;
+ }
+
+ LegacyRequestBuilder& LegacyRequestBuilder::setCommandArgs(BSONObj commandArgs) {
+ invariant(_state == State::kCommandArgs);
+
+ BSONObj legacyCommandArgs;
+ int queryOptions;
+
+ std::tie(legacyCommandArgs, queryOptions) = uassertStatusOK(
+ metadata::downconvertRequest(std::move(commandArgs),
+ std::move(_metadata))
+ );
+
+ _builder.appendNum(queryOptions); // queryOptions
+ _builder.appendStr(_ns);
+ _builder.appendNum(0); // nToSkip
+ _builder.appendNum(1); // nToReturn
+
+ legacyCommandArgs.appendSelfToBufBuilder(_builder);
+ _state = State::kInputDocs;
+ return *this;
+ }
+
+ LegacyRequestBuilder& LegacyRequestBuilder::addInputDocs(DocumentRange inputDocs) {
+ invariant(_state == State::kInputDocs);
+ // no op
+ return *this;
+ }
+
+ LegacyRequestBuilder& LegacyRequestBuilder::addInputDoc(BSONObj inputDoc) {
+ invariant(_state == State::kInputDocs);
+ // no op
+ return *this;
+ }
+
+ RequestBuilderInterface::State LegacyRequestBuilder::getState() const {
+ return _state;
+ }
+
+ std::unique_ptr<Message> LegacyRequestBuilder::done() {
+ invariant(_state == State::kInputDocs);
+ _message->setData(dbQuery, _builder.buf(), _builder.len());
+ _state = State::kDone;
+ return std::move(_message);
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/legacy_request_builder.h b/src/mongo/rpc/legacy_request_builder.h
new file mode 100644
index 00000000000..0beed3b9a71
--- /dev/null
+++ b/src/mongo/rpc/legacy_request_builder.h
@@ -0,0 +1,75 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "mongo/base/string_data.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/rpc/document_range.h"
+#include "mongo/rpc/request_builder_interface.h"
+#include "mongo/util/net/message.h"
+
+namespace mongo {
+namespace rpc {
+
+ class LegacyRequestBuilder : public RequestBuilderInterface {
+ public:
+ LegacyRequestBuilder();
+ ~LegacyRequestBuilder() final;
+
+ LegacyRequestBuilder(std::unique_ptr<Message>);
+
+ LegacyRequestBuilder& setDatabase(StringData database) final;
+ LegacyRequestBuilder& setCommandName(StringData commandName) final;
+ LegacyRequestBuilder& setMetadata(BSONObj metadata) final;
+ LegacyRequestBuilder& setCommandArgs(BSONObj commandArgs) final;
+
+ LegacyRequestBuilder& addInputDocs(DocumentRange inputDocs) final;
+ LegacyRequestBuilder& addInputDoc(BSONObj inputDoc) final;
+
+ State getState() const final;
+
+ std::unique_ptr<Message> done() final;
+
+ private:
+ std::unique_ptr<Message> _message;
+ BufBuilder _builder{};
+
+ // we need to stash this as we need commandArgs to
+ // upconvert.
+ BSONObj _metadata;
+
+ std::string _ns{}; // copied to in setDatabase
+
+ State _state{State::kDatabase};
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/metadata.cpp b/src/mongo/rpc/metadata.cpp
new file mode 100644
index 00000000000..055dfd07984
--- /dev/null
+++ b/src/mongo/rpc/metadata.cpp
@@ -0,0 +1,69 @@
+/**
+ * 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/rpc/metadata.h"
+
+#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/jsobj.h"
+
+namespace mongo {
+namespace rpc {
+namespace metadata {
+
+ BSONObj empty() {
+ return BSONObj();
+ }
+
+ const char kSecondaryOk[] = "$secondaryOk";
+
+ StatusWith<CommandAndMetadata> upconvertRequest(BSONObj legacyCmdObj, int queryFlags) {
+ BSONObjBuilder metadataBob;
+
+ // note second check may be erroneous: see SERVER-18194
+ if ((queryFlags & QueryOption_SlaveOk)) {
+ metadataBob.append(kSecondaryOk, 1);
+ }
+
+ return std::make_tuple(std::move(legacyCmdObj), std::move(metadataBob.obj()));
+ }
+
+ StatusWith<LegacyCommandAndFlags> downconvertRequest(BSONObj cmdObj, BSONObj metadata) {
+ int flags = 0;
+
+ if (metadata.hasField(kSecondaryOk)) {
+ flags |= QueryOption_SlaveOk;
+ }
+
+ return std::make_tuple(std::move(cmdObj), flags);
+ }
+
+} // namespace metadata
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/metadata.h b/src/mongo/rpc/metadata.h
new file mode 100644
index 00000000000..183dedffc5d
--- /dev/null
+++ b/src/mongo/rpc/metadata.h
@@ -0,0 +1,93 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <tuple>
+
+#include "mongo/base/status_with.h"
+
+namespace mongo {
+ class BSONObj;
+
+/**
+ * Utilities for converting metadata between the legacy OP_QUERY format and the new
+ * OP_COMMAND format.
+ *
+ * Metadata consists of information independent of any particular command such as:
+ *
+ * Request/Reply/Both | (legacy) OP_QUERY format | OP_COMMAND format
+ *__________________________________________________________________________________________________
+ * Request | the slaveOk bit | $secondaryOk on metadata obj
+ * Request | $readPreference field of command | $readPreference on metadata obj
+ * Request | $impersonatedUsers on command obj| $impersonatedUsers on metadata obj
+ * Request | $impersonatedRoles on command obj| $impersonatedRoles on metadata obj
+ * Request | maxTimeMS on command obj | $maxTimeMS on metadata obj
+ * Reply | $gleStats field on command reply | $gleStats on metadata obj
+ *
+ * TODO: currently only $secondaryOk (request only) is handled. SERVER-18236 will cover the rest.
+ */
+namespace rpc {
+namespace metadata {
+
+ /**
+ * Returns an empty metadata object.
+ */
+ BSONObj empty();
+
+ /**
+ * The field name for the secondaryOk metadata field.
+ */
+ extern const char kSecondaryOk[];
+
+ /**
+ * A command object and a corresponding metadata object.
+ */
+ using CommandAndMetadata = std::tuple<BSONObj, BSONObj>;
+
+ /**
+ * A legacy command object and a corresponding query flags bitfield. The legacy command object
+ * may contain metadata fields, so it cannot safely be passed to a command's run method.
+ */
+ using LegacyCommandAndFlags = std::tuple<BSONObj, int>;
+
+ /**
+ * Given a legacy command object and a query flags bitfield, attempts to parse and remove
+ * the metadata from the command object and construct a corresponding metadata object.
+ */
+ StatusWith<CommandAndMetadata> upconvertRequest(BSONObj legacyCmdObj, int queryFlags);
+
+ /**
+ * Given a command object and a metadata object, attempts to construct a legacy command
+ * object and query flags bitfield augmented with the given metadata.
+ */
+ StatusWith<LegacyCommandAndFlags> downconvertRequest(BSONObj cmdObj, BSONObj metadata);
+
+} // namespace metadata
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/protocol.cpp b/src/mongo/rpc/protocol.cpp
new file mode 100644
index 00000000000..dae77abce3b
--- /dev/null
+++ b/src/mongo/rpc/protocol.cpp
@@ -0,0 +1,117 @@
+/**
+ * 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/rpc/protocol.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "mongo/base/string_data.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace rpc {
+
+namespace {
+
+ /**
+ * Protocols supported by order of preference.
+ */
+ const Protocol kPreferredProtos[] = {
+ Protocol::kOpCommandV1,
+ Protocol::kOpQuery
+ };
+
+ const char kNone[] = "none";
+ const char kOpQueryOnly[] = "opQueryOnly";
+ const char kOpCommandOnly[] = "opCommandOnly";
+ const char kAll[] = "all";
+
+} // namespace
+
+ StatusWith<Protocol> negotiate(ProtocolSet fst, ProtocolSet snd) {
+ using std::begin;
+ using std::end;
+
+ ProtocolSet common = fst & snd;
+
+ auto it =
+ std::find_if(begin(kPreferredProtos), end(kPreferredProtos), [common](Protocol p) {
+ return common & static_cast<ProtocolSet>(p);
+ });
+
+ if (it == end(kPreferredProtos)) {
+ return Status(ErrorCodes::RPCProtocolNegotiationFailed,
+ "No common protocol found.");
+ }
+ return *it;
+ }
+
+ StatusWith<StringData> toString(ProtocolSet protocols) {
+ switch (protocols) {
+ case supports::kNone:
+ return StringData(kNone);
+ case supports::kOpQueryOnly:
+ return StringData(kOpQueryOnly);
+ case supports::kOpCommandOnly:
+ return StringData(kOpCommandOnly);
+ case supports::kAll:
+ return StringData(kAll);
+ default:
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "Can not convert ProtocolSet " << protocols
+ << " to a string, only the predefined ProtocolSet "
+ << "constants 'none' (0x0), 'opQueryOnly' (0x1), "
+ << "'opCommandOnly' (0x2), and 'all' (0x3) are supported.");
+ }
+ }
+
+ StatusWith<ProtocolSet> parseProtocolSet(StringData repr) {
+ if (repr == kNone) {
+ return supports::kNone;
+ }
+ else if (repr == kOpQueryOnly) {
+ return supports::kOpQueryOnly;
+ }
+ else if (repr == kOpCommandOnly) {
+ return supports::kOpCommandOnly;
+ }
+ else if (repr == kAll) {
+ return supports::kAll;
+ }
+ return Status(ErrorCodes::BadValue,
+ str::stream() << "Can not parse a ProtocolSet from " << repr
+ << " only the predefined ProtocolSet constants "
+ << "'none' (0x0), 'opQueryOnly' (0x1), 'opCommandOnly' (0x2), "
+ << "and 'all' (0x3) are supported.");
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/protocol.h b/src/mongo/rpc/protocol.h
new file mode 100644
index 00000000000..ba370a90e69
--- /dev/null
+++ b/src/mongo/rpc/protocol.h
@@ -0,0 +1,99 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <type_traits>
+
+#include "mongo/base/status_with.h"
+#include "mongo/platform/cstdint.h"
+
+namespace mongo {
+namespace rpc {
+
+ /**
+ * Bit flags representing support for a particular RPC protocol.
+ * This is just an internal representation, and is never transmitted over the wire. It should
+ * never be used for any other feature detection in favor of max/min wire version.
+ *
+ * A new protocol must be added as the highest order bit flag so that it is prioritized in
+ * negotiation.
+ */
+ enum class Protocol : std::uint64_t {
+
+ /**
+ * The pre-3.2 OP_QUERY on db.$cmd protocol
+ */
+ kOpQuery = 1 << 0,
+
+ /**
+ * The post-3.2 OP_COMMAND protocol.
+ */
+ kOpCommandV1 = 1 << 1,
+ };
+
+ /**
+ * Bitfield representing a set of supported RPC protocols.
+ */
+ using ProtocolSet = std::underlying_type<Protocol>::type;
+
+/**
+ * This namespace contains predefined bitfields for common levels of protocol support.
+ */
+namespace supports {
+
+ const ProtocolSet kNone = ProtocolSet{0};
+ const ProtocolSet kOpQueryOnly = static_cast<ProtocolSet>(Protocol::kOpQuery);
+ const ProtocolSet kOpCommandOnly = static_cast<ProtocolSet>(Protocol::kOpCommandV1);
+ const ProtocolSet kAll = kOpQueryOnly | kOpCommandOnly;
+
+} // namespace supports
+
+ /**
+ * Returns the newest protocol supported by two parties.
+ */
+ StatusWith<Protocol> negotiate(ProtocolSet fst, ProtocolSet snd);
+
+ /**
+ * Converts a ProtocolSet to a string. Currently only the predefined ProtocolSets in the
+ * 'supports' namespace are supported.
+ *
+ * This intentionally does not conform to the STL 'to_string' convention so that it will
+ * not conflict with the to_string overload for uint64_t.
+ */
+ StatusWith<StringData> toString(ProtocolSet protocols);
+
+ /**
+ * Parses a ProtocolSet from a string. Currently only the predefined ProtocolSets in the
+ * 'supports' namespace are supported
+ */
+ StatusWith<ProtocolSet> parseProtocolSet(StringData repr);
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/protocol_test.cpp b/src/mongo/rpc/protocol_test.cpp
new file mode 100644
index 00000000000..86211717eb7
--- /dev/null
+++ b/src/mongo/rpc/protocol_test.cpp
@@ -0,0 +1,66 @@
+/**
+ * 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/base/status.h"
+#include "mongo/rpc/protocol.h"
+#include "mongo/unittest/unittest.h"
+
+namespace {
+
+ using namespace mongo::rpc;
+
+ // Checks if negotiation of the first to protocol sets results in the 'proto'
+ const auto assert_negotiated = [](ProtocolSet fst, ProtocolSet snd, Protocol proto) {
+ auto negotiated = negotiate(fst, snd);
+ ASSERT_TRUE(negotiated.isOK());
+ ASSERT_TRUE(negotiated.getValue() == proto);
+ };
+
+ TEST(Protocol, SuccessfulNegotiation) {
+ assert_negotiated(supports::kAll, supports::kAll, Protocol::kOpCommandV1);
+ assert_negotiated(supports::kAll, supports::kOpCommandOnly, Protocol::kOpCommandV1);
+ assert_negotiated(supports::kAll, supports::kOpQueryOnly, Protocol::kOpQuery);
+ }
+
+ // Checks that negotiation fails
+ const auto assert_not_negotiated = [](ProtocolSet fst, ProtocolSet snd) {
+ auto proto = negotiate(fst, snd);
+ ASSERT_TRUE(!proto.isOK());
+ ASSERT_TRUE(proto.getStatus().code() == mongo::ErrorCodes::RPCProtocolNegotiationFailed);
+ };
+
+ TEST(Protocol, FailedNegotiation) {
+ assert_not_negotiated(supports::kOpQueryOnly, supports::kOpCommandOnly);
+ assert_not_negotiated(supports::kAll, supports::kNone);
+ assert_not_negotiated(supports::kOpQueryOnly, supports::kNone);
+ assert_not_negotiated(supports::kOpCommandOnly, supports::kNone);
+ }
+
+} // namespace
diff --git a/src/mongo/rpc/reply_builder_interface.cpp b/src/mongo/rpc/reply_builder_interface.cpp
new file mode 100644
index 00000000000..1b4b5aadbda
--- /dev/null
+++ b/src/mongo/rpc/reply_builder_interface.cpp
@@ -0,0 +1,85 @@
+/**
+ * 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/rpc/reply_builder_interface.h"
+
+#include <utility>
+
+#include "mongo/base/status_with.h"
+#include "mongo/db/jsobj.h"
+
+namespace mongo {
+namespace rpc {
+
+ namespace {
+ const char kOKField[] = "ok";
+ const char kCodeField[] = "code";
+ const char kErrorField[] = "errmsg";
+
+ // similar to appendCommandStatus (duplicating logic here to avoid cyclic library
+ // dependency)
+ BSONObj augmentReplyWithStatus(const Status& status, const BSONObj& reply) {
+ BSONObjBuilder bob;
+ bob.appendElements(reply);
+
+ if (!reply.hasField(kOKField)) {
+ bob.append(kOKField, status.isOK() ? 1.0 : 0.0);
+ }
+
+ if (status.isOK()) {
+ return bob.obj();
+ }
+
+ if (!reply.hasField(kErrorField)) {
+ bob.append(kErrorField, status.reason());
+ }
+
+ if (!reply.hasField(kCodeField)) {
+ bob.append(kCodeField, status.code());
+ }
+
+ return bob.obj();
+ }
+ }
+
+ ReplyBuilderInterface&
+ ReplyBuilderInterface::setCommandReply(StatusWith<BSONObj> commandReply) {
+ auto reply = commandReply.isOK() ? std::move(commandReply.getValue()) : BSONObj();
+ return setRawCommandReply(augmentReplyWithStatus(commandReply.getStatus(), reply));
+ }
+
+ ReplyBuilderInterface&
+ ReplyBuilderInterface::setCommandReply(Status nonOKStatus, BSONObj extraErrorInfo) {
+ invariant(!nonOKStatus.isOK());
+ return setRawCommandReply(augmentReplyWithStatus(nonOKStatus, extraErrorInfo));
+ }
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/reply_builder_interface.h b/src/mongo/rpc/reply_builder_interface.h
new file mode 100644
index 00000000000..00942fe1dac
--- /dev/null
+++ b/src/mongo/rpc/reply_builder_interface.h
@@ -0,0 +1,125 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status_with.h"
+
+namespace mongo {
+ class BSONObj;
+ class Message;
+
+namespace rpc {
+ class DocumentRange;
+
+ /**
+ * Constructs an RPC Reply.
+ */
+ class ReplyBuilderInterface {
+ MONGO_DISALLOW_COPYING(ReplyBuilderInterface);
+ public:
+
+ /**
+ * Reply builders must have their fields set in order as they are immediately written into
+ * the underlying message buffer. This enum represents the next field that can be written
+ * into the builder. Note that when the builder is in state 'kInputDocs', multiple input
+ * docs can be added. After the builder's done() method is called it is in state 'kDone',
+ * and no further methods can be called.
+ */
+ enum class State {
+ kMetadata,
+ kCommandReply,
+ kOutputDocs,
+ kDone
+ };
+
+ virtual ~ReplyBuilderInterface() = default;
+
+ virtual ReplyBuilderInterface& setMetadata(BSONObj metadata) = 0;
+
+ /**
+ * Sets the raw command reply. This should probably not be used in favor of the
+ * variants that accept a Status or StatusWith.
+ */
+ virtual ReplyBuilderInterface& setRawCommandReply(BSONObj reply) = 0;
+
+ /**
+ * Sets the reply for this command. If an engaged StatusWith<BSONObj> is passed, the command
+ * reply will be set to the contained BSONObj, augmented with the element {ok, 1.0} if it
+ * does not already have an "ok" field. If a disengaged StatusWith<BSONObj> is passed, the
+ * command reply will be set to {ok: 0.0, code: <code of status>,
+ * errmsg: <reason of status>}
+ */
+ ReplyBuilderInterface& setCommandReply(StatusWith<BSONObj> commandReply);
+
+ /**
+ * Sets the reply for this command. The status parameter must be non-OK. The reply for
+ * this command will be set to an object containing all the fields in extraErrorInfo,
+ * augmented with {ok: 0.0} , {code: <code of status>}, and {errmsg: <reason of status>}.
+ * If any of the fields "ok", "code", or "errmsg" already exist in extraErrorInfo, they
+ * will be left as-is in the command reply. This use of this form is intended for
+ * interfacing with legacy code that adds additional data to a failed command reply and
+ * its use is discouraged in new code.
+ */
+ ReplyBuilderInterface& setCommandReply(Status nonOKStatus, BSONObj extraErrorInfo);
+
+ /**
+ * Add a range of output documents to the reply. This method can be called multiple times
+ * before calling done().
+ */
+ virtual ReplyBuilderInterface& addOutputDocs(DocumentRange outputDocs) = 0;
+
+ /**
+ * Add a single output document to the reply. This method can be called multiple times
+ * before calling done().
+ */
+ virtual ReplyBuilderInterface& addOutputDoc(BSONObj outputDoc) = 0;
+
+ /**
+ * Gets the state of the builder. As the builder will simply crash the process if it is ever
+ * put in an invalid state, it isn't neccessary to call this method for correctness. Rather
+ * it may be helpful to explicitly assert that the builder is in a certain state to make
+ * code that manipulates the builder more readable.
+ */
+ virtual State getState() const = 0;
+
+ /**
+ * Writes data then transfers ownership of the message to the caller. The behavior of
+ * calling any methods on the builder is subsequently undefined.
+ */
+ virtual std::unique_ptr<Message> done() = 0;
+
+ protected:
+ ReplyBuilderInterface() = default;
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/reply_interface.h b/src/mongo/rpc/reply_interface.h
new file mode 100644
index 00000000000..3fd5644f785
--- /dev/null
+++ b/src/mongo/rpc/reply_interface.h
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/base/disallow_copying.h"
+
+namespace mongo {
+ class BSONObj;
+ class Message;
+
+namespace rpc {
+ class DocumentRange;
+
+ /**
+ * An immutable view of an RPC Reply.
+ */
+ class ReplyInterface {
+ MONGO_DISALLOW_COPYING(ReplyInterface);
+ public:
+ virtual ~ReplyInterface() = default;
+
+ /**
+ * Accessor for the metadata object. Metadata is generally used for information
+ * that is independent of any specific command, e.g. auditing information.
+ */
+ virtual const BSONObj& getMetadata() const = 0;
+
+ /**
+ * The result of executing the command.
+ */
+ virtual const BSONObj& getCommandReply() const = 0;
+
+ /**
+ * A variable number of BSON documents returned by the command. It is valid for the
+ * returned range to be empty.
+ *
+ * Example usage:
+ *
+ * for (auto&& doc : reply.getOutputDocs()) {
+ * ... do stuff with doc
+ * }
+ */
+ virtual DocumentRange getOutputDocs() const = 0;
+
+ protected:
+ ReplyInterface() = default;
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/request_builder_interface.h b/src/mongo/rpc/request_builder_interface.h
new file mode 100644
index 00000000000..39623e12a10
--- /dev/null
+++ b/src/mongo/rpc/request_builder_interface.h
@@ -0,0 +1,118 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "mongo/base/disallow_copying.h"
+
+namespace mongo {
+ class Message;
+ class BSONObj;
+ class StringData;
+
+namespace rpc {
+ class DocumentRange;
+
+ /**
+ * Constructs an RPC request.
+ */
+ class RequestBuilderInterface {
+ MONGO_DISALLOW_COPYING(RequestBuilderInterface);
+ public:
+
+ /**
+ * Request builders must have their fields set in order as they are immediately written into
+ * the underlying message buffer. This enum represents the next field that can be written
+ * into the builder. Note that when the builder is in state 'kInputDocs', multiple input
+ * docs can be added. After the builder's done() method is called it is in state 'kDone',
+ * and no further methods can be called.
+ */
+ enum class State {
+ kDatabase,
+ kCommandName,
+ kMetadata,
+ kCommandArgs,
+ kInputDocs,
+ kDone
+ };
+
+ virtual ~RequestBuilderInterface() = default;
+
+ /**
+ * Sets the database that the command will be executed against.
+ */
+ virtual RequestBuilderInterface& setDatabase(StringData database) = 0;
+
+ /**
+ * Sets the name of the command to execute.
+ */
+ virtual RequestBuilderInterface& setCommandName(StringData commandName) = 0;
+
+ /**
+ * Sets the metadata associated with this command request - see metadata.h for details.
+ */
+ virtual RequestBuilderInterface& setMetadata(BSONObj metadata) = 0;
+
+ /**
+ * Sets the arguments to pass to the command.
+ */
+ virtual RequestBuilderInterface& setCommandArgs(BSONObj commandArgs) = 0;
+
+ /**
+ * Add a range of input documents to the request. This method can be called multiple times
+ * before calling done().
+ */
+ virtual RequestBuilderInterface& addInputDocs(DocumentRange inputDocs) = 0;
+
+ /**
+ * Add a single output document to the request. This method can be called multiple times
+ * before calling done().
+ */
+ virtual RequestBuilderInterface& addInputDoc(BSONObj inputDoc) = 0;
+
+ /**
+ * Get the state of the builder. This method is intended to enable debug or invariant
+ * checks that the builder is in the correct state.
+ */
+ virtual State getState() const = 0;
+
+ /**
+ * Writes data then transfers ownership of the message to the caller.
+ * The behavior of calling any methods on the object is subsequently
+ * undefined.
+ */
+ virtual std::unique_ptr<Message> done() = 0;
+
+ protected:
+ RequestBuilderInterface() = default;
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/rpc/request_interface.h b/src/mongo/rpc/request_interface.h
new file mode 100644
index 00000000000..27f407f3f5c
--- /dev/null
+++ b/src/mongo/rpc/request_interface.h
@@ -0,0 +1,88 @@
+/**
+ * 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.
+ */
+
+#pragma once
+
+#include "mongo/base/disallow_copying.h"
+
+namespace mongo {
+ class BSONObj;
+ class Message;
+ class StringData;
+
+namespace rpc {
+ class DocumentRange;
+
+ /**
+ * An immutable view of an RPC message.
+ */
+ class RequestInterface {
+ MONGO_DISALLOW_COPYING(RequestInterface);
+ public:
+ virtual ~RequestInterface() = default;
+
+ /**
+ * Gets the database that the command is to be executed on.
+ */
+ virtual StringData getDatabase() const = 0;
+
+ /**
+ * Gets the name of the command to execute.
+ */
+ virtual StringData getCommandName() const = 0;
+
+ /**
+ * Gets the metadata associated with the command request. This is information that is
+ * independent of any specific command, i.e. auditing information. See metadata.h for
+ * further details.
+ */
+ virtual const BSONObj& getMetadata() const = 0;
+
+ /**
+ * Gets the arguments to the command - this is passed to the command's run() method.
+ */
+ virtual const BSONObj& getCommandArgs() const = 0;
+
+ /**
+ * A variable number of BSON documents to pass to the command. It is valid for
+ * the returned range to be empty.
+ *
+ * Example usage:
+ *
+ * for (auto&& doc : req.getInputDocs()) {
+ * ... do stuff with doc
+ * }
+ */
+ virtual DocumentRange getInputDocs() const = 0;
+
+ protected:
+ RequestInterface() = default;
+ };
+
+} // namespace rpc
+} // namespace mongo
diff --git a/src/mongo/util/net/message.h b/src/mongo/util/net/message.h
index c1627550724..b3573026f84 100644
--- a/src/mongo/util/net/message.h
+++ b/src/mongo/util/net/message.h
@@ -82,6 +82,8 @@ namespace mongo {
case dbGetMore: return "getmore";
case dbDelete: return "remove";
case dbKillCursors: return "killcursors";
+ case dbCommand: return "command";
+ case dbCommandReply: return "commandReply";
default:
massert( 16141, str::stream() << "cannot translate opcode " << op, !op );
return "";