From b1f46942589771050aec26ed0755fd9b9d00a2c3 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 18 Nov 2025 12:06:57 -0800 Subject: [PATCH] [tool] Further cleanup of proxy logic (#178683) Move getFinalTargetUri to ProxyRule class Fix doc comments Pares URI immediately when creating RegexProxyRule --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../lib/src/isolated/proxy_middleware.dart | 17 ++------- .../lib/src/web/devfs_proxy.dart | 36 +++++++++++-------- .../test/general.shard/web/proxy_test.dart | 8 ++--- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/flutter_tools/lib/src/isolated/proxy_middleware.dart b/packages/flutter_tools/lib/src/isolated/proxy_middleware.dart index a230f39bc44..6f6927a72af 100644 --- a/packages/flutter_tools/lib/src/isolated/proxy_middleware.dart +++ b/packages/flutter_tools/lib/src/isolated/proxy_middleware.dart @@ -24,17 +24,6 @@ shelf.Request proxyRequest(shelf.Request originalRequest, Uri finalTargetUrl) { ); } -/// Rewrites the request path based on the specified rule by: -/// 1. Getting the base target URI from the rule. -/// 2. Replacing the request path according to the rule's replacement logic. -/// 3. Resolving the final target URL by combining the target URI, the rewritten -/// request path and the request query -Uri getFinalTargetUri(shelf.Request request, ProxyRule rule) { - final String rewrittenPath = rule.replace(request.requestedUri.path); - final Uri targetUri = rule.getTargetUri(); - return targetUri.resolveUri(Uri(path: rewrittenPath, query: request.requestedUri.query)); -} - /// Iterates through the provided [effectiveProxy] rules for each incoming [shelf.Request]. /// /// If a rule's pattern matches the request's path, the request is @@ -51,11 +40,11 @@ Future _applyProxyRules( if (!rule.matches(requestPath)) { continue; } - final Uri targetUri = rule.getTargetUri(); - final Uri finalTargetUrl = getFinalTargetUri(request, rule); + final shelf.Handler handler = proxyHandler(rule.targetUri, proxyName: 'flutter_tools'); + final Uri finalTargetUrl = rule.finalTargetUri(request.requestedUri); try { final shelf.Request proxyBackendRequest = proxyRequest(request, finalTargetUrl); - final shelf.Response proxyResponse = await proxyHandler(targetUri)(proxyBackendRequest); + final shelf.Response proxyResponse = await handler(proxyBackendRequest); logger.printStatus('$_kLogEntryPrefix Matched "$requestPath". Requesting "$finalTargetUrl"'); logger.printTrace('$_kLogEntryPrefix Matched with proxy rule: $rule'); if (proxyResponse.statusCode == 404) { diff --git a/packages/flutter_tools/lib/src/web/devfs_proxy.dart b/packages/flutter_tools/lib/src/web/devfs_proxy.dart index 3efa5ef3d76..b0842eceba1 100644 --- a/packages/flutter_tools/lib/src/web/devfs_proxy.dart +++ b/packages/flutter_tools/lib/src/web/devfs_proxy.dart @@ -6,7 +6,8 @@ import 'package:yaml/yaml.dart'; import '../base/logger.dart'; /// Represents a rule for proxying requests based on a specific pattern. -/// Subclasses must implement the [matches], [replace], and [getTargetUri] methods. +/// +/// Subclasses must implement the [matches], [replace], and [targetUri] members. sealed class ProxyRule { static const _kLogEntryPrefix = '[ProxyRule]'; static const _kTarget = 'target'; @@ -22,7 +23,7 @@ sealed class ProxyRule { String replace(String path); /// Returns the target URI to which the request should be proxied. - Uri getTargetUri(); + Uri get targetUri; /// If both or neither 'prefix' and 'regex' are defined, it logs an error and returns null. /// Otherwise, it tries to create a [PrefixProxyRule] or [RegexProxyRule] based on the [yaml] keys. @@ -42,6 +43,17 @@ sealed class ProxyRule { return null; } } + + /// Rewrites [requestUri] for the target URI. + /// + /// 1. Getting the base target URI from the rule. + /// 2. Replacing the request path according to the rule's replacement logic. + /// 3. Resolving the final target URL by combining the target URI, the rewritten + /// request path and the request query + Uri finalTargetUri(Uri requestUri) { + final String rewrittenPath = replace(requestUri.path); + return targetUri.resolveUri(Uri(path: rewrittenPath, query: requestUri.query)); + } } /// A [ProxyRule] implementation that uses regular expressions for matching and @@ -50,17 +62,19 @@ sealed class ProxyRule { /// This rule matches paths against a provided regular expression [_pattern]. /// If a [_replacement] string is provided, it replaces parts of the matched /// path based on regex group capturing. -class RegexProxyRule implements ProxyRule { +class RegexProxyRule extends ProxyRule { /// Creates a [RegexProxyRule] with the given regular expression [pattern], /// [target] URI base, and optional [replacement] string. RegexProxyRule({required RegExp pattern, required String target, String? replacement}) - : _pattern = pattern, - _target = target, + : targetUri = Uri.parse(target), + _pattern = pattern, _replacement = replacement; final RegExp _pattern; - final String _target; final String? _replacement; + @override + /// The target URI to which the request should be proxied. + final Uri targetUri; @override bool matches(String path) { @@ -81,15 +95,9 @@ class RegexProxyRule implements ProxyRule { }); } - @override - Uri getTargetUri() { - final Uri targetBaseUri = Uri.parse(_target); - return targetBaseUri; - } - @override String toString() { - return '{${ProxyRule._kRegex}: ${_pattern.pattern}, ${ProxyRule._kTarget}: $_target, ${ProxyRule._kReplace}: ${_replacement ?? 'null'}}'; + return '{${ProxyRule._kRegex}: ${_pattern.pattern}, ${ProxyRule._kTarget}: $targetUri, ${ProxyRule._kReplace}: ${_replacement ?? 'null'}}'; } /// Checks if the given [yaml] can be handled by this rule. @@ -142,7 +150,7 @@ class PrefixProxyRule extends RegexProxyRule { @override String toString() { - return '{${ProxyRule._kPrefix}: ${_pattern.pattern}, ${ProxyRule._kTarget}: $_target, ${ProxyRule._kReplace}: ${_replacement ?? 'null'}}'; + return '{${ProxyRule._kPrefix}: ${_pattern.pattern}, ${ProxyRule._kTarget}: $targetUri, ${ProxyRule._kReplace}: ${_replacement ?? 'null'}}'; } /// Checks if the given [yaml] can be handled by this rule. diff --git a/packages/flutter_tools/test/general.shard/web/proxy_test.dart b/packages/flutter_tools/test/general.shard/web/proxy_test.dart index 39c1272209c..1a02420fb7e 100644 --- a/packages/flutter_tools/test/general.shard/web/proxy_test.dart +++ b/packages/flutter_tools/test/general.shard/web/proxy_test.dart @@ -170,7 +170,7 @@ void main() { target: 'http://localhost:8080/users/', replacement: r'$1', ); - final Uri targetUri = rule.getTargetUri(); + final Uri targetUri = rule.targetUri; expect(targetUri.toString(), 'http://localhost:8080/users/'); expect(targetUri.scheme, 'http'); expect(targetUri.host, 'localhost'); @@ -283,7 +283,7 @@ void main() { test('getTargetUri returns correct Uri', () { final rule = PrefixProxyRule(prefix: '/api/users', target: 'http://localhost:8080'); - final Uri targetUri = rule.getTargetUri(); + final Uri targetUri = rule.targetUri; expect(targetUri.toString(), 'http://localhost:8080'); expect(targetUri.scheme, 'http'); expect(targetUri.host, 'localhost'); @@ -371,13 +371,13 @@ void main() { test('should add query parameters if original request does have one', () { final rule = RegexProxyRule(pattern: RegExp(r'^/api'), target: 'http://mock-backend.com'); final originalRequest = Request('GET', Uri.parse('http://localhost:8000/api?foo=bar&a=b')); - final Uri target = getFinalTargetUri(originalRequest, rule); + final Uri target = rule.finalTargetUri(originalRequest.requestedUri); expect('$target', 'http://mock-backend.com/api?foo=bar&a=b'); }); test('should not add empty query if original request does not have one', () { final rule = RegexProxyRule(pattern: RegExp(r'^/api'), target: 'http://mock-backend.com'); final originalRequest = Request('GET', Uri.parse('http://localhost:8000/api')); - final Uri target = getFinalTargetUri(originalRequest, rule); + final Uri target = rule.finalTargetUri(originalRequest.requestedUri); expect('$target', 'http://mock-backend.com/api'); }); });