Repository: traefik/traefik
File: pkg/provider/kubernetes/ingress/kubernetes.go (Go)

Code before:
	dupKey := fmt.Sprintf("%s-%.10x", key, h.Sum(nil))

	return dupKey, nil
}

func buildRule(strictPrefixMatching bool, matcher string, path string) string {
	// strictPrefixMatching makes the prefix matching strictly follow the Kubernetes Ingress spec.
	if strictPrefixMatching && matcher == "PathPrefix" {
		// We want to keep the behavior same as https://kubernetes.io/docs/concepts/services-networking/ingress/#examples.
		// i.e. /v12 should not match /v1 , but traefik's PathPrefix matcher works as a prefix match not the
		// element by element match as Kubernetes Ingress, so we need to use PathRegexp as a workaround.
		// Check out TestPrefixMatchRegex() for more examples.
		path = strings.TrimRight(path, "/")
		return "PathRegexp(`" + buildPrefixMatchRegex(path) + "`)"
	}

	return fmt.Sprintf("%s(`%s`)", matcher, path)
}

// buildPrefixMatchRegex returns a regex that matches the given path and any subpaths.
// For example, if path is "/v1", the regex will match "/v1" and "/v1/anything", but not "/v12".
func buildPrefixMatchRegex(path string) string {
	path = strings.TrimSuffix(path, "/")
	return fmt.Sprintf(prefixMatchRegexTemplate, regexp.QuoteMeta(path))
}

func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
	if throttleDuration == 0 {
		return nil
	}

	// Create a buffered channel to hold the pending event (if we're delaying processing the event due to throttling).
	eventsChanBuffered := make(chan interface{}, 1)

	// Run a goroutine that reads events from eventChan and does a
	// non-blocking write to pendingEvent. This guarantees that writing to
	// eventChan will never block, and that pendingEvent will have
	// something in it if there's been an event since we read from that channel.
	pool.GoCtx(func(ctxPool context.Context) {
		for {
			select {
			case <-ctxPool.Done():
				return
			case nextEvent := <-eventsChan:
				select {
				case eventsChanBuffered <- nextEvent:
				default:
					// We already have an event in eventsChanBuffered, so we'll
					// do a refresh as soon as our throttle allows us to. It's fine
					// to drop the event and keep whatever's in the buffer -- we
					// don't do different things for different events.

Review comment (suggestion):
```suggestion
func buildRule(strictPrefixMatching bool, matcher string, path string) string {
	// When enabled, strictPrefixMatching ensures that prefix matching follows
	// the Kubernetes Ingress spec (path-element-wise instead of character-wise).
	if strictPrefixMatching && matcher == "PathPrefix" {
		// To mimic Kubernetes behavior (see:
		// https://kubernetes.io/docs/concepts/services-networking/ingress/#examples),
		// "/v12" should not match "/v1".
		//
		// Traefik's default PathPrefix matcher performs a character-wise prefix match,
		// unlike Kubernetes which matches path elements.
		// Use PathRegexp to replicate element-wise behavior.
		//
		// See TestPrefixMatchRegex() for examples.
		path = strings.TrimRight(path, "/")
		return "PathRegexp(`" + buildPrefixMatchRegex(path) + "`)"
	}

	return fmt.Sprintf("%s(`%s`)", matcher, path)
}

// buildPrefixMatchRegex returns a regex that matches the given path and its subpaths.
// For example, "/v1" matches "/v1" and "/v1/anything", but not "/v12".
func buildPrefixMatchRegex(path string) string {
	path = strings.TrimSuffix(path, "/")
	return fmt.Sprintf(prefixMatchRegexTemplate, regexp.QuoteMeta(path))
}
```

Code after:

	return dupKey, nil
}

func buildRule(strictPrefixMatching bool, matcher string, path string) string {
	// When enabled, strictPrefixMatching ensures that prefix matching follows
	// the Kubernetes Ingress spec (path-element-wise instead of character-wise).
	if strictPrefixMatching && matcher == "PathPrefix" {
		// According to
		// https://kubernetes.io/docs/concepts/services-networking/ingress/#examples,
		// "/v12" should not match "/v1".
		//
		// Traefik's default PathPrefix matcher performs a character-wise prefix match,
		// unlike Kubernetes which matches path elements. To mimic Kubernetes behavior,
		// we will use Path and PathPrefix to replicate element-wise behavior.
		//
		// "PathPrefix" in Kubernetes Gateway API is semantically equivalent to the "Prefix" path type in the
		// Kubernetes Ingress API.
		return buildStrictPrefixMatchingRule(path)
	}

	return fmt.Sprintf("%s(`%s`)", matcher, path)
}

// buildStrictPrefixMatchingRule is a helper function to build a path prefix rule that matches path prefix split by `/`.
// For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`,
// but the path `/abcd` would not. See TestStrictPrefixMatchingRule() for more examples.
//
// "PathPrefix" in Kubernetes Gateway API is semantically equivalent to the "Prefix" path type in the
// Kubernetes Ingress API.
func buildStrictPrefixMatchingRule(path string) string {
	if path == "/" {
		return "PathPrefix(`/`)"
	}

	path = strings.TrimSuffix(path, "/")
	return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", path)
}

func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
	if throttleDuration == 0 {
		return nil
	}

	// Create a buffered channel to hold the pending event (if we're delaying processing the event due to throttling).
	eventsChanBuffered := make(chan interface{}, 1)

	// Run a goroutine that reads events from eventChan and does a
	// non-blocking write to pendingEvent. This guarantees that writing to
	// eventChan will never block, and that pendingEvent will have
	// something in it if there's been an event since we read from that channel.