// Copyright (c) Facebook, Inc. and its affiliates. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "rsocket/framing/FrameSerializer_v1_0.h" #include namespace rsocket { constexpr const ProtocolVersion FrameSerializerV1_0::Version; constexpr const size_t FrameSerializerV1_0::kFrameHeaderSize; constexpr const size_t FrameSerializerV1_0::kMinBytesNeededForAutodetection; namespace { constexpr const uint32_t kMedatadaLengthSize = 3u; // bytes constexpr const uint32_t kMaxMetadataLength = 0xFFFFFFu; // 24bit max value } // namespace ProtocolVersion FrameSerializerV1_0::protocolVersion() const { return Version; } static FrameType deserializeFrameType(uint16_t frameType) { if (frameType > static_cast(FrameType::RESUME_OK) && frameType != static_cast(FrameType::EXT)) { return FrameType::RESERVED; } return static_cast(frameType); } static void serializeHeaderInto( folly::io::QueueAppender& appender, const FrameHeader& header) { appender.writeBE(static_cast(header.streamId)); auto type = static_cast(header.type); // 6 bit auto flags = static_cast(header.flags); // 10 bit appender.write(static_cast((type << 2) | (flags >> 8))); appender.write(static_cast(flags)); // lower 8 bits } static void deserializeHeaderFrom(folly::io::Cursor& cur, FrameHeader& header) { auto streamId = cur.readBE(); if (streamId < 0) { throw std::runtime_error("invalid stream id"); } header.streamId = static_cast(streamId); uint16_t type = cur.readBE(); // |Frame Type |I|M| header.type = deserializeFrameType(type >> 2); header.flags = static_cast(((type & 0x3) << 8) | cur.readBE()); } static void serializeMetadataInto( folly::io::QueueAppender& appender, std::unique_ptr metadata) { if (metadata == nullptr) { return; } // metadata length field not included in the medatadata length uint32_t metadataLength = static_cast(metadata->computeChainDataLength()); CHECK_LT(metadataLength, kMaxMetadataLength) << "Metadata is too big to serialize"; appender.write(static_cast(metadataLength >> 16)); // first byte appender.write( static_cast((metadataLength >> 8) & 0xFF)); // second byte appender.write(static_cast(metadataLength & 0xFF)); // third byte appender.insert(std::move(metadata)); } std::unique_ptr FrameSerializerV1_0::deserializeMetadataFrom( folly::io::Cursor& cur, FrameFlags flags) { if (!(flags & FrameFlags::METADATA)) { return nullptr; } uint32_t metadataLength = 0; metadataLength |= static_cast(cur.read() << 16); metadataLength |= static_cast(cur.read() << 8); metadataLength |= cur.read(); CHECK_LE(metadataLength, kMaxMetadataLength) << "Read out the 24-bit integer incorrectly somehow"; std::unique_ptr metadata; cur.clone(metadata, metadataLength); return metadata; } static std::unique_ptr deserializeDataFrom( folly::io::Cursor& cur) { std::unique_ptr data; auto totalLength = cur.totalLength(); if (totalLength > 0) { cur.clone(data, totalLength); } return data; } static Payload deserializePayloadFrom( folly::io::Cursor& cur, FrameFlags flags) { auto metadata = FrameSerializerV1_0::deserializeMetadataFrom(cur, flags); auto data = deserializeDataFrom(cur); return Payload(std::move(data), std::move(metadata)); } static void serializePayloadInto( folly::io::QueueAppender& appender, Payload&& payload) { serializeMetadataInto(appender, std::move(payload.metadata)); if (payload.data) { appender.insert(std::move(payload.data)); } } static uint32_t payloadFramingSize(const Payload& payload) { return (payload.metadata != nullptr ? kMedatadaLengthSize : 0); } std::unique_ptr FrameSerializerV1_0::serializeOutInternal( Frame_REQUEST_Base&& frame) const { auto queue = createBufferQueue( FrameSerializerV1_0::kFrameHeaderSize + sizeof(uint32_t) + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(static_cast(frame.requestN_)); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } static bool deserializeFromInternal( Frame_REQUEST_Base& frame, std::unique_ptr in) { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); auto requestN = cur.readBE(); // TODO(lehecka): requestN <= 0 if (requestN < 0) { throw std::runtime_error("invalid request N"); } frame.requestN_ = static_cast(requestN); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } static size_t getResumeIdTokenFramingLength( FrameFlags flags, const ResumeIdentificationToken& token) { return !!(flags & FrameFlags::RESUME_ENABLE) ? sizeof(uint16_t) + token.data().size() : 0; } FrameType FrameSerializerV1_0::peekFrameType(const folly::IOBuf& in) const { folly::io::Cursor cur(&in); try { cur.skip(sizeof(int32_t)); // streamId uint8_t type = cur.readBE(); // |Frame Type |I|M| return deserializeFrameType(type >> 2); } catch (...) { return FrameType::RESERVED; } } folly::Optional FrameSerializerV1_0::peekStreamId( const folly::IOBuf& in, bool skipFrameLengthBytes) const { folly::io::Cursor cur(&in); try { if (skipFrameLengthBytes) { cur.skip(3); // skip 3 bytes for frame length } auto streamId = cur.readBE(); if (streamId < 0) { return folly::none; } return folly::make_optional(static_cast(streamId)); } catch (...) { return folly::none; } } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_REQUEST_STREAM&& frame) const { return serializeOutInternal(std::move(frame)); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_REQUEST_CHANNEL&& frame) const { return serializeOutInternal(std::move(frame)); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_REQUEST_RESPONSE&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_REQUEST_FNF&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_REQUEST_N&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + sizeof(uint32_t)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(static_cast(frame.requestN_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_METADATA_PUSH&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); if (frame.metadata_) { appender.insert(std::move(frame.metadata_)); } return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_CANCEL&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_PAYLOAD&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_ERROR&& frame) const { auto queue = createBufferQueue( kFrameHeaderSize + sizeof(uint32_t) + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(static_cast(frame.errorCode_)); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_KEEPALIVE&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + sizeof(int64_t)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(static_cast(frame.position_)); if (frame.data_) { appender.insert(std::move(frame.data_)); } return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_SETUP&& frame) const { auto queue = createBufferQueue( kFrameHeaderSize + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(int32_t) + sizeof(int32_t) + getResumeIdTokenFramingLength(frame.header_.flags, frame.token_) + +sizeof(uint8_t) + frame.metadataMimeType_.length() + sizeof(uint8_t) + frame.dataMimeType_.length() + payloadFramingSize(frame.payload_)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); CHECK( frame.versionMajor_ != ProtocolVersion::Unknown.major || frame.versionMinor_ != ProtocolVersion::Unknown.minor); appender.writeBE(frame.versionMajor_); appender.writeBE(frame.versionMinor_); appender.writeBE(static_cast(frame.keepaliveTime_)); appender.writeBE(static_cast(frame.maxLifetime_)); if (!!(frame.header_.flags & FrameFlags::RESUME_ENABLE)) { appender.writeBE( static_cast(frame.token_.data().size())); appender.push(frame.token_.data().data(), frame.token_.data().size()); } CHECK( frame.metadataMimeType_.length() <= std::numeric_limits::max()); appender.writeBE(static_cast(frame.metadataMimeType_.length())); appender.push( reinterpret_cast(frame.metadataMimeType_.data()), frame.metadataMimeType_.length()); CHECK(frame.dataMimeType_.length() <= std::numeric_limits::max()); appender.writeBE(static_cast(frame.dataMimeType_.length())); appender.push( reinterpret_cast(frame.dataMimeType_.data()), frame.dataMimeType_.length()); serializePayloadInto(appender, std::move(frame.payload_)); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_LEASE&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + sizeof(int32_t) + sizeof(int32_t)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(static_cast(frame.ttl_)); appender.writeBE(static_cast(frame.numberOfRequests_)); if (frame.metadata_) { appender.insert(std::move(frame.metadata_)); } return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_RESUME&& frame) const { auto queue = createBufferQueue( kFrameHeaderSize + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + frame.token_.data().size() + sizeof(int32_t) + sizeof(int32_t)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); CHECK( frame.versionMajor_ != ProtocolVersion::Unknown.major || frame.versionMinor_ != ProtocolVersion::Unknown.minor); appender.writeBE(static_cast(frame.versionMajor_)); appender.writeBE(static_cast(frame.versionMinor_)); appender.writeBE(static_cast(frame.token_.data().size())); appender.push(frame.token_.data().data(), frame.token_.data().size()); appender.writeBE(frame.lastReceivedServerPosition_); appender.writeBE(frame.clientPosition_); return queue.move(); } std::unique_ptr FrameSerializerV1_0::serializeOut( Frame_RESUME_OK&& frame) const { auto queue = createBufferQueue(kFrameHeaderSize + sizeof(int64_t)); folly::io::QueueAppender appender(&queue, /* do not grow */ 0); serializeHeaderInto(appender, frame.header_); appender.writeBE(frame.position_); return queue.move(); } bool FrameSerializerV1_0::deserializeFrom( Frame_REQUEST_STREAM& frame, std::unique_ptr in) const { return deserializeFromInternal(frame, std::move(in)); } bool FrameSerializerV1_0::deserializeFrom( Frame_REQUEST_CHANNEL& frame, std::unique_ptr in) const { return deserializeFromInternal(frame, std::move(in)); } bool FrameSerializerV1_0::deserializeFrom( Frame_REQUEST_RESPONSE& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_REQUEST_FNF& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_REQUEST_N& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); auto requestN = cur.readBE(); if (requestN <= 0) { throw std::runtime_error("invalid request n"); } frame.requestN_ = static_cast(requestN); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_METADATA_PUSH& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); // metadata takes the rest of the frame, just like data in other frames // that's why we use deserializeDataFrom frame.metadata_ = deserializeDataFrom(cur); } catch (...) { return false; } return frame.metadata_ != nullptr; } bool FrameSerializerV1_0::deserializeFrom( Frame_CANCEL& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_PAYLOAD& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_ERROR& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.errorCode_ = static_cast(cur.readBE()); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_KEEPALIVE& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); auto position = cur.readBE(); if (position < 0) { throw std::runtime_error("invalid value for position"); } frame.position_ = static_cast(position); frame.data_ = deserializeDataFrom(cur); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_SETUP& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.versionMajor_ = cur.readBE(); frame.versionMinor_ = cur.readBE(); auto keepaliveTime = cur.readBE(); if (keepaliveTime <= 0) { throw std::runtime_error("invalid keepalive time"); } frame.keepaliveTime_ = static_cast(keepaliveTime); auto maxLifetime = cur.readBE(); if (maxLifetime <= 0) { throw std::runtime_error("invalid maxLife time"); } frame.maxLifetime_ = static_cast(maxLifetime); if (!!(frame.header_.flags & FrameFlags::RESUME_ENABLE)) { auto resumeTokenSize = cur.readBE(); std::vector data(resumeTokenSize); cur.pull(data.data(), data.size()); frame.token_.set(std::move(data)); } else { frame.token_ = ResumeIdentificationToken(); } auto mdmtLen = cur.readBE(); frame.metadataMimeType_ = cur.readFixedString(mdmtLen); auto dmtLen = cur.readBE(); frame.dataMimeType_ = cur.readFixedString(dmtLen); frame.payload_ = deserializePayloadFrom(cur, frame.header_.flags); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_LEASE& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); auto ttl = cur.readBE(); if (ttl <= 0) { throw std::runtime_error("invalid ttl value"); } frame.ttl_ = static_cast(ttl); auto numberOfRequests = cur.readBE(); if (numberOfRequests <= 0) { throw std::runtime_error("invalid numberOfRequests value"); } frame.numberOfRequests_ = static_cast(numberOfRequests); frame.metadata_ = deserializeDataFrom(cur); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_RESUME& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); frame.versionMajor_ = cur.readBE(); frame.versionMinor_ = cur.readBE(); auto resumeTokenSize = cur.readBE(); std::vector data(resumeTokenSize); cur.pull(data.data(), data.size()); frame.token_.set(std::move(data)); auto lastReceivedServerPosition = cur.readBE(); if (lastReceivedServerPosition < 0) { throw std::runtime_error("invalid value for lastReceivedServerPosition"); } frame.lastReceivedServerPosition_ = static_cast(lastReceivedServerPosition); auto clientPosition = cur.readBE(); if (clientPosition < 0) { throw std::runtime_error("invalid value for clientPosition"); } frame.clientPosition_ = static_cast(clientPosition); } catch (...) { return false; } return true; } bool FrameSerializerV1_0::deserializeFrom( Frame_RESUME_OK& frame, std::unique_ptr in) const { folly::io::Cursor cur(in.get()); try { deserializeHeaderFrom(cur, frame.header_); auto position = cur.readBE(); if (position < 0) { throw std::runtime_error("invalid value for position"); } frame.position_ = static_cast(position); } catch (...) { return false; } return true; } ProtocolVersion FrameSerializerV1_0::detectProtocolVersion( const folly::IOBuf& firstFrame, size_t skipBytes) { // SETUP frame // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Stream ID = 0 | // +-----------+-+-+-+-+-----------+-------------------------------+ // |Frame Type |0|M|R|L| Flags | // +-----------+-+-+-+-+-----------+-------------------------------+ // | Major Version | Minor Version | // +-------------------------------+-------------------------------+ // ... // +-------------------------------+-------------------------------+ // RESUME frame // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | Stream ID = 0 | // +-----------+-+-+---------------+-------------------------------+ // |Frame Type |0|0| Flags | // +-------------------------------+-------------------------------+ // | Major Version | Minor Version | // +-------------------------------+-------------------------------+ // ... // +-------------------------------+-------------------------------+ folly::io::Cursor cur(&firstFrame); try { cur.skip(skipBytes); auto streamId = cur.readBE(); auto frameType = cur.readBE() >> 2; cur.skip(sizeof(uint8_t)); // flags auto majorVersion = cur.readBE(); auto minorVersion = cur.readBE(); constexpr static const auto kSETUP = 0x01; constexpr static const auto kRESUME = 0x0D; VLOG(4) << "frameType=" << frameType << "streamId=" << streamId << " majorVersion=" << majorVersion << " minorVersion=" << minorVersion; if (streamId == 0 && (frameType == kSETUP || frameType == kRESUME) && majorVersion == FrameSerializerV1_0::Version.major && minorVersion == FrameSerializerV1_0::Version.minor) { return FrameSerializerV1_0::Version; } } catch (...) { } return ProtocolVersion::Unknown; } size_t FrameSerializerV1_0::frameLengthFieldSize() const { return 3; // bytes } } // namespace rsocket