ReadRecordLayer::ReadResult ReadRecordLayer::readEvent()

in fizz/record/RecordLayer.cpp [18:102]


ReadRecordLayer::ReadResult<Param> ReadRecordLayer::readEvent(
    folly::IOBufQueue& socketBuf,
    Aead::AeadOptions options) {
  if (!unparsedHandshakeData_.empty()) {
    auto param = decodeHandshakeMessage(unparsedHandshakeData_);
    if (param) {
      VLOG(8) << "Received handshake message "
              << toString(EventVisitor()(*param));
      return ReadResult<Param>::from(std::move(param).value());
    }
  }

  while (true) {
    // Read one record. We read one record at a time since records could cause
    // a change in the record layer.
    auto messageResult = read(socketBuf, options);
    if (!messageResult) {
      return ReadResult<Param>::noneWithSizeHint(messageResult.sizeHint);
    }

    auto& message = messageResult.message;

    if (!unparsedHandshakeData_.empty() &&
        message->type != ContentType::handshake) {
      throw std::runtime_error("spliced handshake data");
    }

    switch (message->type) {
      case ContentType::alert: {
        auto alert = decode<Alert>(std::move(message->fragment));
        if (alert.description == AlertDescription::close_notify) {
          return ReadResult<Param>::from(Param(CloseNotify(socketBuf.move())));
        } else {
          return ReadResult<Param>::from(Param(std::move(alert)));
        }
      }
      case ContentType::handshake: {
        std::unique_ptr<folly::IOBuf> handshakeMessage =
            unparsedHandshakeData_.move();
        // It is possible that a peer might send us records in a manner such
        // that there is a 16KB record and only 1 byte of handshake message in
        // each record. Since we normally just trim the IOBuf, we would end up
        // holding 16K of data. To prevent this we allocate a contiguous
        // buffer to copy over these bytes. We supply kExtraAlloc bytes in
        // order to avoid needing to re-allocate a lot of times if we receive
        // a lot of small messages. There might be more optimal reallocation
        // policies, but this should be fine.
        message->fragment->coalesce();
        constexpr size_t kExtraAlloc = 1024;
        if (!handshakeMessage) {
          handshakeMessage =
              folly::IOBuf::create(message->fragment->length() + kExtraAlloc);
        } else if (handshakeMessage->tailroom() < message->fragment->length()) {
          // There might be remaining bytes from the previous handshake that are
          // left over in the unparsedHandshakeData_ buffer.
          handshakeMessage->unshare();
          handshakeMessage->reserve(
              0, message->fragment->length() + kExtraAlloc);
        }
        memcpy(
            handshakeMessage->writableTail(),
            message->fragment->data(),
            message->fragment->length());
        handshakeMessage->append(message->fragment->length());
        unparsedHandshakeData_.append(std::move(handshakeMessage));
        auto param = decodeHandshakeMessage(unparsedHandshakeData_);
        if (param) {
          VLOG(8) << "Received handshake message "
                  << toString(EventVisitor()(*param));
          return ReadResult<Param>::from(std::move(param).value());
        } else {
          // If we read handshake data but didn't have enough to get a full
          // message we immediately try to read another record.
          // TODO: add limits on number of records we buffer
          continue;
        }
      }
      case ContentType::application_data:
        return ReadResult<Param>::from(
            Param(AppData(std::move(message->fragment))));
      default:
        throw std::runtime_error("unknown content type");
    }
  }
}