uint32_t OpMsg::flags(const Message& message) { if (message.operation() != dbMsg) return 0; // Other command protocols are the same as no flags set. return BufReader(message.singleData().data(), message.dataSize()) .read<LittleEndian<uint32_t>>(); }
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; }