Proxy errors load as an empty page

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-11-10 18:01:22 +01:00
parent 0982565fdc
commit 91a9ee5295
3 changed files with 64 additions and 2 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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('<html></html>');
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'));
});
});
}