Skip to content

Commit abc263e

Browse files
authored
Feature/fix cors proxy (#169)
* Change default cors proxy server * Change CORS Proxy handling in Avalonia Browser app. Now the cors proxy URL is a override. If nothing is set a default proxy is used.
1 parent 812526c commit abc263e

6 files changed

Lines changed: 120 additions & 6 deletions

File tree

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/SystemSetup/C64HostConfig.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ public class C64HostConfig : IHostSystemConfig, ICloneable
1515
//public const string DefaultCorsProxyURL = "https://api.allorigins.win/raw?url="; // Doesn't work reliably
1616
//public const string DefaultCorsProxyURL = "https://corsproxy.io/?url="; // Stopped being possible to download binary files on free tier
1717
//public const string DefaultCorsProxyURL = "https://proxy.corsfix.com/?url="; // Only free from localhost
18-
public const string DefaultCorsProxyURL = "https://cors-anywhere.com/";
18+
//public const string DefaultCorsProxyURL = "https://cors-anywhere.com/"; // Only works from localhost
19+
public const string DefaultCorsProxyURL = "https://api.codetabs.com/v1/proxy?quest=";
1920

2021
private C64SystemConfig _systemConfig = new();
2122
ISystemConfig IHostSystemConfig.SystemConfig => _systemConfig;
@@ -31,7 +32,32 @@ public C64SystemConfig SystemConfig
3132

3233
public C64AvaloniaInputConfig InputConfig { get; set; } = new C64AvaloniaInputConfig();
3334

34-
public string CorsProxyURL { get; set; } = DefaultCorsProxyURL;
35+
/// <summary>
36+
/// Cors Proxy address override.
37+
/// If set to null or empty, the default CORS proxy URL will be used when running in WebAssembly. When running on desktop, this setting is ignored and no CORS proxy will be used.
38+
/// </summary>
39+
/// <value></value>
40+
public string? CorsProxyOverrideURL { get; set; } = null;
41+
42+
/// <summary>
43+
/// Return the current CORS proxy URL to use.
44+
/// If running in WebAssembly, this will return the CorsProxyOverrideURL if set, or the DefaultCorsProxyURL if CorsProxyOverrideURL is null or empty.
45+
/// If not running in WebAssembly, this will return null to indicate that no CORS proxy should be used.
46+
/// </summary>
47+
/// <returns></returns> <summary>
48+
///
49+
/// </summary>
50+
/// <returns></returns>
51+
public string GetCorsProxyURL()
52+
{
53+
if (!PlatformDetection.IsRunningInWebAssembly())
54+
{
55+
// CORS proxy is only needed when running in WebAssembly, so return null to not use any proxy when running on desktop
56+
return null!;
57+
}
58+
// If running in WebAssembly, return the configured CORS proxy URL, or the default if not set
59+
return string.IsNullOrEmpty(CorsProxyOverrideURL) ? DefaultCorsProxyURL : CorsProxyOverrideURL;
60+
}
3561

3662
private bool _basicAIAssistantDefaultEnabled;
3763
[JsonIgnore]

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/ViewModels/C64ConfigDialogViewModel.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class C64ConfigDialogViewModel : ViewModelBase
4848
private RenderTargetOption? _selectedRenderTarget;
4949
private bool _suppressRenderTargetUpdate;
5050

51+
private string _corsProxyOverrideURL = string.Empty;
52+
5153
// AI Coding Assistant properties - now using config objects
5254
private CodeSuggestionBackendTypeEnum _selectedAIBackendType;
5355
private ApiConfig _openAIConfig = new();
@@ -61,6 +63,7 @@ public class C64ConfigDialogViewModel : ViewModelBase
6163
public ReactiveCommand<Unit, Unit> DownloadRomsToFilesCommand { get; }
6264
public ReactiveCommand<Unit, Unit> ClearRomsCommand { get; }
6365
public ReactiveCommand<Unit, Unit> TestAIBackendCommand { get; }
66+
public ReactiveCommand<Unit, Unit> ResetCorsProxyOverrideURLCommand { get; }
6467
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
6568
public ReactiveCommand<Unit, Unit> CancelCommand { get; }
6669

@@ -86,6 +89,7 @@ public C64ConfigDialogViewModel(
8689
AudioEnabled = _workingConfig.SystemConfig.AudioEnabled;
8790

8891
RomDirectory = _workingConfig.SystemConfig.ROMDirectory;
92+
CorsProxyOverrideURL = _workingConfig.CorsProxyOverrideURL ?? string.Empty;
8993

9094
// Initialize AI Coding Assistant properties
9195
SelectedAIBackendType = _workingConfig.CodeSuggestionBackendType;
@@ -117,6 +121,14 @@ public C64ConfigDialogViewModel(
117121
TestAIBackendAsync,
118122
outputScheduler: RxApp.MainThreadScheduler);
119123

124+
ResetCorsProxyOverrideURLCommand = ReactiveCommandHelper.CreateSafeCommand(
125+
() =>
126+
{
127+
CorsProxyOverrideURL = string.Empty;
128+
return Task.CompletedTask;
129+
},
130+
outputScheduler: RxApp.MainThreadScheduler);
131+
120132
SaveCommand = ReactiveCommandHelper.CreateSafeCommand(
121133
async () =>
122134
{
@@ -269,6 +281,21 @@ public string RomDirectory
269281
}
270282
}
271283

284+
public string CorsProxyOverrideURL
285+
{
286+
get => _corsProxyOverrideURL;
287+
set
288+
{
289+
if (_corsProxyOverrideURL == value)
290+
return;
291+
292+
this.RaiseAndSetIfChanged(ref _corsProxyOverrideURL, value);
293+
_workingConfig.CorsProxyOverrideURL = string.IsNullOrEmpty(value) ? null : value;
294+
}
295+
}
296+
297+
public static string CorsProxyOverrideURLWatermark => C64HostConfig.DefaultCorsProxyURL;
298+
272299
public RenderProviderOption? SelectedRenderProvider
273300
{
274301
get => _selectedRenderProvider;
@@ -331,8 +358,9 @@ public async Task AutoDownloadRomsToByteArrayAsync()
331358

332359
foreach (var romDownload in _workingConfig.SystemConfig.ROMDownloadUrls)
333360
{
334-
var fullROMUrl = !string.IsNullOrEmpty(_workingConfig.CorsProxyURL)
335-
? $"{_workingConfig.CorsProxyURL}{Uri.EscapeDataString(romDownload.Value)}"
361+
var proxyUrl = _workingConfig.GetCorsProxyURL();
362+
var fullROMUrl = !string.IsNullOrEmpty(proxyUrl)
363+
? $"{proxyUrl}{Uri.EscapeDataString(romDownload.Value)}"
336364
: romDownload.Value;
337365

338366
var romBytes = await _httpClient.GetByteArrayAsync(fullROMUrl);
@@ -1034,6 +1062,7 @@ private void UpdateValidationMessageFromConfig()
10341062
private void ApplyWorkingConfigToOriginal()
10351063
{
10361064
_originalConfig.SystemConfig.ROMDirectory = _workingConfig.SystemConfig.ROMDirectory;
1065+
_originalConfig.CorsProxyOverrideURL = _workingConfig.CorsProxyOverrideURL;
10371066
_originalConfig.SystemConfig.AudioEnabled = _workingConfig.SystemConfig.AudioEnabled;
10381067
_originalConfig.SystemConfig.KeyboardJoystickEnabled = _workingConfig.SystemConfig.KeyboardJoystickEnabled;
10391068
_originalConfig.SystemConfig.KeyboardJoystick = _workingConfig.SystemConfig.KeyboardJoystick;

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/ViewModels/C64MenuViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ private async Task LoadPreloadedDiskImage()
528528
_loggerFactory,
529529
_httpClient,
530530
HostApp!,
531-
corsProxyUrl: PlatformDetection.IsRunningInWebAssembly() ? c64HostConfig.CorsProxyURL : null);
531+
corsProxyUrl: c64HostConfig.GetCorsProxyURL());
532532
}
533533

534534
await _d64AutoDownloadAndRun.DownloadAndRunDiskImage(

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/Views/C64ConfigUserControl.axaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,35 @@
229229
</Border>
230230
</StackPanel>
231231

232+
<!-- Network Section (browser only) -->
233+
<StackPanel Width="460" Classes="wrap-item"
234+
IsVisible="{Binding IsRunningInWebAssembly}">
235+
<Border Classes="content-section">
236+
<StackPanel Classes="spacing-tight">
237+
<TextBlock Text="Network"/>
238+
239+
<Grid ColumnDefinitions="90,*,Auto" Classes="spacing-tight">
240+
<TextBlock Grid.Column="0"
241+
Text="CORS proxy:"
242+
Classes="secondary-text"/>
243+
<TextBox Grid.Column="1"
244+
Text="{Binding CorsProxyOverrideURL, Mode=TwoWay}"
245+
Watermark="{Binding CorsProxyOverrideURLWatermark}"
246+
Classes="field-aligned"
247+
KeyDown="CorsProxyTextBox_KeyDown"/>
248+
<Button Grid.Column="2"
249+
Content="Reset"
250+
Command="{Binding ResetCorsProxyOverrideURLCommand}"
251+
Margin="4,0,0,0"
252+
Height="32"
253+
Padding="12,0"
254+
VerticalContentAlignment="Center"
255+
Classes=""/>
256+
</Grid>
257+
</StackPanel>
258+
</Border>
259+
</StackPanel>
260+
232261
<!-- AI Coding Assistant Section -->
233262
<StackPanel Width="460" Classes="wrap-item">
234263
<Border Classes="content-section">

src/apps/Avalonia/Highbyte.DotNet6502.App.Avalonia.Core/Views/C64ConfigUserControl.axaml.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,35 @@ private void CloseOverlay()
258258
}
259259
}
260260

261+
// In Avalonia WASM on macOS, TextBox default key bindings only include Ctrl+C/V, not Meta (CMD).
262+
// This handler explicitly handles CMD+C/V/X/A so copy/paste works in the browser.
263+
private void CorsProxyTextBox_KeyDown(object? sender, KeyEventArgs e)
264+
{
265+
if (sender is not TextBox textBox) return;
266+
if ((e.KeyModifiers & (KeyModifiers.Meta | KeyModifiers.Control)) == 0) return;
267+
268+
if (e.Key == Key.C)
269+
{
270+
textBox.Copy();
271+
e.Handled = true;
272+
}
273+
else if (e.Key == Key.V)
274+
{
275+
textBox.Paste();
276+
e.Handled = true;
277+
}
278+
else if (e.Key == Key.X)
279+
{
280+
textBox.Cut();
281+
e.Handled = true;
282+
}
283+
else if (e.Key == Key.A)
284+
{
285+
textBox.SelectAll();
286+
e.Handled = true;
287+
}
288+
}
289+
261290
private async void LoadRoms_Click(object? sender, RoutedEventArgs e)
262291
{
263292
if (TopLevel.GetTopLevel(this)?.StorageProvider == null)

src/apps/Highbyte.DotNet6502.App.WASM/Emulator/SystemSetup/C64HostConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class C64HostConfig : IHostSystemConfig, ICloneable
1414
//public const string DefaultCorsProxyURL = "https://thingproxy.freeboard.io/fetch/"; // Doesn't seem to work with redirects
1515
//public const string DefaultCorsProxyURL = "https://corsproxy.io/?url="; // Stopped being possible to download binary files on free tier
1616
//public const string DefaultCorsProxyURL = "https://proxy.corsfix.com/?url="; // Only free from localhost
17-
public const string DefaultCorsProxyURL = "https://cors-anywhere.com/";
17+
//public const string DefaultCorsProxyURL = "https://cors-anywhere.com/"; // Only works from localhost
18+
public const string DefaultCorsProxyURL = "https://api.codetabs.com/v1/proxy?quest=";
1819

1920
private C64SystemConfig _systemConfig;
2021
ISystemConfig IHostSystemConfig.SystemConfig => _systemConfig;

0 commit comments

Comments
 (0)