OpMsg OpMsg::parse(const Message& message) try { // It is the caller's responsibility to call the correct parser for a given message type. invariant(!message.empty()); invariant(message.operation() == dbMsg); const uint32_t flags = OpMsg::flags(message); uassert(ErrorCodes::IllegalOpMsgFlag, str::stream() << "Message contains illegal flags value: Ob" << std::bitset<32>(flags).to_string(), !containsUnknownRequiredFlags(flags)); constexpr int kCrc32Size = 4; const bool haveChecksum = flags & kChecksumPresent; const int checksumSize = haveChecksum ? kCrc32Size : 0; // The sections begin after the flags and before the checksum (if present). BufReader sectionsBuf(message.singleData().data() + sizeof(flags), message.dataSize() - sizeof(flags) - checksumSize); // TODO some validation may make more sense in the IDL parser. I've tagged them with comments. bool haveBody = false; OpMsg msg; while (!sectionsBuf.atEof()) { const auto sectionKind = sectionsBuf.read<Section>(); switch (sectionKind) { case Section::kBody: { uassert(40430, "Multiple body sections in message", !haveBody); haveBody = true; msg.body = sectionsBuf.read<Validated<BSONObj>>(); break; } case Section::kDocSequence: { // We use an O(N^2) algorithm here and an O(N*M) algorithm below. These are fastest // for the current small values of N, but would be problematic if it is large. // If we need more document sequences, raise the limit and use a better algorithm. uassert(ErrorCodes::TooManyDocumentSequences, "Too many document sequences in OP_MSG", msg.sequences.size() < 2); // Limit is <=2 since we are about to add one. // The first 4 bytes are the total size, including themselves. const auto remainingSize = sectionsBuf.read<LittleEndian<int32_t>>() - sizeof(int32_t); BufReader seqBuf(sectionsBuf.skip(remainingSize), remainingSize); const auto name = seqBuf.readCStr(); uassert(40431, str::stream() << "Duplicate document sequence: " << name, !msg.getSequence(name)); // TODO IDL msg.sequences.push_back({name.toString()}); while (!seqBuf.atEof()) { msg.sequences.back().objs.push_back(seqBuf.read<Validated<BSONObj>>()); } break; } default: // Using uint32_t so we append as a decimal number rather than as a char. uasserted(40432, str::stream() << "Unknown section kind " << uint32_t(sectionKind)); } } uassert(40587, "OP_MSG messages must have a body", haveBody); // Detect duplicates between doc sequences and body. TODO IDL // Technically this is O(N*M) but N is at most 2. for (const auto& docSeq : msg.sequences) { const char* name = docSeq.name.c_str(); // Pointer is redirected by next call. auto inBody = !dotted_path_support::extractElementAtPathOrArrayAlongPath(msg.body, name).eoo(); uassert(40433, str::stream() << "Duplicate field between body and document sequence " << docSeq.name, !inBody); } return msg; } catch (const DBException& ex) { LOG(1) << "invalid message: " << ex.code() << " " << redact(ex) << " -- " << redact(hexdump(message.singleData().view2ptr(), message.size())); throw; }
OpMsg OpMsg::parse(const Message& message) try { // TODO some validation may make more sense in the IDL parser. I've tagged them with comments. OpMsg msg; // Use a separate BufReader for the flags since the flags can change how much room we have // for sections. BufReader(message.singleData().data(), message.dataSize()).read(msg.flags); uassert(40429, str::stream() << "Message contains illegal flags value: " << msg.flags, !containsUnknownRequiredFlags(msg.flags)); invariant(!msg.isFlagSet(kChecksumPresent)); // TODO SERVER-28679 check checksum here. constexpr int kCrc32Size = 4; const int checksumSize = msg.isFlagSet(kChecksumPresent) ? kCrc32Size : 0; BufReader sectionsBuf(message.singleData().data() + sizeof(msg.flags), message.dataSize() - sizeof(msg.flags) - checksumSize); bool haveBody = false; while (!sectionsBuf.atEof()) { const auto sectionKind = sectionsBuf.read<Section>(); switch (sectionKind) { case Section::kBody: { uassert(40430, "Multiple body sections in message", !haveBody); haveBody = true; msg.body = sectionsBuf.read<Validated<BSONObj>>(); break; } case Section::kDocSequence: { // The first 4 bytes are the total size, including themselves. const auto remainingSize = sectionsBuf.read<int32_t>() - sizeof(int32_t); BufReader seqBuf(sectionsBuf.skip(remainingSize), remainingSize); const auto name = seqBuf.readCStr(); uassert(40431, str::stream() << "Duplicate document sequence: " << name, !msg.getSequence(name)); // TODO IDL msg.sequences.push_back({name.toString()}); while (!seqBuf.atEof()) { msg.sequences.back().objs.push_back(seqBuf.read<Validated<BSONObj>>()); } break; } default: // Using uint32_t so we append as a decimal number rather than as a char. uasserted(40432, str::stream() << "Unknown section kind " << uint32_t(sectionKind)); } } // Detect duplicates between doc sequences and body. TODO IDL // Technically this is O(N*M) but N is at most 2. for (const auto& docSeq : msg.sequences) { const char* name = docSeq.name.c_str(); // Pointer is redirected by next call. auto inBody = !dotted_path_support::extractElementAtPathOrArrayAlongPath(msg.body, name).eoo(); uassert(40433, str::stream() << "Duplicate field between body and document sequence " << docSeq.name, !inBody); } return msg; } catch (const DBException& ex) { // TODO change to LOG(1). log() << "invalid message: " << redact(ex) << ' ' << hexdump(message.singleData().view2ptr(), message.size()); throw; }