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");
}
}
}