[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>
This commit is contained in:
Kevin Moore 2025-11-18 12:06:57 -08:00 committed by GitHub
parent 88f960983f
commit b1f4694258
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 32 deletions

View File

@ -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<shelf.Response> _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) {

View File

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

View File

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