Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Iot.Operations.Protocol;
using Azure.Iot.Operations.Protocol.Connection;
using Azure.Iot.Operations.Protocol.Models;
using IMqttClient = MQTTnet.IMqttClient;

namespace Azure.Iot.Operations.Mqtt;

public class ExtendedPubSubMqttClient(IMqttClient mqttNetClient, OrderedAckMqttClientOptions? clientOptions = null)
: OrderedAckMqttClient(mqttNetClient, clientOptions), IExtendedPubSubMqttClient
{
private MqttClientConnectResult? _connectResult;

public override async Task<MqttClientConnectResult> ConnectAsync(MqttClientOptions options, CancellationToken cancellationToken = default)
{
var connectResult = await base.ConnectAsync(options, cancellationToken);
_connectResult = connectResult;
return connectResult;
}

public override async Task<MqttClientConnectResult> ConnectAsync(MqttConnectionSettings settings, CancellationToken cancellationToken = default)
{
var connectResult = await base.ConnectAsync(settings, cancellationToken);
_connectResult = connectResult;
return connectResult;
}

public MqttClientConnectResult? GetConnectResult()
{
return _connectResult;
}
}
14 changes: 14 additions & 0 deletions dotnet/src/Azure.Iot.Operations.Protocol/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.CompilerServices;

// TODO: @maximsemenov80 it looks like assembly shoulb be signed for the integration tests to work

Check warning on line 6 in dotnet/src/Azure.Iot.Operations.Protocol/AssemblyInfo.cs

View workflow job for this annotation

GitHub Actions / CI-spelling

Unknown word (maximsemenov)

Check warning on line 6 in dotnet/src/Azure.Iot.Operations.Protocol/AssemblyInfo.cs

View workflow job for this annotation

GitHub Actions / CI-spelling

Unknown word (shoulb) Suggestions: (should, shul, soul, seoul, shoal)
//[assembly: InternalsVisibleTo("Azure.Iot.Operations.Protocol.IntegrationTests")]

namespace Azure.Iot.Operations.Protocol;

public class AssemblyInfo {
// This class is intentionally left empty.
// It serves as a placeholder for assembly-level attributes and metadata.
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.2" />
</ItemGroup>

<ItemGroup>
<Folder Include="Chunking\Exceptions\" />
</ItemGroup>

<!-- Add strong name signing properties for release pipeline compatibility -->
<PropertyGroup>
<AssemblyOriginatorKeyFile>$(MSBuildProjectDirectory)\..\..\MSSharedLibKey.snk</AssemblyOriginatorKeyFile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Buffers;
using System.Security.Cryptography;

namespace Azure.Iot.Operations.Protocol.Chunking;

/// <summary>
/// Provides checksum calculation for message chunking.
/// </summary>
internal static class ChecksumCalculator
{
/// <summary>
/// Calculates a checksum for the given data using the specified algorithm.
/// </summary>
/// <param name="data">The data to calculate a checksum for.</param>
/// <param name="algorithm">The algorithm to use for the checksum.</param>
/// <returns>A string representation of the checksum.</returns>
public static string CalculateChecksum(ReadOnlySequence<byte> data, ChunkingChecksumAlgorithm algorithm)
{
ReadOnlySpan<byte> hash = CalculateHashBytes(data, algorithm);
return Convert.ToHexString(hash).ToLowerInvariant();
}

/// <summary>
/// Verifies that the calculated checksum matches the expected checksum.
/// </summary>
/// <param name="data">The data to calculate a checksum for.</param>
/// <param name="expectedChecksum">The expected checksum value.</param>
/// <param name="algorithm">The algorithm to use for the checksum.</param>
/// <returns>True if the checksums match, false otherwise.</returns>
public static bool VerifyChecksum(ReadOnlySequence<byte> data, string expectedChecksum, ChunkingChecksumAlgorithm algorithm)
{
string actualChecksum = CalculateChecksum(data, algorithm);
return string.Equals(actualChecksum, expectedChecksum, StringComparison.OrdinalIgnoreCase);
}

private static byte[] CalculateHashBytes(ReadOnlySequence<byte> data, ChunkingChecksumAlgorithm algorithm)
{
using HashAlgorithm hashAlgorithm = CreateHashAlgorithm(algorithm);

if (data.IsSingleSegment)
{
return hashAlgorithm.ComputeHash(data.FirstSpan.ToArray());
}

// Process multiple segments
hashAlgorithm.Initialize();

foreach (ReadOnlyMemory<byte> segment in data)
{
hashAlgorithm.TransformBlock(segment.Span.ToArray(), 0, segment.Length, null, 0);
}

hashAlgorithm.TransformFinalBlock([], 0, 0);
return hashAlgorithm.Hash!;
}

private static HashAlgorithm CreateHashAlgorithm(ChunkingChecksumAlgorithm algorithm)
{
return algorithm switch
{
#pragma warning disable CA5351
ChunkingChecksumAlgorithm.MD5 => MD5.Create(),
#pragma warning restore CA5351
ChunkingChecksumAlgorithm.SHA256 => SHA256.Create(),
_ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Text.Json.Serialization;

namespace Azure.Iot.Operations.Protocol.Chunking;

/// <summary>
/// Represents the metadata for a chunk of a larger MQTT message.
/// </summary>
internal class ChunkMetadata
{
/// <summary>
/// Gets or sets the unique identifier for the chunked message.
/// </summary>
[JsonPropertyName(ChunkingConstants.MessageIdField)]
public string MessageId { get; set; } = null!;

/// <summary>
/// Gets or sets the index of this chunk in the sequence.
/// </summary>
[JsonPropertyName(ChunkingConstants.ChunkIndexField)]
public int ChunkIndex { get; set; }

/// <summary>
/// Gets or sets the total number of chunks in the message.
/// This property is only present in the first chunk.
/// </summary>
[JsonPropertyName(ChunkingConstants.TotalChunksField)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? TotalChunks { get; set; }

/// <summary>
/// Gets or sets the checksum of the complete message.
/// This property is only present in the first chunk.
/// </summary>
[JsonPropertyName(ChunkingConstants.ChecksumField)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Checksum { get; set; }

/// <summary>
/// Creates a new instance of the <see cref="ChunkMetadata"/> class for a first chunk.
/// </summary>
/// <param name="messageId">The unique message identifier.</param>
/// <param name="totalChunks">The total number of chunks in the message.</param>
/// <param name="checksum">The checksum of the complete message.</param>
/// <returns>A new instance of <see cref="ChunkMetadata"/> configured for the first chunk.</returns>
public static ChunkMetadata CreateFirstChunk(string messageId, int totalChunks, string checksum)
{
return new ChunkMetadata
{
MessageId = messageId,
ChunkIndex = 0,
TotalChunks = totalChunks,
Checksum = checksum
};
} /// <summary>
/// Creates a new instance of the <see cref="ChunkMetadata"/> class for subsequent chunks.
/// </summary>
/// <param name="messageId">The unique message identifier.</param>
/// <param name="chunkIndex">The index of this chunk in the sequence.</param>
/// <returns>A new instance of <see cref="ChunkMetadata"/> configured for a subsequent chunk.</returns>
public static ChunkMetadata CreateSubsequentChunk(string messageId, int chunkIndex)
{
return new ChunkMetadata
{
MessageId = messageId,
ChunkIndex = chunkIndex
};
}
}
Loading
Loading