@@ -34,11 +34,6 @@ type Rule struct {
34
34
35
35
type methodPattern string
36
36
37
- // An asterisk is treated as matching any method
38
- func (t methodPattern ) matches (input string ) bool {
39
- return t == "*" || string (t ) == input
40
- }
41
-
42
37
// Beyond the 9 methods defined in HTTP 1.1, there actually are many more seldom used extension methods by
43
38
// various systems.
44
39
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
@@ -126,11 +121,21 @@ func parseHostPattern(input string) (host []labelPattern, rest string, err error
126
121
// Represents a valid label in a hostname. For example, wobble in `wib-ble.wobble.com`.
127
122
type labelPattern string
128
123
124
+ // An `asterisk` is treated as matching anything
125
+ func (lp labelPattern ) matches (input string ) bool {
126
+ return lp == "*" || string (lp ) == input
127
+ }
128
+
129
129
func parseLabelPattern (rest string ) (labelPattern , string , error ) {
130
130
if rest == "" {
131
131
return "" , "" , errors .New ("expected label, got empty string" )
132
132
}
133
133
134
+ // If the label is simply an asterisk, good to go.
135
+ if rest [0 ] == '*' {
136
+ return "*" , rest [1 :], nil
137
+ }
138
+
134
139
// First try to get a valid leading char. Leading char in a label cannot be a hyphen.
135
140
if ! isValidLabelChar (rest [0 ]) || rest [0 ] == '-' {
136
141
return "" , "" , fmt .Errorf ("could not pull label from front of string: %s" , rest )
@@ -216,11 +221,24 @@ func parsePathPattern(input string) ([]segmentPattern, string, error) {
216
221
// Represents a valid url path segmentPattern.
217
222
type segmentPattern string
218
223
224
+ // An `*` is treated as matching anything
225
+ func (sp segmentPattern ) matches (input string ) bool {
226
+ return sp == "*" || string (sp ) == input
227
+ }
228
+
219
229
func parsePathSegmentPattern (input string ) (segmentPattern , string , error ) {
220
230
if input == "" {
221
231
return "" , "" , nil
222
232
}
223
233
234
+ if len (input ) > 0 && input [0 ] == '*' {
235
+ if len (input ) > 1 && input [1 ] != '/' {
236
+ return "" , "" , fmt .Errorf ("path segment wildcards must be for the entire segment, got: %s" , input )
237
+ }
238
+
239
+ return segmentPattern (input [0 ]), input [1 :], nil
240
+ }
241
+
224
242
var i int
225
243
for i = 0 ; i < len (input ); i ++ {
226
244
c := input [i ]
@@ -411,84 +429,6 @@ func (re *Engine) Evaluate(method, url string) Result {
411
429
}
412
430
}
413
431
414
- type protocol string
415
-
416
- func parseProtocol (input string ) (protocol , string , error ) {
417
- if input == "" {
418
- return "" , "" , errors .New ("expected protocol, got empty string" )
419
- }
420
-
421
- // Look for "://" separator
422
- if idx := strings .Index (input , "://" ); idx > 0 {
423
- protocolPart := input [:idx ]
424
- rest := input [idx + 3 :]
425
-
426
- // Validate protocol characters (scheme per RFC 3986)
427
- // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
428
- if len (protocolPart ) == 0 {
429
- return "" , "" , errors .New ("empty protocol" )
430
- }
431
-
432
- // First character must be alpha
433
- if ! ((protocolPart [0 ] >= 'A' && protocolPart [0 ] <= 'Z' ) ||
434
- (protocolPart [0 ] >= 'a' && protocolPart [0 ] <= 'z' )) {
435
- return "" , "" , errors .New ("protocol must start with a letter" )
436
- }
437
-
438
- // Rest can be alphanumeric, +, -, or .
439
- for i := 1 ; i < len (protocolPart ); i ++ {
440
- c := protocolPart [i ]
441
- if ! ((c >= 'A' && c <= 'Z' ) || (c >= 'a' && c <= 'z' ) ||
442
- (c >= '0' && c <= '9' ) || c == '+' || c == '-' || c == '.' ) {
443
- return "" , "" , fmt .Errorf ("invalid character in protocol: %c" , c )
444
- }
445
- }
446
-
447
- return protocol (protocolPart ), rest , nil
448
- }
449
-
450
- // No protocol found
451
- return "" , input , nil
452
- }
453
-
454
- type port uint16
455
-
456
- func parsePort (input string ) (port , string , error ) {
457
- if input == "" {
458
- return 0 , "" , nil
459
- }
460
-
461
- // Port must start with ':'
462
- if input [0 ] != ':' {
463
- return 0 , input , nil
464
- }
465
-
466
- // Find the end of the port number
467
- i := 1
468
- for i < len (input ) && input [i ] >= '0' && input [i ] <= '9' {
469
- i ++
470
- }
471
-
472
- // No digits found after ':'
473
- if i == 1 {
474
- return 0 , "" , errors .New ("expected port number after ':'" )
475
- }
476
-
477
- portStr := input [1 :i ]
478
- rest := input [i :]
479
-
480
- // Convert to uint16 (port range is 0-65535)
481
- portNum := 0
482
- for _ , digit := range portStr {
483
- portNum = portNum * 10 + int (digit - '0' )
484
- if portNum > 65535 {
485
- return 0 , "" , errors .New ("port number too large (max 65535)" )
486
- }
487
- }
488
-
489
- return port (portNum ), rest , nil
490
- }
491
-
492
432
// Matches checks if the rule matches the given method and URL using wildcard patterns
493
433
func (re * Engine ) matches (r Rule , method , url string ) bool {
494
434
return true
0 commit comments