Skip to content
Merged
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
40 changes: 28 additions & 12 deletions src/Titanium.Inspector/Services/SessionArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,40 @@

public static async Task ExportNativeArchiveAsync(IEnumerable<SessionSnapshot> sessions, string zipPath, CancellationToken ct = default)
{
await using var fs = File.Create(zipPath);
using var zip = new ZipArchive(fs, ZipArchiveMode.Create);
var index = 0;
foreach (var session in sessions)
await using var fs = new FileStream(
zipPath,
FileMode.Create,
FileAccess.ReadWrite,
FileShare.None,
bufferSize: 4096,
FileOptions.Asynchronous | FileOptions.SequentialScan);
using (var zip = new ZipArchive(fs, ZipArchiveMode.Create, leaveOpen: true))
{
ct.ThrowIfCancellationRequested();
var entry = zip.CreateEntry($"session-{index:D5}.json");
await using var stream = await entry.OpenAsync(ct);
await JsonSerializer.SerializeAsync(stream, session, cancellationToken: ct);
index++;
var index = 0;
foreach (var session in sessions)
{
ct.ThrowIfCancellationRequested();
var entry = zip.CreateEntry($"session-{index:D5}.json");
await using var stream = entry.Open();

Check warning on line 69 in src/Titanium.Inspector/Services/SessionArchive.cs

View workflow job for this annotation

GitHub Actions / build

Await OpenAsync instead.

Check warning on line 69 in src/Titanium.Inspector/Services/SessionArchive.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Await OpenAsync instead.

See more on https://sonarcloud.io/project/issues?id=justcoding121_titanium-web-proxy&issues=AaBT_q-QpsdzSMnaK0Aw&open=AaBT_q-QpsdzSMnaK0Aw&pullRequest=988
await JsonSerializer.SerializeAsync(stream, session, cancellationToken: ct);
index++;
}
}

await fs.FlushAsync(ct);
}

public static async Task<List<SessionSnapshot>> ImportNativeArchiveAsync(string zipPath, CancellationToken ct = default)
{
var list = new List<SessionSnapshot>();
await using var fs = File.OpenRead(zipPath);
using var zip = new ZipArchive(fs, ZipArchiveMode.Read);
await using var fs = new FileStream(
zipPath,
FileMode.Open,
FileAccess.Read,
FileShare.Read,
bufferSize: 4096,
FileOptions.Asynchronous | FileOptions.SequentialScan);
using var zip = new ZipArchive(fs, ZipArchiveMode.Read, leaveOpen: true);
foreach (var entry in zip.Entries.OrderBy(e => e.FullName))
{
ct.ThrowIfCancellationRequested();
Expand All @@ -78,7 +94,7 @@
continue;
}

await using var stream = await entry.OpenAsync(ct);
await using var stream = entry.Open();

Check warning on line 97 in src/Titanium.Inspector/Services/SessionArchive.cs

View workflow job for this annotation

GitHub Actions / build

Await OpenAsync instead.

Check warning on line 97 in src/Titanium.Inspector/Services/SessionArchive.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Await OpenAsync instead.

See more on https://sonarcloud.io/project/issues?id=justcoding121_titanium-web-proxy&issues=AaBT_q-QpsdzSMnaK0Ax&open=AaBT_q-QpsdzSMnaK0Ax&pullRequest=988
var snap = await JsonSerializer.DeserializeAsync<SessionSnapshot>(stream, cancellationToken: ct);
if (snap is not null)
{
Expand Down
28 changes: 19 additions & 9 deletions src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@
public bool SystemProxy
{
get => _systemProxy;
set // NOSONAR S4275 -- fail paths leave _systemProxy unchanged and re-raise PropertyChanged to snap the checkbox back

Check warning on line 1056 in src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Refactor this setter so that it actually refers to the field '_systemProxy'.
{
if (_systemProxy == value)
{
Expand Down Expand Up @@ -1142,7 +1142,7 @@
public bool DecryptHttps
{
get => _decryptHttps;
set // NOSONAR S4275 -- true path updates _decryptHttps via SetDecryptHttpsCore after async trust flow

Check warning on line 1145 in src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Refactor this setter so that it actually refers to the field '_decryptHttps'.
{
if (_decryptHttpsBusy || _decryptHttps == value)
{
Expand Down Expand Up @@ -1932,16 +1932,23 @@
return;
}

var imported = await SessionArchive.ImportNativeArchiveAsync(path);
foreach (var snap in imported)
try
{
_registry.Add(snap);
_all.Add(snap);
}
var imported = await SessionArchive.ImportNativeArchiveAsync(path);
foreach (var snap in imported)
{
_registry.Add(snap);
_all.Add(snap);
}

ApplyFilter();
RefreshSessionCountText();
StatusText = $"Appended {imported.Count} sessions from {Path.GetFileName(path)}";
ApplyFilter();
RefreshSessionCountText();
StatusText = $"Appended {imported.Count} sessions from {Path.GetFileName(path)}";
}
catch (Exception ex)
{
StatusText = "Import archive failed: " + Truncate(ex.Message, 160);
}
}

private IReadOnlyList<SessionSnapshot> ResolveExportSelection()
Expand Down Expand Up @@ -1996,7 +2003,10 @@
{
try
{
await execute().ConfigureAwait(false);
// Preserve Avalonia UI sync context so StatusText / collection updates after
// awaits are applied on the UI thread (ConfigureAwait(false) caused macOS
// headless flakes where export wrote the file but StatusText stayed "Ready").
await execute();
}
catch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ await fx.DispatchAsync(() =>
fx.Robot.Click("MenuExportArchive");
});

var exportDeadline = DateTime.UtcNow.AddSeconds(5);
var exportDeadline = DateTime.UtcNow.AddSeconds(15);
while (DateTime.UtcNow < exportDeadline &&
!fx.ViewModel.StatusText.Contains("Exported 1 sessions", StringComparison.Ordinal))
{
Expand All @@ -277,12 +277,29 @@ await fx.DispatchAsync(() =>
Assert.IsTrue(File.Exists(zip));
StringAssert.Contains(fx.ViewModel.StatusText, "Exported 1 sessions");
});

// macOS runners can briefly keep the zip handle; wait until a shared read succeeds.
fx.PathPicker.OpenPath = zip;
var readableDeadline = DateTime.UtcNow.AddSeconds(10);
while (DateTime.UtcNow < readableDeadline)
{
try
{
await using var probe = new FileStream(zip, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
break;
}
catch (IOException)
{
await Task.Delay(50);
}
}

await fx.DispatchAsync(() => fx.Robot.Click("MenuImportArchive"));

var importDeadline = DateTime.UtcNow.AddSeconds(5);
var importDeadline = DateTime.UtcNow.AddSeconds(15);
while (DateTime.UtcNow < importDeadline &&
!fx.ViewModel.StatusText.Contains("Appended", StringComparison.Ordinal))
!fx.ViewModel.StatusText.Contains("Appended", StringComparison.Ordinal) &&
!fx.ViewModel.StatusText.Contains("Import archive failed", StringComparison.Ordinal))
{
await Task.Delay(50);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ await fx.DispatchAsync(() =>
fx.Robot.Click("MenuExportHar");
});

// ExportHarCommand is async; file may exist before StatusText is updated.
var deadline = DateTime.UtcNow.AddSeconds(5);
// ExportHarCommand is async; wait for StatusText (file may exist briefly before it).
var deadline = DateTime.UtcNow.AddSeconds(15);
while (DateTime.UtcNow < deadline &&
!fx.ViewModel.StatusText.Contains("Exported 1 sessions", StringComparison.Ordinal))
{
Expand Down
Loading