mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 22:00:47 +01:00

This patch adds a StunDictionary. The dictionary has a reader and a writer. A writer can update a reader by creating a delta. The delta is applied by the reader, and the ack is applied by the writer. Using this mechanism, two ice agents can (in the future) communicate properties w/o manually needing to add new code. The delta and delta-ack attributes has been allocated at IANA. Bug: webrtc:15392 Change-Id: Icdbaf157004258b26ffa0c1f922e083b1ed23899 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/314901 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Jonas Oreland <jonaso@webrtc.org> Cr-Commit-Position: refs/heads/main@{#40513}
337 lines
10 KiB
C++
337 lines
10 KiB
C++
/*
|
|
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "p2p/base/stun_dictionary.h"
|
|
|
|
#include <string>
|
|
|
|
#include "rtc_base/gunit.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "test/gtest.h"
|
|
|
|
namespace {
|
|
|
|
void Sync(cricket::StunDictionaryView& dictionary,
|
|
cricket::StunDictionaryWriter& writer) {
|
|
int pending = writer.Pending();
|
|
auto delta = writer.CreateDelta();
|
|
if (delta == nullptr) {
|
|
EXPECT_EQ(pending, 0);
|
|
} else {
|
|
EXPECT_NE(pending, 0);
|
|
auto delta_ack = dictionary.ApplyDelta(*delta);
|
|
if (!delta_ack.ok()) {
|
|
RTC_LOG(LS_ERROR) << "delta_ack.error(): " << delta_ack.error().message();
|
|
}
|
|
EXPECT_TRUE(delta_ack.ok());
|
|
ASSERT_NE(delta_ack.value().first.get(), nullptr);
|
|
writer.ApplyDeltaAck(*delta_ack.value().first);
|
|
EXPECT_FALSE(writer.Pending());
|
|
}
|
|
}
|
|
|
|
void XorToggle(cricket::StunByteStringAttribute& attr, size_t byte) {
|
|
ASSERT_TRUE(attr.length() > byte);
|
|
uint8_t val = attr.GetByte(byte);
|
|
uint8_t new_val = val ^ (128 - (byte & 255));
|
|
attr.SetByte(byte, new_val);
|
|
}
|
|
|
|
std::unique_ptr<cricket::StunByteStringAttribute> Crop(
|
|
const cricket::StunByteStringAttribute& attr,
|
|
int new_length) {
|
|
auto new_attr =
|
|
std::make_unique<cricket::StunByteStringAttribute>(attr.type());
|
|
std::string content = std::string(attr.string_view());
|
|
content.erase(new_length);
|
|
new_attr->CopyBytes(content);
|
|
return new_attr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace cricket {
|
|
|
|
constexpr int kKey1 = 100;
|
|
|
|
TEST(StunDictionary, CreateEmptyDictionaryWriter) {
|
|
StunDictionaryView dictionary;
|
|
StunDictionaryWriter writer;
|
|
EXPECT_TRUE(dictionary.empty());
|
|
EXPECT_TRUE(writer->empty());
|
|
EXPECT_EQ(writer.Pending(), 0);
|
|
EXPECT_EQ(writer.CreateDelta().get(), nullptr);
|
|
}
|
|
|
|
TEST(StunDictionary, SetAndGet) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(writer->GetUInt64(kKey1), nullptr);
|
|
EXPECT_EQ(writer->GetByteString(kKey1), nullptr);
|
|
EXPECT_EQ(writer->GetAddress(kKey1), nullptr);
|
|
EXPECT_EQ(writer->GetUInt16List(kKey1), nullptr);
|
|
}
|
|
|
|
TEST(StunDictionary, SetAndApply) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
|
|
StunDictionaryView dictionary;
|
|
EXPECT_TRUE(dictionary.empty());
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 12);
|
|
}
|
|
|
|
TEST(StunDictionary, SetSetAndApply) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
writer.SetUInt32(kKey1)->SetValue(29);
|
|
|
|
StunDictionaryView dictionary;
|
|
EXPECT_TRUE(dictionary.empty());
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 29u);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 12);
|
|
}
|
|
|
|
TEST(StunDictionary, SetAndApplyAndSetAndApply) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
|
|
StunDictionaryView dictionary;
|
|
EXPECT_TRUE(dictionary.empty());
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 12);
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(29);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 29u);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 12);
|
|
}
|
|
|
|
TEST(StunDictionary, ChangeType) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u);
|
|
|
|
writer.SetUInt64(kKey1)->SetValue(29);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(writer->GetUInt64(kKey1)->value(), 29ull);
|
|
}
|
|
|
|
TEST(StunDictionary, ChangeTypeApply) {
|
|
StunDictionaryWriter writer;
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u);
|
|
|
|
StunDictionaryView dictionary;
|
|
EXPECT_TRUE(dictionary.empty());
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u);
|
|
|
|
writer.SetUInt64(kKey1)->SetValue(29);
|
|
EXPECT_EQ(writer->GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(writer->GetUInt64(kKey1)->value(), 29ull);
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1)->value(), 29ull);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 16);
|
|
}
|
|
|
|
TEST(StunDictionary, Pending) {
|
|
StunDictionaryWriter writer;
|
|
EXPECT_EQ(writer.Pending(), 0);
|
|
EXPECT_FALSE(writer.Pending(kKey1));
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
EXPECT_EQ(writer.Pending(), 1);
|
|
EXPECT_TRUE(writer.Pending(kKey1));
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(29);
|
|
EXPECT_EQ(writer.Pending(), 1);
|
|
EXPECT_TRUE(writer.Pending(kKey1));
|
|
|
|
writer.SetUInt32(kKey1 + 1)->SetValue(31);
|
|
EXPECT_EQ(writer.Pending(), 2);
|
|
EXPECT_TRUE(writer.Pending(kKey1));
|
|
EXPECT_TRUE(writer.Pending(kKey1 + 1));
|
|
|
|
StunDictionaryView dictionary;
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(writer.Pending(), 0);
|
|
EXPECT_FALSE(writer.Pending(kKey1));
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(32);
|
|
EXPECT_EQ(writer.Pending(), 1);
|
|
EXPECT_TRUE(writer.Pending(kKey1));
|
|
}
|
|
|
|
TEST(StunDictionary, Delete) {
|
|
StunDictionaryWriter writer;
|
|
StunDictionaryView dictionary;
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 12);
|
|
|
|
writer.Delete(kKey1);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 8);
|
|
|
|
writer.Delete(kKey1);
|
|
EXPECT_EQ(writer.Pending(), 0);
|
|
}
|
|
|
|
TEST(StunDictionary, MultiWriter) {
|
|
StunDictionaryWriter writer1;
|
|
StunDictionaryWriter writer2;
|
|
StunDictionaryView dictionary;
|
|
|
|
writer1.SetUInt32(kKey1)->SetValue(27);
|
|
Sync(dictionary, writer1);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
|
|
writer2.SetUInt32(kKey1 + 1)->SetValue(28);
|
|
Sync(dictionary, writer2);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1 + 1)->value(), 28u);
|
|
|
|
writer1.Delete(kKey1);
|
|
Sync(dictionary, writer1);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr);
|
|
|
|
writer2.Delete(kKey1 + 1);
|
|
Sync(dictionary, writer2);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1 + 1), nullptr);
|
|
}
|
|
|
|
TEST(StunDictionary, BytesStoredIsCountedCorrectlyAfterMultipleUpdates) {
|
|
StunDictionaryWriter writer;
|
|
StunDictionaryView dictionary;
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
writer.SetUInt64(kKey1 + 1)->SetValue(28);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 28);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull);
|
|
writer.Delete(kKey1);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 24);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull);
|
|
writer.Delete(kKey1 + 1);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 16);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1), nullptr);
|
|
}
|
|
}
|
|
|
|
TEST(StunDictionary, MaxBytesStoredCausesErrorOnOverflow) {
|
|
StunDictionaryWriter writer;
|
|
StunDictionaryView dictionary;
|
|
|
|
dictionary.set_max_bytes_stored(30);
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
writer.SetUInt64(kKey1 + 1)->SetValue(28);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.bytes_stored(), 28);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull);
|
|
|
|
writer.SetByteString(kKey1 + 2)->CopyBytes("k");
|
|
{
|
|
auto delta = writer.CreateDelta();
|
|
auto delta_ack = dictionary.ApplyDelta(*delta);
|
|
EXPECT_FALSE(delta_ack.ok());
|
|
}
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull);
|
|
EXPECT_EQ(dictionary.GetByteString(kKey1 + 2), nullptr);
|
|
|
|
writer.Delete(kKey1 + 1);
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1), nullptr);
|
|
EXPECT_EQ(dictionary.GetByteString(kKey1 + 2)->string_view(), "k");
|
|
}
|
|
|
|
TEST(StunDictionary, DataTypes) {
|
|
StunDictionaryWriter writer;
|
|
StunDictionaryView dictionary;
|
|
|
|
rtc::SocketAddress addr("127.0.0.1", 8080);
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
writer.SetUInt64(kKey1 + 1)->SetValue(28);
|
|
writer.SetAddress(kKey1 + 2)->SetAddress(addr);
|
|
writer.SetByteString(kKey1 + 3)->CopyBytes("keso");
|
|
writer.SetUInt16List(kKey1 + 4)->AddTypeAtIndex(0, 7);
|
|
|
|
Sync(dictionary, writer);
|
|
EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u);
|
|
EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull);
|
|
EXPECT_EQ(dictionary.GetAddress(kKey1 + 2)->GetAddress(), addr);
|
|
EXPECT_EQ(dictionary.GetByteString(kKey1 + 3)->string_view(), "keso");
|
|
EXPECT_EQ(dictionary.GetUInt16List(kKey1 + 4)->GetType(0), 7);
|
|
}
|
|
|
|
TEST(StunDictionary, ParseError) {
|
|
StunDictionaryWriter writer;
|
|
StunDictionaryView dictionary;
|
|
|
|
rtc::SocketAddress addr("127.0.0.1", 8080);
|
|
|
|
writer.SetUInt32(kKey1)->SetValue(27);
|
|
writer.SetUInt64(kKey1 + 1)->SetValue(28);
|
|
writer.SetAddress(kKey1 + 2)->SetAddress(addr);
|
|
writer.SetByteString(kKey1 + 3)->CopyBytes("keso");
|
|
writer.SetUInt16List(kKey1 + 4)->AddTypeAtIndex(0, 7);
|
|
|
|
auto delta = writer.CreateDelta();
|
|
|
|
// The first 10 bytes are in the header...
|
|
// any modification makes parsing fail.
|
|
for (int i = 0; i < 10; i++) {
|
|
XorToggle(*delta, i);
|
|
EXPECT_FALSE(dictionary.ApplyDelta(*delta).ok());
|
|
XorToggle(*delta, i); // toogle back
|
|
}
|
|
|
|
// Remove bytes from the delta.
|
|
for (size_t i = 0; i < delta->length(); i++) {
|
|
// The delta does not contain a footer,
|
|
// so it it possible to Crop at special values (attribute boundaries)
|
|
// and apply will still work.
|
|
const std::vector<int> valid_crop_length = {18, 28, 42, 56, 66, 74};
|
|
bool valid = std::find(valid_crop_length.begin(), valid_crop_length.end(),
|
|
i) != valid_crop_length.end();
|
|
auto cropped_delta = Crop(*delta, i);
|
|
if (valid) {
|
|
EXPECT_TRUE(dictionary.ApplyDelta(*cropped_delta).ok());
|
|
} else {
|
|
EXPECT_FALSE(dictionary.ApplyDelta(*cropped_delta).ok());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace cricket
|