Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore: rework RPC version to match new header spec#8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
deansheather merged 2 commits intomainfromdean/rpc-version
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 0 additions & 36 deletionsTests/Vpn.Proto/ApiVersionTest.cs
View file
Open in desktop

This file was deleted.

14 changes: 7 additions & 7 deletionsTests/Vpn.Proto/RpcHeaderTest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,17 +9,17 @@ public class RpcHeaderTest
[Test(Description = "Parse and use some valid header strings")]
public void Valid()
{
var headerStr = "codervpn 2.1 manager";
var headerStr = "codervpnmanager 1.3,2.1";
var header = RpcHeader.Parse(headerStr);
Assert.That(header.Role.ToString(), Is.EqualTo(RpcRole.Manager));
Assert.That(header.Version, Is.EqualTo(newApiVersion(2, 1)));
Assert.That(header.VersionList, Is.EqualTo(newRpcVersionList(new RpcVersion(1, 3), new RpcVersion(2, 1))));
Assert.That(header.ToString(), Is.EqualTo(headerStr + "\n"));
Assert.That(header.ToBytes().ToArray(), Is.EqualTo(Encoding.UTF8.GetBytes(headerStr + "\n")));

headerStr = "codervpn 1.0 tunnel";
headerStr = "codervpntunnel1.0";
header = RpcHeader.Parse(headerStr);
Assert.That(header.Role.ToString(), Is.EqualTo(RpcRole.Tunnel));
Assert.That(header.Version, Is.EqualTo(newApiVersion(1, 0)));
Assert.That(header.VersionList, Is.EqualTo(newRpcVersionList(new RpcVersion(1, 0))));
Assert.That(header.ToString(), Is.EqualTo(headerStr + "\n"));
Assert.That(header.ToBytes().ToArray(), Is.EqualTo(Encoding.UTF8.GetBytes(headerStr + "\n")));
}
Expand All@@ -29,13 +29,13 @@ public void ParseInvalid()
{
var ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpn"));
Assert.That(ex.Message, Does.Contain("Wrong number of parts"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpn1.0manager cats"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpn manager cats 1.0"));
Assert.That(ex.Message, Does.Contain("Wrong number of parts"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpn 1.0"));
Assert.That(ex.Message, Does.Contain("Wrong number of parts"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("cats 1.0 manager"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("catsmanager1.0"));
Assert.That(ex.Message, Does.Contain("Invalid preamble"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpn 1.0 cats"));
ex = Assert.Throws<ArgumentException>(() => RpcHeader.Parse("codervpncats1.0"));
Assert.That(ex.Message, Does.Contain("Unknown role 'cats'"));
}
}
101 changes: 101 additions & 0 deletionsTests/Vpn.Proto/RpcVersionTest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
using Coder.Desktop.Vpn.Proto;
using NUnit.Framework.Constraints;

namespace Coder.Desktop.Tests.Vpn.Proto;

[TestFixture]
public class RpcVersionTest
{
[Test(Description = "Parse a variety of version strings")]
public void Parse()
{
Assert.That(RpcVersion.Parse("2.1"), Is.EqualTo(new RpcVersion(2, 1)));
Assert.That(RpcVersion.Parse("1.0"), Is.EqualTo(new RpcVersion(1, 0)));

Assert.Throws<ArgumentException>(() => RpcVersion.Parse("cats"));
Assert.Throws<ArgumentException>(() => RpcVersion.Parse("cats.dogs"));
Assert.Throws<ArgumentException>(() => RpcVersion.Parse("1.dogs"));
Assert.Throws<ArgumentException>(() => RpcVersion.Parse("1.0.1"));
Assert.Throws<ArgumentException>(() => RpcVersion.Parse("11"));
}

private void IsCompatibleWithBothWays(RpcVersion a, RpcVersion b, IResolveConstraint c)
{
Assert.That(a.IsCompatibleWith(b), c);
Assert.That(b.IsCompatibleWith(a), c);
}

[Test(Description = "Test that versions are compatible")]
public void IsCompatibleWith()
{
var twoOne = new RpcVersion(2, 1);
Assert.That(twoOne.IsCompatibleWith(twoOne), Is.EqualTo(twoOne));

// 2.1 && 2.2 => 2.1
IsCompatibleWithBothWays(twoOne, new RpcVersion(2, 2), Is.EqualTo(new RpcVersion(2, 1)));
// 2.1 && 2.0 => 2.0
IsCompatibleWithBothWays(twoOne, new RpcVersion(2, 0), Is.EqualTo(new RpcVersion(2, 0)));
// 2.1 && 3.1 => null
IsCompatibleWithBothWays(twoOne, new RpcVersion(3, 1), Is.Null);
// 2.1 && 1.1 => null
IsCompatibleWithBothWays(twoOne, new RpcVersion(1, 1), Is.Null);
}
}

[TestFixture]
public class RpcVersionListTest
{
[Test(Description = "Parse a variety of version list strings")]
public void Parse()
{
Assert.That(RpcVersionList.Parse("1.0"), Is.EqualTo(new RpcVersionList(new RpcVersion(1, 0))));
Assert.That(RpcVersionList.Parse("1.3,2.1"),
Is.EqualTo(new RpcVersionList(new RpcVersion(1, 3), new RpcVersion(2, 1))));

var ex = Assert.Throws<ArgumentException>(() => RpcVersionList.Parse("0.1"));
Assert.That(ex.InnerException, Is.Not.Null);
Assert.That(ex.InnerException.Message, Does.Contain("Invalid major version"));
ex = Assert.Throws<ArgumentException>(() => RpcVersionList.Parse(""));
Assert.That(ex.InnerException, Is.Not.Null);
Assert.That(ex.InnerException.Message, Does.Contain("Invalid version string"));
ex = Assert.Throws<ArgumentException>(() => RpcVersionList.Parse("2.1,1.1"));
Assert.That(ex.InnerException, Is.Not.Null);
Assert.That(ex.InnerException.Message, Does.Contain("sorted"));
ex = Assert.Throws<ArgumentException>(() => RpcVersionList.Parse("1.1,1.2"));
Assert.That(ex.InnerException, Is.Not.Null);
Assert.That(ex.InnerException.Message, Does.Contain("Duplicate major version"));
}

[Test(Description = "Validate a variety of version lists to test every error")]
public void Validate()
{
Assert.DoesNotThrow(() =>
new RpcVersionList(new RpcVersion(1, 3), new RpcVersion(2, 4), new RpcVersion(3, 0)).Validate());

var ex = Assert.Throws<ArgumentException>(() => new RpcVersionList(new RpcVersion(0, 1)).Validate());
Assert.That(ex.Message, Does.Contain("Invalid major version"));
ex = Assert.Throws<ArgumentException>(() =>
new RpcVersionList(new RpcVersion(2, 1), new RpcVersion(1, 2)).Validate());
Assert.That(ex.Message, Does.Contain("sorted"));
ex = Assert.Throws<ArgumentException>(() =>
new RpcVersionList(new RpcVersion(1, 1), new RpcVersion(1, 2)).Validate());
Assert.That(ex.Message, Does.Contain("Duplicate major version"));
}

private void IsCompatibleWithBothWays(RpcVersionList a, RpcVersionList b, IResolveConstraint c)
{
Assert.That(a.IsCompatibleWith(b), c);
Assert.That(b.IsCompatibleWith(a), c);
}

[Test(Description = "Check a variety of lists against each other to determine compatible version")]
public void IsCompatibleWith()
{
var list1 = RpcVersionList.Parse("1.2,2.4,3.2");
Assert.That(list1.IsCompatibleWith(list1), Is.EqualTo(new RpcVersion(3, 2)));

IsCompatibleWithBothWays(list1, RpcVersionList.Parse("4.1,5.2"), Is.Null);
IsCompatibleWithBothWays(list1, RpcVersionList.Parse("1.2,2.3"), Is.EqualTo(new RpcVersion(2, 3)));
IsCompatibleWithBothWays(list1, RpcVersionList.Parse("2.3,3.3"), Is.EqualTo(new RpcVersion(3, 2)));
}
}
40 changes: 40 additions & 0 deletionsTests/Vpn/SpeakerTest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
usingSystem.Buffers;
usingSystem.IO.Pipelines;
usingSystem.Reflection;
usingSystem.Text;
usingSystem.Threading.Channels;
usingCoder.Desktop.Vpn;
usingCoder.Desktop.Vpn.Proto;
Expand DownExpand Up@@ -277,6 +278,45 @@ public async Task ReadLargeHeader()
Assert.That(gotEx.Message,Does.Contain("Header malformed or too large"));
}

[Test(Description="Receive an invalid header")]
[Timeout(30_000)]
publicasyncTaskReceiveInvalidHeader()
{
varcases=newDictionary<string,(string,string?)>
{
{"invalid\n",("Failed to parse peer header","Wrong number of parts in header string")},
{"cats tunnel 1.0\n",("Failed to parse peer header","Invalid preamble in header string")},
{"codervpn cats 1.0\n",("Failed to parse peer header","Unknown role 'cats'")},
{"codervpn manager 1.0\n",("Expected peer role 'tunnel' but got 'manager'",null)},
{
"codervpn tunnel 1000.1\n",
($"No RPC versions are compatible: local={RpcVersionList.Current}, remote=1000.1",null)
},
{"codervpn tunnel 0.1\n",("Failed to parse peer header","Invalid version list '0.1'")},
{"codervpn tunnel 1.0,1.2\n",("Failed to parse peer header","Invalid version list '1.0,1.2'")},
{"codervpn tunnel 2.0,3.1,1.2\n",("Failed to parse peer header","Invalid version list '2.0,3.1,1.2'")},
};

foreach(var(header,(expectedOuter,expectedInner))incases)
{
var(stream1,stream2)=BidirectionalPipe.New();
awaitusingvarspeaker1=newSpeaker<ManagerMessage,TunnelMessage>(stream1);

awaitstream2.WriteAsync(Encoding.UTF8.GetBytes(header));

vargotEx=Assert.CatchAsync(()=>speaker1.StartAsync(),$"header: '{header}'");
Assert.That(gotEx.Message,Does.Contain(expectedOuter),$"header: '{header}'");
if(expectedInnerisnull)
{
Assert.That(gotEx.InnerException,Is.Null,$"header: '{header}'");
continue;
}

Assert.That(gotEx.InnerException,Is.Not.Null,$"header: '{header}'");
Assert.That(gotEx.InnerException!.Message,Does.Contain(expectedInner),$"header: '{header}'");
}
}

[Test(Description="Encounter a write error during message send")]
[Timeout(30_000)]
publicasyncTaskSendMessageWriteError()
Expand Down
103 changes: 0 additions & 103 deletionsVpn.Proto/ApiVersion.cs
View file
Open in desktop

This file was deleted.

14 changes: 7 additions & 7 deletionsVpn.Proto/RpcHeader.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,13 +6,13 @@ namespace Coder.Desktop.Vpn.Proto;
/// A header to write or read from a stream to identify the speaker's role and version.
/// </summary>
/// <param name="role">Role of the speaker</param>
/// <param name="version">Version of the speaker</param>
public class RpcHeader(RpcRole role,ApiVersion version)
/// <param name="versionList">Version of the speaker</param>
public class RpcHeader(RpcRole role,RpcVersionList versionList)
{
private const string Preamble = "codervpn";

public RpcRole Role { get; } = role;
publicApiVersion Version { get; } =version;
publicRpcVersionList VersionList { get; } =versionList;

/// <summary>
/// Parse a header string into a <c>SpeakerHeader</c>.
Expand All@@ -26,17 +26,17 @@ public static RpcHeader Parse(string header)
if (parts.Length != 3) throw new ArgumentException($"Wrong number of parts in header string '{header}'");
if (parts[0] != Preamble) throw new ArgumentException($"Invalid preamble in header string '{header}'");

varversion =ApiVersion.Parse(parts[1]);
varrole =new RpcRole(parts[2]);
return new RpcHeader(role,version);
varrole =new RpcRole(parts[1]);
varversionList =RpcVersionList.Parse(parts[2]);
return new RpcHeader(role,versionList);
}

/// <summary>
/// Construct a header string from the role and version with a trailing newline.
/// </summary>
public override string ToString()
{
return $"{Preamble} {Version} {Role}\n";
return $"{Preamble} {Role} {VersionList}\n";
}

public ReadOnlyMemory<byte> ToBytes()
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp