diff --git a/Build/ReleaseNotes.md b/Build/ReleaseNotes.md
index 8d53b3a..e03c494 100644
--- a/Build/ReleaseNotes.md
+++ b/Build/ReleaseNotes.md
@@ -1,4 +1,3 @@
-[Core] Fixed null ref exception in TCP transport layer.
-[Core] Added support for .NET 6.0.
-[Core] Assemblies are now signed (strong name).
-[Core] Updated nuget packages.
\ No newline at end of file
+[Core] Improved support for .NET 6.0+ (Passing cancellation tokens etc.).
+[Client] Both clients are not thread safe.
+[Client] Fixed an issue in internal buffer management which may lead to payload corruption.
\ No newline at end of file
diff --git a/Source/CoAP.TestClient/Program.cs b/Source/CoAP.TestClient/Program.cs
index 3699b03..8eff9d3 100644
--- a/Source/CoAP.TestClient/Program.cs
+++ b/Source/CoAP.TestClient/Program.cs
@@ -1,273 +1,321 @@
-using CoAPnet;
-using CoAPnet.Client;
-using CoAPnet.Extensions.DTLS;
-using CoAPnet.Logging;
-using System;
+using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using CoAPnet;
+using CoAPnet.Client;
+using CoAPnet.Extensions.DTLS;
+using CoAPnet.Logging;
-namespace CoAP.TestClient
+namespace CoAP.TestClient;
+
+static class Program
{
- static class Program
+ static async Task Main()
{
- static async Task Main22()
- {
- var coapFactory = new CoapFactory();
- coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
+ await ObserveTradfriLamp();
+ //await SendRequestsToCoapMe();
+ }
+
+ static async Task RequestInParallelTasks()
+ {
+ var coapFactory = new CoapFactory();
+ coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
- using var coapClient = coapFactory.CreateClient();
+ const int TaskCount = 100;
+ using (var coapClient = coapFactory.CreateClient())
+ {
Console.WriteLine("< CONNECTING...");
var connectOptions = new CoapClientConnectOptionsBuilder()
- .WithHost("GW-B8D7AF2B3EA3.fritz.box")
- //.WithHost("127.0.0.1")
- .WithDtlsTransportLayer(o =>
- o.WithPreSharedKey("Client_identity", "7x3A1gqWvu9cBGD7"))
+ .WithHost("coap.me")
.Build();
- using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
- {
- await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token);
- }
+ await coapClient.ConnectAsync(connectOptions, CancellationToken.None).ConfigureAwait(false);
- var request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("15001")
- .Build();
+ var tasks = new Task[TaskCount];
- using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
+ for (var i = 0; i < TaskCount; i++)
{
- var response = await coapClient.RequestAsync(request, cancellationTokenSource.Token);
- PrintResponse(response);
+ tasks[i] = Task.Run(async () =>
+ {
+ var request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("separate")
+ .Build();
+
+ var response = await coapClient.RequestAsync(request, CancellationToken.None);
+ //PrintResponse(response);
+
+ Console.WriteLine("Received response.");
+ });
}
+
+ await Task.WhenAll(tasks);
}
+ }
- static async Task MainPsk()
- {
- // Generate new PSK Token.
-
- var coapFactory = new CoapFactory();
- coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
+ static async Task GetStatusFromTradfriGateway()
+ {
+ var coapFactory = new CoapFactory();
+ coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
- using (var coapClient = coapFactory.CreateClient())
- {
- Console.WriteLine("< CONNECTING...");
+ using var coapClient = coapFactory.CreateClient();
- var connectOptions = new CoapClientConnectOptionsBuilder()
- .WithHost("GW-B8D7AF2B3EA3.fritz.box")
- .WithDtlsTransportLayer(o =>
- o.WithPreSharedKey("Client_identity", File.ReadAllText(@"D:\SourceCode\Wirehome.Private\Tradfri\Key.txt")))
- .Build();
+ Console.WriteLine("< CONNECTING...");
- using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
- {
- await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token).ConfigureAwait(false);
- }
+ var connectOptions = new CoapClientConnectOptionsBuilder()
+ .WithHost("GW-B8D7AF2B3EA3.fritz.box")
+ //.WithHost("127.0.0.1")
+ .WithDtlsTransportLayer(o =>
+ o.WithPreSharedKey("Client_identity", "7x3A1gqWvu9cBGD7"))
+ .Build();
- var request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Post)
- .WithPath("15011/9063")
- .WithPayload("{\"9090\":\"WH\"}")
- .Build();
+ using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
+ {
+ await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token);
+ }
- var response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
- PrintResponse(response);
- }
+ var request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("15001")
+ .Build();
+
+ using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
+ {
+ var response = await coapClient.RequestAsync(request, cancellationTokenSource.Token);
+ PrintResponse(response);
}
+ }
+
+ static async Task GenerateTradfriPskToken()
+ {
+ // Generate new PSK Token.
- static async Task Main()
+ var coapFactory = new CoapFactory();
+ coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
+
+ using (var coapClient = coapFactory.CreateClient())
{
- var coapFactory = new CoapFactory();
- coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
+ Console.WriteLine("< CONNECTING...");
+
+ var connectOptions = new CoapClientConnectOptionsBuilder()
+ .WithHost("GW-B8D7AF2B3EA3.fritz.box")
+ .WithDtlsTransportLayer(o =>
+ o.WithPreSharedKey("Client_identity",
+ File.ReadAllText(@"D:\SourceCode\Wirehome.Private\Tradfri\Key.txt")))
+ .Build();
- using (var coapClient = coapFactory.CreateClient())
+ using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
- Console.WriteLine("< CONNECTING...");
+ await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token).ConfigureAwait(false);
+ }
- var connectOptions = new CoapClientConnectOptionsBuilder()
- .WithHost("GW-B8D7AF2B3EA3.fritz.box")
- .WithDtlsTransportLayer(o =>
- o.WithPreSharedKey("WH", "UP3ThsT7ineCsKoc"))
- .Build();
+ var request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Post)
+ .WithPath("15011/9063")
+ .WithPayload("{\"9090\":\"WH\"}")
+ .Build();
- using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
- {
- await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token).ConfigureAwait(false);
- }
+ var response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
+ PrintResponse(response);
+ }
+ }
+
+ static void PrintResponse(CoapResponse response)
+ {
+ Console.WriteLine("> RESPONSE");
+ Console.WriteLine(" + Status = " + response.StatusCode);
+ Console.WriteLine(" + Status code = " + (int)response.StatusCode);
+ Console.WriteLine(" + Content format = " + response.Options.ContentFormat);
+ Console.WriteLine(" + Max age = " + response.Options.MaxAge);
+ Console.WriteLine(" + E tag = " + ByteArrayToString(response.Options.ETag));
+ Console.WriteLine(" + Payload = " + Encoding.UTF8.GetString(response.Payload));
+ Console.WriteLine();
+ }
+
+ static string ByteArrayToString(byte[] buffer)
+ {
+ if (buffer == null || buffer.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ var hex = new StringBuilder(buffer.Length * 2);
+ hex.Append("0x");
+
+ foreach (var @byte in buffer)
+ {
+ hex.AppendFormat("{0:x2}", @byte);
+ }
- var request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("15001")
- .Build();
+ return hex.ToString();
+ }
- var response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
- PrintResponse(response);
+ static async Task SendRequestsToCoapMe()
+ {
+ var coapFactory = new CoapFactory();
+ coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("15001/65550")
- .Build();
+ using (var coapClient = coapFactory.CreateClient())
+ {
+ Console.WriteLine("< CONNECTING...");
- response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
- PrintResponse(response);
+ var connectOptions = new CoapClientConnectOptionsBuilder()
+ .WithHost("coap.me")
+ .Build();
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Put)
- .WithPath("15001/65550")
- .WithPayload("{\"3311\": [{\"5850\": 1}]}")
- .Build();
+ await coapClient.ConnectAsync(connectOptions, CancellationToken.None).ConfigureAwait(false);
- response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
- PrintResponse(response);
+ // separate
- var observeOptions = new CoapObserveOptionsBuilder()
- .WithPath("15001/65550")
- .WithResponseHandler(new ResponseHandler())
- .Build();
+ var request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("separate")
+ .Build();
- var observeResponse = await coapClient.ObserveAsync(observeOptions, CancellationToken.None).ConfigureAwait(false);
- PrintResponse(observeResponse.Response);
+ var response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- Console.WriteLine("Observed messages for lamp!");
+ // hello
- Console.WriteLine("Press any key to unobserve.");
- Console.ReadLine();
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("hello")
+ .Build();
- await coapClient.StopObservationAsync(observeResponse, CancellationToken.None).ConfigureAwait(false);
- }
- }
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- class ResponseHandler : ICoapResponseHandler
- {
- public Task HandleResponseAsync(HandleResponseContext context)
- {
- Console.WriteLine("> RECEIVED OBSERVED RESOURCE");
- Console.WriteLine(" + Sequence number = " + context.SequenceNumber);
- PrintResponse(context.Response);
- return Task.CompletedTask;
- }
- }
+ // broken
- public static void PrintResponse(CoapResponse response)
- {
- Console.WriteLine("> RESPONSE");
- Console.WriteLine(" + Status = " + response.StatusCode);
- Console.WriteLine(" + Status code = " + (int) response.StatusCode);
- Console.WriteLine(" + Content format = " + response.Options.ContentFormat);
- Console.WriteLine(" + Max age = " + response.Options.MaxAge);
- Console.WriteLine(" + E tag = " + ByteArrayToString(response.Options.ETag));
- Console.WriteLine(" + Payload = " + Encoding.UTF8.GetString(response.Payload));
- Console.WriteLine();
- }
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("broken")
+ .Build();
- static string ByteArrayToString(byte[] buffer)
- {
- if (buffer == null || buffer.Length == 0)
- {
- return string.Empty;
- }
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- var hex = new StringBuilder(buffer.Length * 2);
- hex.Append("0x");
+ // secret
- foreach (var @byte in buffer)
- {
- hex.AppendFormat("{0:x2}", @byte);
- }
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("secret")
+ .Build();
- return hex.ToString();
- }
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- static async Task Main9()
- {
- var coapFactory = new CoapFactory();
- coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
+ // large-create
- using (var coapClient = coapFactory.CreateClient())
- {
- Console.WriteLine("< CONNECTING...");
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("large-create")
+ .Build();
- var connectOptions = new CoapClientConnectOptionsBuilder()
- .WithHost("coap.me")
- .Build();
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- await coapClient.ConnectAsync(connectOptions, CancellationToken.None).ConfigureAwait(false);
+ // location1/location2/location3
- // separate
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("location1/location2/location3")
+ .Build();
- var request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("separate")
- .Build();
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- var response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ // large
- // hello
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("large")
+ .Build();
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("hello")
- .Build();
+ response = await coapClient.RequestAsync(request, CancellationToken.None);
+ PrintResponse(response);
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ await Task.Delay(TimeSpan.FromSeconds(10));
+ }
+ }
- // broken
+ static async Task ObserveTradfriLamp()
+ {
+ var coapFactory = new CoapFactory();
+ coapFactory.DefaultLogger.RegisterSink(new CoapNetLoggerConsoleSink());
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("broken")
- .Build();
+ using (var coapClient = coapFactory.CreateClient())
+ {
+ Console.WriteLine("< CONNECTING...");
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ var connectOptions = new CoapClientConnectOptionsBuilder()
+ .WithHost("GW-B8D7AF2B3EA3.fritz.box")
+ .WithDtlsTransportLayer(o =>
+ o.WithPreSharedKey("WH", "UP3ThsT7ineCsKoc"))
+ .Build();
- // secret
+ using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
+ {
+ await coapClient.ConnectAsync(connectOptions, cancellationTokenSource.Token).ConfigureAwait(false);
+ }
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("secret")
- .Build();
+ var request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("15001")
+ .Build();
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ var response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
+ PrintResponse(response);
- // large-create
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Get)
+ .WithPath("15001/65557")
+ .Build();
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("large-create")
- .Build();
+ response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
+ PrintResponse(response);
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ request = new CoapRequestBuilder()
+ .WithMethod(CoapRequestMethod.Put)
+ .WithPath("15001/65557")
+ .WithPayload("{\"3311\": [{\"5850\": 1}]}")
+ .Build();
- // location1/location2/location3
+ response = await coapClient.RequestAsync(request, CancellationToken.None).ConfigureAwait(false);
+ PrintResponse(response);
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("location1/location2/location3")
- .Build();
+ var observeOptions = new CoapObserveOptionsBuilder()
+ .WithPath("15001/65557")
+ .WithResponseHandler(new ResponseHandler())
+ .Build();
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ var observeResponse =
+ await coapClient.ObserveAsync(observeOptions, CancellationToken.None).ConfigureAwait(false);
+
+ PrintResponse(observeResponse.Response);
- // large
+ Console.WriteLine("Observed messages for lamp!");
- request = new CoapRequestBuilder()
- .WithMethod(CoapRequestMethod.Get)
- .WithPath("large")
- .Build();
+ Console.WriteLine("Press any key to unobserve.");
+ Console.ReadLine();
- response = await coapClient.RequestAsync(request, CancellationToken.None);
- PrintResponse(response);
+ await coapClient.StopObservationAsync(observeResponse, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
- await Task.Delay(TimeSpan.FromSeconds(10));
- }
+ class ResponseHandler :
+ ICoapResponseHandler
+ {
+ public Task HandleResponseAsync(HandleResponseContext context)
+ {
+ Console.WriteLine("> RECEIVED OBSERVED RESOURCE");
+ Console.WriteLine(" + Sequence number = " + context.SequenceNumber);
+ PrintResponse(context.Response);
+ return Task.CompletedTask;
}
}
}
\ No newline at end of file
diff --git a/Source/CoAPnet.Extensions.DTLS/CoAPnet.Extensions.DTLS.csproj b/Source/CoAPnet.Extensions.DTLS/CoAPnet.Extensions.DTLS.csproj
index dce7492..c29f5ad 100644
--- a/Source/CoAPnet.Extensions.DTLS/CoAPnet.Extensions.DTLS.csproj
+++ b/Source/CoAPnet.Extensions.DTLS/CoAPnet.Extensions.DTLS.csproj
@@ -28,8 +28,10 @@
true
true
LICENSE
-
+ 7.3
For release notes please go to CoAPnet release notes (https://www.nuget.org/packages/CoAPnet/).
+ true
+ CS1591
diff --git a/Source/CoAPnet.Tests/CoapClient_Tests.cs b/Source/CoAPnet.Tests/CoapClient_Tests.cs
index 4adfd44..a2e2c1a 100644
--- a/Source/CoAPnet.Tests/CoapClient_Tests.cs
+++ b/Source/CoAPnet.Tests/CoapClient_Tests.cs
@@ -49,7 +49,7 @@ public async Task Timeout_When_No_Response_Received()
using (var coapClient = new CoapFactory().CreateClient())
{
var options = new CoapClientConnectOptionsBuilder()
- .WithHost("127.0.0.1")
+ .WithHost("123.123.123.1")
.WithPort(5555)
.Build();
diff --git a/Source/CoAPnet.Tests/CoapMessageDecoder_Tests.cs b/Source/CoAPnet.Tests/CoapMessageDecoder_Tests.cs
index 1ae8d2a..14d0a06 100644
--- a/Source/CoAPnet.Tests/CoapMessageDecoder_Tests.cs
+++ b/Source/CoAPnet.Tests/CoapMessageDecoder_Tests.cs
@@ -1,4 +1,5 @@
-using CoAPnet.Logging;
+using System;
+using CoAPnet.Logging;
using CoAPnet.Protocol;
using CoAPnet.Protocol.Encoding;
using CoAPnet.Protocol.Options;
@@ -9,7 +10,7 @@
namespace CoAPnet.Tests
{
[TestClass]
- public class CoapMessageDecoder_Tests
+ public sealed class CoapMessageDecoder_Tests
{
[TestMethod]
public void Encode_And_Decode_Full()
@@ -22,15 +23,16 @@ public void Encode_And_Decode_Full()
Code = CoapMessageCodes.Post,
Id = 0x5876,
Token = new byte[] { 1, 2, 3, 4 },
- Payload = Encoding.UTF8.GetBytes("payloadOver13chars")
+ Payload = Encoding.UTF8.GetBytes("payloadOver13chars"),
+ Options = new List
+ {
+ optionBuilder.CreateETag(new byte[12]),
+ optionBuilder.CreateUriPort(5648),
+ optionBuilder.CreateContentFormat(CoapMessageContentFormat.ApplicationJson)
+ }
};
- message.Options = new List
- {
- optionBuilder.CreateUriPort(5648)
- };
-
- Enocde_And_Decode_Internal(message);
+ EncodeAndDecodeInternal(message);
}
[TestMethod]
@@ -43,15 +45,15 @@ public void Encode_And_Decode_Payload_Length_12()
Type = CoapMessageType.Confirmable,
Code = CoapMessageCodes.Put,
Id = ushort.MaxValue,
- Payload = Encoding.UTF8.GetBytes("123456789012")
- };
-
- message.Options = new List
- {
- optionBuilder.CreateUriPort(5648)
+ Payload = Encoding.UTF8.GetBytes("123456789012"),
+ Options = new List
+ {
+ optionBuilder.CreateUriPort(5648),
+ optionBuilder.CreateContentFormat(CoapMessageContentFormat.ApplicationJson)
+ }
};
- Enocde_And_Decode_Internal(message);
+ EncodeAndDecodeInternal(message);
}
[TestMethod]
@@ -65,15 +67,15 @@ public void Encode_And_Decode_No_Payload()
Code = CoapMessageCodes.Get,
Id = 0x5876,
Token = new byte[] { 1, 2, 3, 4 },
- Payload = null
- };
-
- message.Options = new List
- {
- optionBuilder.CreateUriPort(50)
+ Payload = null,
+ Options = new List
+ {
+ optionBuilder.CreateUriPort(50),
+ optionBuilder.CreateContentFormat(CoapMessageContentFormat.ApplicationJson)
+ }
};
- Enocde_And_Decode_Internal(message);
+ EncodeAndDecodeInternal(message);
}
[TestMethod]
@@ -87,15 +89,15 @@ public void Encode_And_Decode_No_Token()
Code = CoapMessageCodes.Put,
Id = 0x5876,
Token = null,
- Payload = null
- };
-
- message.Options = new List
- {
- optionBuilder.CreateUriPort(66000)
+ Payload = null,
+ Options = new List
+ {
+ optionBuilder.CreateUriPort(66000),
+ optionBuilder.CreateContentFormat(CoapMessageContentFormat.ApplicationJson)
+ }
};
- Enocde_And_Decode_Internal(message);
+ EncodeAndDecodeInternal(message);
}
[TestMethod]
@@ -109,24 +111,30 @@ public void Encode_And_Decode_Multiple_Uri_Path()
Code = CoapMessageCodes.Delete,
Id = 0x50,
Token = null,
- Payload = null
+ Payload = null,
+ Options = new List
+ {
+ optionBuilder.CreateUriPath("a"),
+ optionBuilder.CreateUriPath("b"),
+ optionBuilder.CreateUriPath("c")
+ }
};
- message.Options = new List
- {
- optionBuilder.CreateUriPath("a"),
- optionBuilder.CreateUriPath("b"),
- optionBuilder.CreateUriPath("c")
- };
-
- Enocde_And_Decode_Internal(message);
+ EncodeAndDecodeInternal(message);
}
- void Enocde_And_Decode_Internal(CoapMessage message)
+ static void EncodeAndDecodeInternal(CoapMessage message)
{
var messageEncoder = new CoapMessageEncoder();
var messageBuffer = messageEncoder.Encode(message);
+ // Use a larger buffer to ensure that reading within the bounds works.
+ var largerMessageBuffer = new byte[messageBuffer.Count * 2];
+ Array.Copy(messageBuffer.Array, messageBuffer.Offset, largerMessageBuffer, 0, messageBuffer.Count);
+ Array.Copy(messageBuffer.Array, messageBuffer.Offset, largerMessageBuffer, messageBuffer.Count, messageBuffer.Count);
+
+ messageBuffer = new ArraySegment(largerMessageBuffer, 0, messageBuffer.Count);
+
var messageDecoder = new CoapMessageDecoder(new CoapNetLogger());
var decodedMessage = messageDecoder.Decode(messageBuffer);
diff --git a/Source/CoAPnet.Tests/CoapMessageReader_Tests.cs b/Source/CoAPnet.Tests/CoapMessageReader_Tests.cs
index b398f24..dbee9f7 100644
--- a/Source/CoAPnet.Tests/CoapMessageReader_Tests.cs
+++ b/Source/CoAPnet.Tests/CoapMessageReader_Tests.cs
@@ -15,10 +15,13 @@ public void Read_Bits()
writer.WriteBits(15, 4);
var reader = new CoapMessageReader(writer.ToArray());
-
+ Assert.IsFalse(reader.EndOfStream);
Assert.AreEqual(1, reader.ReadBits(2));
+ Assert.IsTrue(reader.EndOfStream);
Assert.AreEqual(3, reader.ReadBits(2));
+ Assert.IsTrue(reader.EndOfStream);
Assert.AreEqual(15, reader.ReadBits(4));
+ Assert.IsTrue(reader.EndOfStream);
}
[TestMethod]
@@ -31,11 +34,15 @@ public void Read_Spanning_Bits()
writer.WriteBits(254, 8);
var reader = new CoapMessageReader(writer.ToArray());
-
+ Assert.IsFalse(reader.EndOfStream);
Assert.AreEqual(1, reader.ReadBits(2));
+ Assert.IsFalse(reader.EndOfStream);
Assert.AreEqual(3, reader.ReadBits(2));
+ Assert.IsFalse(reader.EndOfStream);
Assert.AreEqual(15, reader.ReadBits(4));
+ Assert.IsFalse(reader.EndOfStream);
Assert.AreEqual(254, reader.ReadBits(8));
+ Assert.IsTrue(reader.EndOfStream);
}
}
}
diff --git a/Source/CoAPnet.sln b/Source/CoAPnet.sln
index 17aa6a7..d75f709 100644
--- a/Source/CoAPnet.sln
+++ b/Source/CoAPnet.sln
@@ -14,7 +14,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{050EC2DA-10AC-4453-BE76-B3BA90CA1EB6}"
ProjectSection(SolutionItems) = preProject
..\Build\build.ps1 = ..\Build\build.ps1
- ..\Build\CoAPnet.Extensions.DTLS.nuspec = ..\Build\CoAPnet.Extensions.DTLS.nuspec
..\Build\ReleaseNotes.md = ..\Build\ReleaseNotes.md
..\Build\upload.ps1 = ..\Build\upload.ps1
EndProjectSection
diff --git a/Source/CoAPnet.sln.DotSettings b/Source/CoAPnet.sln.DotSettings
new file mode 100644
index 0000000..f111090
--- /dev/null
+++ b/Source/CoAPnet.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/Source/CoAPnet/Client/CoapClient.cs b/Source/CoAPnet/Client/CoapClient.cs
index d830216..8e6874e 100644
--- a/Source/CoAPnet/Client/CoapClient.cs
+++ b/Source/CoAPnet/Client/CoapClient.cs
@@ -1,37 +1,38 @@
-using CoAPnet.Internal;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using CoAPnet.Internal;
using CoAPnet.Logging;
using CoAPnet.LowLevelClient;
using CoAPnet.MessageDispatcher;
using CoAPnet.Protocol;
using CoAPnet.Protocol.Observe;
using CoAPnet.Protocol.Options;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
namespace CoAPnet.Client
{
public sealed class CoapClient : ICoapClient
{
- readonly CoapRequestToMessageConverter _requestToMessageConverter = new CoapRequestToMessageConverter();
- readonly CoapMessageToResponseConverter _messageToResponseConverter = new CoapMessageToResponseConverter();
+ readonly CoapNetLogger _logger;
+ readonly LowLevelCoapClient _lowLevelClient;
readonly CoapMessageDispatcher _messageDispatcher = new CoapMessageDispatcher();
readonly CoapMessageIdProvider _messageIdProvider = new CoapMessageIdProvider();
readonly CoapMessageTokenProvider _messageTokenProvider = new CoapMessageTokenProvider();
-
- readonly CoapNetLogger _logger;
+ readonly CoapMessageToResponseConverter _messageToResponseConverter = new CoapMessageToResponseConverter();
readonly CoapClientObservationManager _observationManager;
- readonly LowLevelCoapClient _lowLevelClient;
+ readonly CoapRequestToMessageConverter _requestToMessageConverter = new CoapRequestToMessageConverter();
+
+ CancellationTokenSource _cancellationToken;
CoapClientConnectOptions _connectOptions;
- CancellationTokenSource _cancellationToken;
public CoapClient(CoapNetLogger logger)
{
- _logger = logger;
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_lowLevelClient = new LowLevelCoapClient(_logger);
- _observationManager = new CoapClientObservationManager(_messageToResponseConverter, _lowLevelClient, _logger);
+ _observationManager =
+ new CoapClientObservationManager(_messageToResponseConverter, _lowLevelClient, _logger);
}
public async Task ConnectAsync(CoapClientConnectOptions options, CancellationToken cancellationToken)
@@ -39,7 +40,7 @@ public async Task ConnectAsync(CoapClientConnectOptions options, CancellationTok
_connectOptions = options ?? throw new ArgumentNullException(nameof(options));
await _lowLevelClient.ConnectAsync(options, cancellationToken).ConfigureAwait(false);
-
+
_cancellationToken = new CancellationTokenSource();
ParallelTask.StartLongRunning(() => ReceiveMessages(_cancellationToken.Token), _cancellationToken.Token);
}
@@ -58,13 +59,15 @@ public async Task RequestAsync(CoapRequest request, CancellationTo
var payload = responseMessage.Payload;
if (CoapClientBlockTransferReceiver.IsBlockTransfer(responseMessage))
{
- payload = await new CoapClientBlockTransferReceiver(requestMessage, responseMessage, this, _logger).ReceiveFullPayload(cancellationToken).ConfigureAwait(false);
+ payload = await new CoapClientBlockTransferReceiver(requestMessage, responseMessage, this, _logger)
+ .ReceiveFullPayload(cancellationToken).ConfigureAwait(false);
}
return _messageToResponseConverter.Convert(responseMessage, payload);
}
- public async Task ObserveAsync(CoapObserveOptions options, CancellationToken cancellationToken)
+ public async Task ObserveAsync(CoapObserveOptions options,
+ CancellationToken cancellationToken)
{
if (options is null)
{
@@ -74,7 +77,7 @@ public async Task ObserveAsync(CoapObserveOptions options,
var request = new CoapRequest
{
Method = CoapRequestMethod.Get,
- Options = options.Request.Options,
+ Options = options.Request.Options
};
var token = _messageTokenProvider.Next();
@@ -88,7 +91,8 @@ public async Task ObserveAsync(CoapObserveOptions options,
var payload = responseMessage.Payload;
if (CoapClientBlockTransferReceiver.IsBlockTransfer(responseMessage))
{
- payload = await new CoapClientBlockTransferReceiver(requestMessage, responseMessage, this, _logger).ReceiveFullPayload(cancellationToken).ConfigureAwait(false);
+ payload = await new CoapClientBlockTransferReceiver(requestMessage, responseMessage, this, _logger)
+ .ReceiveFullPayload(cancellationToken).ConfigureAwait(false);
}
_observationManager.Register(token, options.ResponseHandler);
@@ -119,6 +123,19 @@ public async Task StopObservationAsync(CoapObserveResponse observeResponse, Canc
_observationManager.Deregister(observeResponse.Token);
}
+ public void Dispose()
+ {
+ try
+ {
+ _cancellationToken?.Cancel(false);
+ }
+ finally
+ {
+ _cancellationToken?.Dispose();
+ _lowLevelClient?.Dispose();
+ }
+ }
+
internal async Task RequestAsync(CoapMessage requestMessage, CancellationToken cancellationToken)
{
if (requestMessage is null)
@@ -129,11 +146,13 @@ internal async Task RequestAsync(CoapMessage requestMessage, Cancel
requestMessage.Id = _messageIdProvider.Next();
var responseAwaiter = _messageDispatcher.AddAwaiter(requestMessage.Id);
+
try
{
await _lowLevelClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
- var responseMessage = await responseAwaiter.WaitOneAsync(_connectOptions.CommunicationTimeout).ConfigureAwait(false);
+ var responseMessage = await responseAwaiter.WaitOneAsync(_connectOptions.CommunicationTimeout)
+ .ConfigureAwait(false);
if (responseMessage.Code.Equals(CoapMessageCodes.Empty))
{
@@ -148,19 +167,6 @@ internal async Task RequestAsync(CoapMessage requestMessage, Cancel
}
}
- public void Dispose()
- {
- try
- {
- _cancellationToken?.Cancel(false);
- }
- finally
- {
- _cancellationToken?.Dispose();
- _lowLevelClient?.Dispose();
- }
- }
-
async Task ReceiveMessages(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
@@ -192,10 +198,18 @@ async Task ReceiveMessages(CancellationToken cancellationToken)
}
catch (Exception exception)
{
- _logger.Error(nameof(CoapClient), exception, "Error while receiving messages.");
+ if (!_cancellationToken.IsCancellationRequested)
+ {
+ _logger.Error(nameof(CoapClient), exception, "Error while receiving messages.");
+ }
+ else
+ {
+ _logger.Information(nameof(CoapClient), "Stopped receiving messages due to cancellation.");
+ }
+
+ _messageDispatcher.Dispatch(exception);
}
}
}
}
-}
-
+}
\ No newline at end of file
diff --git a/Source/CoAPnet/Client/CoapClientBlockTransferReceiver.cs b/Source/CoAPnet/Client/CoapClientBlockTransferReceiver.cs
index 37ee605..305642e 100644
--- a/Source/CoAPnet/Client/CoapClientBlockTransferReceiver.cs
+++ b/Source/CoAPnet/Client/CoapClientBlockTransferReceiver.cs
@@ -13,9 +13,6 @@ namespace CoAPnet.Client
{
public sealed class CoapClientBlockTransferReceiver
{
- readonly CoapBlockTransferOptionValueEncoder _blockValueEncoder = new CoapBlockTransferOptionValueEncoder();
- readonly CoapBlockTransferOptionValueDecoder _blockValueDecoder = new CoapBlockTransferOptionValueDecoder();
-
readonly CoapMessage _requestMessage;
readonly CoapMessage _firstResponseMessage;
readonly CoapClient _client;
@@ -42,7 +39,7 @@ public static bool IsBlockTransfer(CoapMessage responseMessage)
public async Task> ReceiveFullPayload(CancellationToken cancellationToken)
{
var receivedBlock2Option = _firstResponseMessage.Options.First(o => o.Number == CoapMessageOptionNumber.Block2);
- var receivedBlock2OptionValue = _blockValueDecoder.Decode(((CoapMessageOptionUintValue)receivedBlock2Option.Value).Value);
+ var receivedBlock2OptionValue = CoapBlockTransferOptionValueDecoder.Decode(((CoapMessageOptionUintValue)receivedBlock2Option.Value).Value);
_logger.Trace(nameof(CoapClientBlockTransferReceiver), "Received Block2 {0}.", FormatBlock2OptionValue(receivedBlock2OptionValue));
var requestMessage = new CoapMessage
@@ -69,11 +66,11 @@ public async Task> ReceiveFullPayload(CancellationToken cance
receivedBlock2OptionValue.Number++;
// TODO: Avoid setting value. Create new instead.
- requestBlock2Option.Value = new CoapMessageOptionUintValue(_blockValueEncoder.Encode(receivedBlock2OptionValue));
+ requestBlock2Option.Value = new CoapMessageOptionUintValue(CoapBlockTransferOptionValueEncoder.Encode(receivedBlock2OptionValue));
var response = await _client.RequestAsync(requestMessage, cancellationToken).ConfigureAwait(false);
receivedBlock2Option = response.Options.First(o => o.Number == CoapMessageOptionNumber.Block2);
- receivedBlock2OptionValue = _blockValueDecoder.Decode(((CoapMessageOptionUintValue)receivedBlock2Option.Value).Value);
+ receivedBlock2OptionValue = CoapBlockTransferOptionValueDecoder.Decode(((CoapMessageOptionUintValue)receivedBlock2Option.Value).Value);
_logger.Trace(nameof(CoapClientBlockTransferReceiver), "Received Block2 {0}.", FormatBlock2OptionValue(receivedBlock2OptionValue));
diff --git a/Source/CoAPnet/Client/CoapMessageToken.cs b/Source/CoAPnet/Client/CoapMessageToken.cs
index 238c875..8545e93 100644
--- a/Source/CoAPnet/Client/CoapMessageToken.cs
+++ b/Source/CoAPnet/Client/CoapMessageToken.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Client
{
- public class CoapMessageToken
+ public sealed class CoapMessageToken
{
public CoapMessageToken(byte[] value)
{
diff --git a/Source/CoAPnet/Client/CoapMessageTokenProvider.cs b/Source/CoAPnet/Client/CoapMessageTokenProvider.cs
index 5aa4fdd..717f644 100644
--- a/Source/CoAPnet/Client/CoapMessageTokenProvider.cs
+++ b/Source/CoAPnet/Client/CoapMessageTokenProvider.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Client
{
- public class CoapMessageTokenProvider
+ public sealed class CoapMessageTokenProvider
{
ulong _value;
diff --git a/Source/CoAPnet/Client/CoapObserveRequest.cs b/Source/CoAPnet/Client/CoapObserveRequest.cs
index c56a307..bab6668 100644
--- a/Source/CoAPnet/Client/CoapObserveRequest.cs
+++ b/Source/CoAPnet/Client/CoapObserveRequest.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Client
{
- public class CoapObserveRequest
+ public sealed class CoapObserveRequest
{
public CoapRequestOptions Options { get; set; } = new CoapRequestOptions();
}
diff --git a/Source/CoAPnet/Client/CoapObserveResponse.cs b/Source/CoAPnet/Client/CoapObserveResponse.cs
index bb55c70..ade99e7 100644
--- a/Source/CoAPnet/Client/CoapObserveResponse.cs
+++ b/Source/CoAPnet/Client/CoapObserveResponse.cs
@@ -3,7 +3,7 @@
namespace CoAPnet.Client
{
- public class CoapObserveResponse
+ public sealed class CoapObserveResponse
{
readonly ICoapClient _client;
diff --git a/Source/CoAPnet/Client/CoapRequest.cs b/Source/CoAPnet/Client/CoapRequest.cs
index dc85350..6c44154 100644
--- a/Source/CoAPnet/Client/CoapRequest.cs
+++ b/Source/CoAPnet/Client/CoapRequest.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Client
{
- public class CoapRequest
+ public sealed class CoapRequest
{
public CoapRequestMethod Method { get; set; } = CoapRequestMethod.Get;
diff --git a/Source/CoAPnet/Client/CoapRequestOptions.cs b/Source/CoAPnet/Client/CoapRequestOptions.cs
index cb1fb8f..192e057 100644
--- a/Source/CoAPnet/Client/CoapRequestOptions.cs
+++ b/Source/CoAPnet/Client/CoapRequestOptions.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Client
{
- public class CoapRequestOptions
+ public sealed class CoapRequestOptions
{
///
/// This is only required when accessing virtual servers.
diff --git a/Source/CoAPnet/Client/CoapResponse.cs b/Source/CoAPnet/Client/CoapResponse.cs
index 0c1b7d1..a221d96 100644
--- a/Source/CoAPnet/Client/CoapResponse.cs
+++ b/Source/CoAPnet/Client/CoapResponse.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Client
{
- public class CoapResponse
+ public sealed class CoapResponse
{
public CoapResponseStatusCode StatusCode
{
diff --git a/Source/CoAPnet/Client/CoapResponseOptions.cs b/Source/CoAPnet/Client/CoapResponseOptions.cs
index 6d162e0..a6380c1 100644
--- a/Source/CoAPnet/Client/CoapResponseOptions.cs
+++ b/Source/CoAPnet/Client/CoapResponseOptions.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Client
{
- public class CoapResponseOptions
+ public sealed class CoapResponseOptions
{
public CoapMessageContentFormat? ContentFormat
{
diff --git a/Source/CoAPnet/Client/HandleResponseContext.cs b/Source/CoAPnet/Client/HandleResponseContext.cs
index 0ce3636..e32ec56 100644
--- a/Source/CoAPnet/Client/HandleResponseContext.cs
+++ b/Source/CoAPnet/Client/HandleResponseContext.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Client
{
- public class HandleResponseContext
+ public sealed class HandleResponseContext
{
public uint SequenceNumber { get; set; }
diff --git a/Source/CoAPnet/CoAPnet.csproj b/Source/CoAPnet/CoAPnet.csproj
index f65a968..4ffe558 100644
--- a/Source/CoAPnet/CoAPnet.csproj
+++ b/Source/CoAPnet/CoAPnet.csproj
@@ -37,6 +37,9 @@
true
true
LICENSE
+ 7.3
+ true
+ CS1591
diff --git a/Source/CoAPnet/LowLevelClient/LowLevelCoapClient.cs b/Source/CoAPnet/LowLevelClient/LowLevelCoapClient.cs
index 539bb22..a1ab37c 100644
--- a/Source/CoAPnet/LowLevelClient/LowLevelCoapClient.cs
+++ b/Source/CoAPnet/LowLevelClient/LowLevelCoapClient.cs
@@ -1,25 +1,27 @@
-using CoAPnet.Client;
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using CoAPnet.Client;
using CoAPnet.Exceptions;
using CoAPnet.Logging;
using CoAPnet.Protocol;
using CoAPnet.Protocol.Encoding;
using CoAPnet.Transport;
-using System;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
namespace CoAPnet.LowLevelClient
{
public sealed class LowLevelCoapClient : ILowLevelCoapClient
{
- readonly CoapMessageEncoder _messageEncoder = new CoapMessageEncoder();
- readonly CoapMessageDecoder _messageDecoder;
readonly CoapNetLogger _logger;
+ readonly CoapMessageDecoder _messageDecoder;
+ readonly CoapMessageEncoder _messageEncoder = new CoapMessageEncoder();
// The size of the receive buffer is large enough so that a whole
// UDP datagram will fit into the buffer at once.
readonly ArraySegment _receiveBuffer = new ArraySegment(new byte[65535]);
+ readonly SemaphoreSlim _syncRoot = new SemaphoreSlim(1, 1);
CoapClientConnectOptions _connectOptions;
CoapTransportLayerAdapter _transportLayerAdapter;
@@ -31,6 +33,45 @@ public LowLevelCoapClient(CoapNetLogger logger)
_messageDecoder = new CoapMessageDecoder(logger);
}
+ public async Task SendAsync(CoapMessage message, CancellationToken cancellationToken)
+ {
+ if (message is null)
+ {
+ throw new ArgumentNullException(nameof(message));
+ }
+
+ var requestMessageBuffer = _messageEncoder.Encode(message);
+
+ await _syncRoot.WaitAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ await _transportLayerAdapter.SendAsync(requestMessageBuffer, cancellationToken).ConfigureAwait(false);
+ }
+ finally
+ {
+ _syncRoot.Release();
+ }
+ }
+
+ public async Task ReceiveAsync(CancellationToken cancellationToken)
+ {
+ var datagramLength = await _transportLayerAdapter.ReceiveAsync(_receiveBuffer, cancellationToken)
+ .ConfigureAwait(false);
+
+ if (datagramLength == 0)
+ {
+ return null;
+ }
+
+ Debug.Assert(_receiveBuffer.Array != null);
+ return _messageDecoder.Decode(new ArraySegment(_receiveBuffer.Array, 0, datagramLength));
+ }
+
+ public void Dispose()
+ {
+ _transportLayerAdapter?.Dispose();
+ }
+
public async Task ConnectAsync(CoapClientConnectOptions options, CancellationToken cancellationToken)
{
_connectOptions = options ?? throw new ArgumentNullException(nameof(options));
@@ -43,16 +84,17 @@ public async Task ConnectAsync(CoapClientConnectOptions options, CancellationTok
}
cancellationToken.ThrowIfCancellationRequested();
-
+
_transportLayerAdapter = new CoapTransportLayerAdapter(transportLayer, _logger);
try
{
var transportLayerConnectOptions = new CoapTransportLayerConnectOptions
{
- EndPoint = await ResolveIPEndPoint(options).ConfigureAwait(false)
+ EndPoint = await ResolveIpEndPoint(options).ConfigureAwait(false)
};
- await _transportLayerAdapter.ConnectAsync(transportLayerConnectOptions, cancellationToken).ConfigureAwait(false);
+ await _transportLayerAdapter.ConnectAsync(transportLayerConnectOptions, cancellationToken)
+ .ConfigureAwait(false);
}
catch (Exception)
{
@@ -61,62 +103,32 @@ public async Task ConnectAsync(CoapClientConnectOptions options, CancellationTok
}
}
- public Task SendAsync(CoapMessage message, CancellationToken cancellationToken)
- {
- if (message is null)
- {
- throw new ArgumentNullException(nameof(message));
- }
-
- var requestMessageBuffer = _messageEncoder.Encode(message);
- return _transportLayerAdapter.SendAsync(requestMessageBuffer, cancellationToken);
- }
-
- public async Task ReceiveAsync(CancellationToken cancellationToken)
- {
- var datagramLength = await _transportLayerAdapter.ReceiveAsync(_receiveBuffer, cancellationToken).ConfigureAwait(false);
- if (datagramLength == 0)
- {
- return null;
- }
-
- return _messageDecoder.Decode(new ArraySegment(_receiveBuffer.Array, 0, datagramLength));
- }
-
- public void Dispose()
- {
- _transportLayerAdapter?.Dispose();
- }
-
- async Task ResolveIPEndPoint(CoapClientConnectOptions connectOptions)
+ async Task ResolveIpEndPoint(CoapClientConnectOptions connectOptions)
{
if (IPAddress.TryParse(connectOptions.Host, out var ipAddress))
{
return new IPEndPoint(ipAddress, connectOptions.Port);
}
- else
- {
#if NETSTANDARD1_3
await Task.FromResult(0);
throw new NotSupportedException("Resolving DNS end points is not supported for NETSTANDARD1_3. Please pass IP address instead.");
#else
- try
- {
- var hostIPAddresses = await Dns.GetHostAddressesAsync(connectOptions.Host).ConfigureAwait(false);
- if (hostIPAddresses.Length == 0)
- {
- throw new CoapCommunicationException("Failed to resolve DNS end point", null);
- }
-
- // We only use the first address for now.
- return new IPEndPoint(hostIPAddresses[0], _connectOptions.Port);
- }
- catch (Exception exception)
+ try
+ {
+ var hostIpAddresses = await Dns.GetHostAddressesAsync(connectOptions.Host).ConfigureAwait(false);
+ if (hostIpAddresses.Length == 0)
{
- throw new CoapCommunicationException("Error while resolving DNS name.", exception);
+ throw new CoapCommunicationException("Failed to resolve DNS end point", null);
}
-#endif
+
+ // We only use the first address for now.
+ return new IPEndPoint(hostIpAddresses[0], _connectOptions.Port);
+ }
+ catch (Exception exception)
+ {
+ throw new CoapCommunicationException("Error while resolving DNS name.", exception);
}
+#endif
}
}
-}
+}
\ No newline at end of file
diff --git a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValue.cs b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValue.cs
index 8b3dff4..94636c2 100644
--- a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValue.cs
+++ b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValue.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Protocol.BlockTransfer
{
- public class CoapBlockTransferOptionValue
+ public sealed class CoapBlockTransferOptionValue
{
public ushort Number { get; set; }
diff --git a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueDecoder.cs b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueDecoder.cs
index 8a8baf7..c5d1f52 100644
--- a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueDecoder.cs
+++ b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueDecoder.cs
@@ -3,9 +3,9 @@
namespace CoAPnet.Protocol.BlockTransfer
{
- public class CoapBlockTransferOptionValueDecoder
+ public static class CoapBlockTransferOptionValueDecoder
{
- public CoapBlockTransferOptionValue Decode(uint value)
+ public static CoapBlockTransferOptionValue Decode(uint value)
{
var mask = 0x7;
var size = (ushort)(value & mask);
diff --git a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueEncoder.cs b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueEncoder.cs
index afec2eb..31c3263 100644
--- a/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueEncoder.cs
+++ b/Source/CoAPnet/Protocol/BlockTransfer/CoapBlockTransferOptionValueEncoder.cs
@@ -3,9 +3,9 @@
namespace CoAPnet.Protocol.BlockTransfer
{
- public class CoapBlockTransferOptionValueEncoder
+ public static class CoapBlockTransferOptionValueEncoder
{
- public uint Encode(CoapBlockTransferOptionValue value)
+ public static uint Encode(CoapBlockTransferOptionValue value)
{
if (value is null)
{
diff --git a/Source/CoAPnet/Protocol/Encoding/CoapMessageDecoder.cs b/Source/CoAPnet/Protocol/Encoding/CoapMessageDecoder.cs
index 9b5f5f5..56de8ca 100644
--- a/Source/CoAPnet/Protocol/Encoding/CoapMessageDecoder.cs
+++ b/Source/CoAPnet/Protocol/Encoding/CoapMessageDecoder.cs
@@ -1,8 +1,8 @@
-using CoAPnet.Exceptions;
+using System;
+using System.Collections.Generic;
+using CoAPnet.Exceptions;
using CoAPnet.Logging;
using CoAPnet.Protocol.Options;
-using System;
-using System.Collections.Generic;
namespace CoAPnet.Protocol.Encoding
{
@@ -15,8 +15,7 @@ public CoapMessageDecoder(CoapNetLogger logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
-
- // TODO: Consider creating "CoapMessageDecodeResult" which has Message (null or set) and "DecodeResult" as INTERFACE with all possible errors (VersionInvalidDecodeResult) etc.
+
public CoapMessage Decode(ArraySegment buffer)
{
using (var reader = new CoapMessageReader(buffer))
@@ -59,7 +58,7 @@ public CoapMessage Decode(ArraySegment buffer)
Id = (ushort)id,
Token = token,
Options = options,
- Payload = payload
+ Payload = new ArraySegment(payload)
};
return message;
@@ -158,7 +157,8 @@ CoapMessageOption CreateOption(CoapMessageOptionNumber number, byte[] value)
return _optionFactory.CreateObserve(DecodeUintOptionValue(value));
}
- _logger.Warning(nameof(CoapMessageDecoder), "Invalid message: CoAP option number {0} not supported.", number);
+ _logger.Warning(nameof(CoapMessageDecoder), "Invalid message: CoAP option number {0} not supported.",
+ number);
// We do not throw because new RFCs might use new options. We wrap unknown ones
// into a opaque value.
@@ -180,7 +180,7 @@ List DecodeOptions(CoapMessageReader reader)
var delta = reader.ReadBits(4);
var length = reader.ReadBits(4);
- if ((byte)(delta << 4 | length) == 0xFF)
+ if ((byte)((delta << 4) | length) == 0xFF)
{
// Payload marker.
break;
@@ -235,17 +235,17 @@ static uint DecodeUintOptionValue(byte[] value)
if (value.Length == 2)
{
- return (uint)(value[0] << 8 | value[1]);
+ return (uint)((value[0] << 8) | value[1]);
}
if (value.Length == 3)
{
- return (uint)(value[0] << 16 | value[1] << 8 | value[2]);
+ return (uint)((value[0] << 16) | (value[1] << 8) | value[2]);
}
if (value.Length == 4)
{
- return (uint)(value[0] << 24 | value[1] << 16 | value[2] << 8 | value[3]);
+ return (uint)((value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3]);
}
throw new CoapProtocolViolationException("The buffer for the uint option is too long.");
diff --git a/Source/CoAPnet/Protocol/Encoding/CoapMessageEncoder.cs b/Source/CoAPnet/Protocol/Encoding/CoapMessageEncoder.cs
index 47f82a7..2f3ae22 100644
--- a/Source/CoAPnet/Protocol/Encoding/CoapMessageEncoder.cs
+++ b/Source/CoAPnet/Protocol/Encoding/CoapMessageEncoder.cs
@@ -8,7 +8,7 @@ namespace CoAPnet.Protocol.Encoding
{
public sealed class CoapMessageEncoder
{
- readonly byte[] _emptyArray = new byte[0];
+ static readonly byte[] EmptyArray = new byte[0];
public ArraySegment Encode(CoapMessage message)
{
@@ -48,7 +48,7 @@ public ArraySegment Encode(CoapMessage message)
}
}
- void EncodeOptions(IEnumerable options, CoapMessageWriter writer)
+ static void EncodeOptions(IEnumerable options, CoapMessageWriter writer)
{
if (options == null)
{
@@ -66,7 +66,7 @@ void EncodeOptions(IEnumerable options, CoapMessageWriter wri
if (option.Value is CoapMessageOptionEmptyValue)
{
- value = _emptyArray;
+ value = EmptyArray;
}
else if (option.Value is CoapMessageOptionUintValue uintValue)
{
diff --git a/Source/CoAPnet/Protocol/Encoding/CoapMessageReader.cs b/Source/CoAPnet/Protocol/Encoding/CoapMessageReader.cs
index 89fe2aa..30800ef 100644
--- a/Source/CoAPnet/Protocol/Encoding/CoapMessageReader.cs
+++ b/Source/CoAPnet/Protocol/Encoding/CoapMessageReader.cs
@@ -1,24 +1,32 @@
using System;
-using System.IO;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace CoAPnet.Protocol.Encoding
{
public sealed class CoapMessageReader : IDisposable
{
- readonly MemoryStream _memoryStream;
- readonly ArraySegment _buffer;
+ readonly byte[] _buffer;
+ readonly int _length;
int _bitOffset = -1;
byte _byteCache;
+ int _position;
public CoapMessageReader(ArraySegment buffer)
{
- _memoryStream = new MemoryStream(buffer.Array, 0, buffer.Count, false);
- _buffer = buffer;
+ Debug.Assert(buffer.Array != null);
+
+ _buffer = buffer.Array;
+ _position = buffer.Offset;
+ _length = buffer.Count;
}
- public bool EndOfStream => _memoryStream.Position == _memoryStream.Length;
+ public bool EndOfStream => _position == _length;
+
+ public void Dispose()
+ {
+ }
public int ReadBits(int count)
{
@@ -47,31 +55,31 @@ public int ReadBits(int count)
public byte ReadByte()
{
- return (byte)_memoryStream.ReadByte();
+ var @byte = _buffer[_position];
+ _position++;
+
+ return @byte;
}
public byte[] ReadBytes(int count)
{
var buffer = new byte[count];
- _memoryStream.Read(buffer, 0, buffer.Length);
-
+ Array.Copy(_buffer, _position, buffer, 0, count);
+ _position += count;
return buffer;
}
- public ArraySegment ReadToEnd()
- {
- return new ArraySegment(_buffer.Array, (int)_memoryStream.Position, (int)(_memoryStream.Length - _memoryStream.Position));
- }
-
- public void Dispose()
+ public byte[] ReadToEnd()
{
- _memoryStream.Dispose();
+ // We have to copy the payload because the internal buffer is used for other calls!
+ return ReadBytes(_length - _position);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void FillByteCache()
{
- _byteCache = (byte)_memoryStream.ReadByte();
+ _byteCache = _buffer[_position];
+ _position++;
_bitOffset = 7;
}
}
diff --git a/Source/CoAPnet/Protocol/Observe/CoapObserveOptionValue.cs b/Source/CoAPnet/Protocol/Observe/CoapObserveOptionValue.cs
index 4734be9..5e58b0f 100644
--- a/Source/CoAPnet/Protocol/Observe/CoapObserveOptionValue.cs
+++ b/Source/CoAPnet/Protocol/Observe/CoapObserveOptionValue.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Protocol.Observe
{
- public class CoapObserveOptionValue
+ public static class CoapObserveOptionValue
{
public const uint Register = 0;
diff --git a/Source/CoAPnet/Protocol/Options/CoapMessageOptionEmptyValue.cs b/Source/CoAPnet/Protocol/Options/CoapMessageOptionEmptyValue.cs
index c54c0a4..10a8b3a 100644
--- a/Source/CoAPnet/Protocol/Options/CoapMessageOptionEmptyValue.cs
+++ b/Source/CoAPnet/Protocol/Options/CoapMessageOptionEmptyValue.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Protocol.Options
{
- public class CoapMessageOptionEmptyValue : CoapMessageOptionValue
+ public sealed class CoapMessageOptionEmptyValue : CoapMessageOptionValue
{
public override bool Equals(object obj)
{
diff --git a/Source/CoAPnet/Protocol/Options/CoapMessageOptionOpaqueValue.cs b/Source/CoAPnet/Protocol/Options/CoapMessageOptionOpaqueValue.cs
index 6e7e4e3..1351352 100644
--- a/Source/CoAPnet/Protocol/Options/CoapMessageOptionOpaqueValue.cs
+++ b/Source/CoAPnet/Protocol/Options/CoapMessageOptionOpaqueValue.cs
@@ -2,7 +2,7 @@
namespace CoAPnet.Protocol.Options
{
- public class CoapMessageOptionOpaqueValue : CoapMessageOptionValue
+ public sealed class CoapMessageOptionOpaqueValue : CoapMessageOptionValue
{
public CoapMessageOptionOpaqueValue(byte[] value)
{
diff --git a/Source/CoAPnet/Protocol/Options/CoapMessageOptionStringValue.cs b/Source/CoAPnet/Protocol/Options/CoapMessageOptionStringValue.cs
index 6e170e6..dbcf451 100644
--- a/Source/CoAPnet/Protocol/Options/CoapMessageOptionStringValue.cs
+++ b/Source/CoAPnet/Protocol/Options/CoapMessageOptionStringValue.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Protocol.Options
{
- public class CoapMessageOptionStringValue : CoapMessageOptionValue
+ public sealed class CoapMessageOptionStringValue : CoapMessageOptionValue
{
public CoapMessageOptionStringValue(string value)
{
diff --git a/Source/CoAPnet/Protocol/Options/CoapMessageOptionUintValue.cs b/Source/CoAPnet/Protocol/Options/CoapMessageOptionUintValue.cs
index f8f84e9..5903a5f 100644
--- a/Source/CoAPnet/Protocol/Options/CoapMessageOptionUintValue.cs
+++ b/Source/CoAPnet/Protocol/Options/CoapMessageOptionUintValue.cs
@@ -1,6 +1,6 @@
namespace CoAPnet.Protocol.Options
{
- public class CoapMessageOptionUintValue : CoapMessageOptionValue
+ public sealed class CoapMessageOptionUintValue : CoapMessageOptionValue
{
public CoapMessageOptionUintValue(uint value)
{
diff --git a/Source/CoAPnet/Transport/UdpCoapTransportLayer.cs b/Source/CoAPnet/Transport/UdpCoapTransportLayer.cs
index 4442c8e..773fc23 100644
--- a/Source/CoAPnet/Transport/UdpCoapTransportLayer.cs
+++ b/Source/CoAPnet/Transport/UdpCoapTransportLayer.cs
@@ -7,8 +7,8 @@ namespace CoAPnet.Transport
{
public sealed class UdpCoapTransportLayer : ICoapTransportLayer
{
- UdpClient _udpClient;
CoapTransportLayerConnectOptions _connectOptions;
+ UdpClient _udpClient;
public Task ConnectAsync(CoapTransportLayerConnectOptions options, CancellationToken cancellationToken)
{
@@ -23,7 +23,12 @@ public Task ConnectAsync(CoapTransportLayerConnectOptions options, CancellationT
public async Task ReceiveAsync(ArraySegment buffer, CancellationToken cancellationToken)
{
+#if NET6_0_OR_GREATER
+ var receiveResult = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
+#else
var receiveResult = await _udpClient.ReceiveAsync().ConfigureAwait(false);
+#endif
+
Array.Copy(receiveResult.Buffer, 0, buffer.Array, buffer.Offset, receiveResult.Buffer.Length);
return receiveResult.Buffer.Length;