@@ -34,11 +34,6 @@ type Rule struct {
3434
3535type methodPattern string
3636
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-
4237// Beyond the 9 methods defined in HTTP 1.1, there actually are many more seldom used extension methods by
4338// various systems.
4439// 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
126121// Represents a valid label in a hostname. For example, wobble in `wib-ble.wobble.com`.
127122type labelPattern string
128123
124+ // An `asterisk` is treated as matching anything
125+ func (lp labelPattern ) matches (input string ) bool {
126+ return lp == "*" || string (lp ) == input
127+ }
128+
129129func parseLabelPattern (rest string ) (labelPattern , string , error ) {
130130 if rest == "" {
131131 return "" , "" , errors .New ("expected label, got empty string" )
132132 }
133133
134+ // If the label is simply an asterisk, good to go.
135+ if rest [0 ] == '*' {
136+ return "*" , rest [1 :], nil
137+ }
138+
134139 // First try to get a valid leading char. Leading char in a label cannot be a hyphen.
135140 if ! isValidLabelChar (rest [0 ]) || rest [0 ] == '-' {
136141 return "" , "" , fmt .Errorf ("could not pull label from front of string: %s" , rest )
@@ -216,11 +221,24 @@ func parsePathPattern(input string) ([]segmentPattern, string, error) {
216221// Represents a valid url path segmentPattern.
217222type segmentPattern string
218223
224+ // An `*` is treated as matching anything
225+ func (sp segmentPattern ) matches (input string ) bool {
226+ return sp == "*" || string (sp ) == input
227+ }
228+
219229func parsePathSegmentPattern (input string ) (segmentPattern , string , error ) {
220230 if input == "" {
221231 return "" , "" , nil
222232 }
223233
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+
224242 var i int
225243 for i = 0 ; i < len (input ); i ++ {
226244 c := input [i ]
@@ -411,84 +429,6 @@ func (re *Engine) Evaluate(method, url string) Result {
411429 }
412430}
413431
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-
492432// Matches checks if the rule matches the given method and URL using wildcard patterns
493433func (re * Engine ) matches (r Rule , method , url string ) bool {
494434 return true
0 commit comments