|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Globalization;
using Microsoft.ApplicationInsights.Channel;
namespace Microsoft.DotNet.Cli.Telemetry.PersistenceChannel;
internal class StorageTransmission : Transmission, IDisposable
{
internal Action<StorageTransmission> Disposing;
protected StorageTransmission(string fullPath, Uri address, byte[] content, string contentType,
string contentEncoding)
: base(address, content, contentType, contentEncoding)
{
FullFilePath = fullPath;
FileName = Path.GetFileName(fullPath);
}
internal string FileName { get; }
internal string FullFilePath { get; }
/// <summary>
/// Disposing the storage transmission.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Creates a new transmission from the specified <paramref name="stream" />.
/// </summary>
/// <returns>Return transmission loaded from file; return null if the file is corrupted.</returns>
internal static async Task<StorageTransmission> CreateFromStreamAsync(Stream stream, string fileName)
{
StreamReader reader = new(stream);
Uri address = await ReadAddressAsync(reader).ConfigureAwait(false);
string contentType = await ReadHeaderAsync(reader, "Content-Type").ConfigureAwait(false);
string contentEncoding = await ReadHeaderAsync(reader, "Content-Encoding").ConfigureAwait(false);
byte[] content = await ReadContentAsync(reader).ConfigureAwait(false);
return new StorageTransmission(fileName, address, content, contentType, contentEncoding);
}
/// <summary>
/// Saves the transmission to the specified <paramref name="stream" />.
/// </summary>
internal static async Task SaveAsync(Transmission transmission, Stream stream)
{
StreamWriter writer = new(stream);
try
{
await writer.WriteLineAsync(transmission.EndpointAddress.ToString()).ConfigureAwait(false);
await writer.WriteLineAsync("Content-Type" + ":" + transmission.ContentType).ConfigureAwait(false);
await writer.WriteLineAsync("Content-Encoding" + ":" + transmission.ContentEncoding)
.ConfigureAwait(false);
await writer.WriteLineAsync(string.Empty).ConfigureAwait(false);
await writer.WriteAsync(Convert.ToBase64String(transmission.Content)).ConfigureAwait(false);
}
finally
{
writer.Flush();
}
}
private static async Task<string> ReadHeaderAsync(TextReader reader, string headerName)
{
string line = await reader.ReadLineAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(line))
{
throw new FormatException(string.Format(CultureInfo.InvariantCulture, "{0} header is expected.",
headerName));
}
string[] parts = line.Split(':');
if (parts.Length != 2)
{
throw new FormatException(string.Format(CultureInfo.InvariantCulture,
"Unexpected header format. {0} header is expected. Actual header: {1}", headerName, line));
}
if (parts[0] != headerName)
{
throw new FormatException(string.Format(CultureInfo.InvariantCulture,
"{0} header is expected. Actual header: {1}", headerName, line));
}
return parts[1].Trim();
}
private static async Task<Uri> ReadAddressAsync(TextReader reader)
{
string addressLine = await reader.ReadLineAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(addressLine))
{
throw new FormatException("Transmission address is expected.");
}
Uri address = new(addressLine);
return address;
}
private static async Task<byte[]> ReadContentAsync(TextReader reader)
{
string content = await reader.ReadToEndAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(content) || content == Environment.NewLine)
{
throw new FormatException("Content is expected.");
}
return Convert.FromBase64String(content);
}
private void Dispose(bool disposing)
{
if (disposing)
{
Action<StorageTransmission> disposingDelegate = Disposing;
disposingDelegate?.Invoke(this);
}
}
}
|