22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ import 'package:glob/glob.dart' ;
56import 'package:yaml/yaml.dart' ;
67import '../base/logger.dart' ;
78
9+ bool _twoCanHandle (bool a, bool b, bool c) {
10+ return (a && b) || (a && c) || (b && c);
11+ }
12+
813/// Represents a rule for proxying requests based on a specific pattern.
914/// Subclasses must implement the [matches] , [replace] , and [getTargetUri] methods.
1015sealed class ProxyRule {
1116 static const _kLogEntryPrefix = '[ProxyRule]' ;
1217 static const _kTarget = 'target' ;
1318 static const _kRegex = 'regex' ;
1419 static const _kPrefix = 'prefix' ;
20+ static const _kSource = 'source' ;
1521 static const _kReplace = 'replace' ;
1622
1723 /// Checks if the given [path] matches the rule's pattern.
@@ -27,16 +33,22 @@ sealed class ProxyRule {
2733 /// If both or neither 'prefix' and 'regex' are defined, it logs an error and returns null.
2834 /// Otherwise, it tries to create a [PrefixProxyRule] or [RegexProxyRule] based on the [yaml] keys.
2935 static ProxyRule ? fromYaml (YamlMap yaml, Logger logger) {
30- if (PrefixProxyRule .canHandle (yaml) && RegexProxyRule .canHandle (yaml)) {
36+ if (_twoCanHandle (
37+ PrefixProxyRule .canHandle (yaml),
38+ RegexProxyRule .canHandle (yaml),
39+ SourceProxyRule .canHandle (yaml),
40+ )) {
3141 logger.printError (
32- '$_kLogEntryPrefix Both $_kPrefix and $_kRegex are defined in the proxy rule YAML.'
42+ '$_kLogEntryPrefix Multiple patterns ( $_kPrefix , $_kRegex , $ _kSource ) are defined in the proxy rule YAML.'
3343 ' Only one should be used.' ,
3444 );
3545 return null ;
3646 } else if (PrefixProxyRule .canHandle (yaml)) {
3747 return PrefixProxyRule .fromYaml (yaml, logger);
3848 } else if (RegexProxyRule .canHandle (yaml)) {
3949 return RegexProxyRule .fromYaml (yaml, logger);
50+ } else if (SourceProxyRule .canHandle (yaml)) {
51+ return SourceProxyRule .fromYaml (yaml, logger);
4052 } else {
4153 logger.printError ('$_kLogEntryPrefix Invalid proxy rule in YAML: $yaml ' );
4254 return null ;
@@ -172,3 +184,54 @@ class PrefixProxyRule extends RegexProxyRule {
172184 return PrefixProxyRule (prefix: prefix, target: target, replacement: replacement? .trim ());
173185 }
174186}
187+
188+ /// A [ProxyRule] implementation that uses glob syntax for matching and
189+ /// replacement.
190+ ///
191+ /// This rule matches paths against a provided glob syntax [_source] .
192+ /// If a [_replacement] string is provided, it replaces parts of the matched
193+ /// path based on regex group capturing.
194+ class SourceProxyRule extends RegexProxyRule {
195+ /// Creates a [SourceProxyRule] with the given glob [source] ,
196+ /// [target] URI base, and optional [replacement] string.
197+ SourceProxyRule ({required Glob source, required super .target, super .replacement})
198+ : _source = source,
199+ super (pattern: RegExp (RegExp .escape (source.pattern)));
200+ final Glob _source;
201+
202+ @override
203+ bool matches (String path) {
204+ return _source.matches (path);
205+ }
206+
207+ @override
208+ String toString () {
209+ return '{${ProxyRule ._kSource }: ${_source .pattern }, ${ProxyRule ._kTarget }: $_target , ${ProxyRule ._kReplace }: ${_replacement ?? 'null' }}' ;
210+ }
211+
212+ /// Checks if the given [yaml] can be handled by this rule.
213+ /// It requires the 'source' key to be present and non-empty.
214+ static bool canHandle (YamlMap yaml) {
215+ return yaml.containsKey (ProxyRule ._kSource) &&
216+ yaml[ProxyRule ._kSource] is String &&
217+ (yaml[ProxyRule ._kSource] as String ).isNotEmpty;
218+ }
219+
220+ /// Attempts to create a [SourceProxyRule] from the provided [yaml] map.
221+ /// If the 'source' or 'target' keys are missing or invalid, it logs an error
222+ /// and returns null.
223+ static SourceProxyRule ? fromYaml (YamlMap yaml, Logger effectiveLogger) {
224+ final source = yaml[ProxyRule ._kSource] as String ? ;
225+ final target = yaml[ProxyRule ._kTarget] as String ? ;
226+ final replacement = yaml[ProxyRule ._kReplace] as String ? ;
227+ if (source == null || source.isEmpty) {
228+ return null ;
229+ } else if (target == null || target.isEmpty) {
230+ effectiveLogger.printError (
231+ '${ProxyRule ._kLogEntryPrefix } Invalid ${ProxyRule ._kTarget } for ${ProxyRule ._kSource }: $source . ${ProxyRule ._kTarget } cannot be null' ,
232+ );
233+ return null ;
234+ }
235+ return SourceProxyRule (source: Glob (source), target: target, replacement: replacement? .trim ());
236+ }
237+ }
0 commit comments