From 91a9ee5295e7120376de4feb79229c17a0f45d3d Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Tue, 10 Nov 2026 18:01:22 +0100 Subject: [PATCH] Proxy errors load as an empty page Co-Authored-By: Claude Opus 5.5 --- app/lib/desktop/github_proxy.dart | 8 ++- app/pubspec.yaml | 2 +- .../desktop/desktop_wrapper_no_text_test.dart | 56 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/app/lib/desktop/github_proxy.dart b/app/lib/desktop/github_proxy.dart index cb0a827..7ddec8e 100644 --- a/app/lib/desktop/github_proxy.dart +++ b/app/lib/desktop/github_proxy.dart @@ -177,8 +177,14 @@ class GitHubProxy { } void _passThrough(HttpRequest request, http.Response response) { + final isNavigate = request.headers.value('sec-fetch-mode') == 'navigate'; + request.response.statusCode = response.statusCode; + if (isNavigate) { + // Top-level navigation (window load) must not paint error JSON; the app's + // own fetch() calls never send 'navigate', so they still get the body. + return; + } request.response - ..statusCode = response.statusCode ..headers.contentType = ContentType.json ..add(response.bodyBytes); } diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 704c02d..10f8dfa 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -2,7 +2,7 @@ name: diet_guard_app description: "Companion phone app for diet_guard: log meals on the go." publish_to: 'none' -version: 1.0.151+1 +version: 1.0.152+1 environment: sdk: ^3.12.2 diff --git a/app/test/desktop/desktop_wrapper_no_text_test.dart b/app/test/desktop/desktop_wrapper_no_text_test.dart index c1c05ae..75d2d4b 100644 --- a/app/test/desktop/desktop_wrapper_no_text_test.dart +++ b/app/test/desktop/desktop_wrapper_no_text_test.dart @@ -70,4 +70,60 @@ void main() { expect(response.body, isEmpty, reason: 'nothing painted in the window'); }); } + + group('proxy error responses', () { + late Directory tempDir; + late WrapperServer server; + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp('diet_guard_proxy_err_'); + final webRoot = p.join(tempDir.path, 'web'); + final dataDir = p.join(tempDir.path, 'data'); + await Directory(webRoot).create(recursive: true); + File(p.join(webRoot, 'index.html')).writeAsStringSync(''); + await Directory(dataDir).create(recursive: true); + File(p.join(dataDir, 'documents')).writeAsStringSync(''); + final tokenPath = p.join(tempDir.path, 'sync_token'); + await File(tokenPath).writeAsString('t'); + server = WrapperServer( + webRoot: webRoot, + dataDir: dataDir, + gitHubProxy: GitHubProxy( + tokenPath: tokenPath, + fallbackTokenPath: p.join(tempDir.path, 'fallback_token'), + httpClient: MockClient((_) async => + http.Response('{"message": "Bad credentials"}', 401)), + ), + ); + await server.start(0); + }); + + tearDown(() async { + await server.stop(); + await tempDir.delete(recursive: true); + }); + + Uri url(String path) => Uri.parse('http://localhost:${server.port}$path'); + + test('a proxy error loaded as a page has an empty body', () async { + final request = http.Request('GET', url('/github/api/repos/o/r/contents/x')) + ..headers['sec-fetch-mode'] = 'navigate'; + final response = await http.Response.fromStream( + await http.Client().send(request), + ); + expect(response.statusCode, HttpStatus.unauthorized); + expect(response.body, isEmpty, reason: 'nothing painted in the window'); + }); + + test('the app fetch still gets the proxy error body', () async { + final response = await http.Response.fromStream( + await http.Client().send(http.Request( + 'GET', + url('/github/api/repos/o/r/contents/x'), + )), + ); + expect(response.statusCode, HttpStatus.unauthorized); + expect(response.body, contains('Bad credentials')); + }); + }); }