Repository: traefik/traefik (language: go)

Issue:
HTTPRoute: Middleware Sequence
### Welcome!

- [x] Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
- [x] Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.

### What did you do?

we defined middlewares through filters in HTTPRoute Kubernetes resources (i.e. using the Gateway API), the sequence of middlewares are cors, authz.

### What did you see instead?

The sequence of middlewares are not respected. some times its authz,cors and sometimes it is cors,authz.. I drilled down to the code. it looks like map to array conversion is done here. hence the sequence gets random. https://github.com/traefik/traefik/blob/master/pkg/provider/kubernetes/gateway/httproute.go#L430-L439

### What version of Traefik are you using?

3.1.5

### What is your environment & configuration?

```yaml
# (paste your configuration here)
```

Add more configuration information here.


### If applicable, please paste the log output in DEBUG level

_No response_


Discussion / hints:

Fix patch:
diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml
index c377fd139e..0d2ffc9181 100644
--- a/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml
+++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/filter_extension_ref.yml
@@ -54,4 +54,9 @@ spec:
           extensionRef:
             group: traefik.io
             kind: Middleware
-            name: my-middleware
+            name: my-first-middleware
+        - type: ExtensionRef
+          extensionRef:
+            group: traefik.io
+            kind: Middleware
+            name: my-second-middleware
diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go
index fc6c9cf1bb..df9905aed7 100644
--- a/pkg/provider/kubernetes/gateway/httproute.go
+++ b/pkg/provider/kubernetes/gateway/httproute.go
@@ -301,36 +301,52 @@ func (p *Provider) loadHTTPBackendRef(namespace string, backendRef gatev1.HTTPBa
 }
 
 func (p *Provider) loadMiddlewares(conf *dynamic.Configuration, namespace, routerName string, filters []gatev1.HTTPRouteFilter, pathMatch *gatev1.HTTPPathMatch) ([]string, error) {
+	type namedMiddleware struct {
+		Name   string
+		Config *dynamic.Middleware
+	}
+
 	pm := ptr.Deref(pathMatch, gatev1.HTTPPathMatch{
 		Type:  ptr.To(gatev1.PathMatchPathPrefix),
 		Value: ptr.To("/"),
 	})
 
-	middlewares := make(map[string]*dynamic.Middleware)
+	var middlewares []namedMiddleware
 	for i, filter := range filters {
 		name := fmt.Sprintf("%s-%s-%d", routerName, strings.ToLower(string(filter.Type)), i)
+
 		switch filter.Type {
 		case gatev1.HTTPRouteFilterRequestRedirect:
-			middlewares[name] = createRequestRedirect(filter.RequestRedirect, pm)
+			middlewares = append(middlewares, namedMiddleware{
+				name,
+				createRequestRedirect(filter.RequestRedirect, pm),
+			})
 
 		case gatev1.HTTPRouteFilterRequestHeaderModifier:
-			middlewares[name] = createRequestHeaderModifier(filter.RequestHeaderModifier)
+			middlewares = append(middlewares, namedMiddleware{
+				name,
+				createRequestHeaderModifier(filter.RequestHeaderModifier),
+			})
 
 		case gatev1.HTTPRouteFilterExtensionRef:
 			name, middleware, err := p.loadHTTPRouteFilterExtensionRef(namespace, filter.ExtensionRef)
 			if err != nil {
 				return nil, fmt.Errorf("loading ExtensionRef filter %s: %w", filter.Type, err)
 			}
-
-			middlewares[name] = middleware
+			middlewares = append(middlewares, namedMiddleware{
+				name,
+				middleware,
+			})
 
 		case gatev1.HTTPRouteFilterURLRewrite:
-			var err error
 			middleware, err := createURLRewrite(filter.URLRewrite, pm)
 			if err != nil {
 				return nil, fmt.Errorf("invalid filter %s: %w", filter.Type, err)
 			}
-			middlewares[name] = middleware
+			middlewares = append(middlewares, namedMiddleware{
+				name,
+				middleware,
+			})
 
 		default:
 			// As per the spec: https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional
@@ -342,12 +358,11 @@ func (p *Provider) loadMiddlewares(conf *dynamic.Configuration, namespace, route
 	}
 
 	var middlewareNames []string
-	for name, middleware := range middlewares {
-		if middleware != nil {
-			conf.HTTP.Middlewares[name] = middleware
+	for _, m := range middlewares {
+		if m.Config != nil {
+			conf.HTTP.Middlewares[m.Name] = m.Config
 		}
-
-		middlewareNames = append(middlewareNames, name)
+		middlewareNames = append(middlewareNames, m.Name)
 	}
 
 	return middlewareNames, nil


Test patch:
diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go
index 28d1c95915..c3b287ddd2 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes_test.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go
@@ -2431,7 +2431,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
 		entryPoints          map[string]Entrypoint
 	}{
 		{
-			desc: "HTTPRoute with ExtensionRef filter",
+			desc: "ExtensionRef filter",
 			groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
 				traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
 					return namespace + "-" + name, nil, nil
@@ -2459,7 +2459,10 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
 							Rule:        "Host(`foo.com`) && Path(`/bar`)",
 							Priority:    100008,
 							RuleSyntax:  "v3",
-							Middlewares: []string{"default-my-middleware"},
+							Middlewares: []string{
+								"default-my-first-middleware",
+								"default-my-second-middleware",
+							},
 						},
 					},
 					Middlewares: map[string]*dynamic.Middleware{},
@@ -2497,7 +2500,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
 			},
 		},
 		{
-			desc: "HTTPRoute with ExtensionRef filter and create middleware",
+			desc: "ExtensionRef filter with middleware creation",
 			groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
 				traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
 					return namespace + "-" + name, &dynamic.Middleware{Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, nil
@@ -2525,11 +2528,15 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
 							Rule:        "Host(`foo.com`) && Path(`/bar`)",
 							Priority:    100008,
 							RuleSyntax:  "v3",
-							Middlewares: []string{"default-my-middleware"},
+							Middlewares: []string{
+								"default-my-first-middleware",
+								"default-my-second-middleware",
+							},
 						},
 					},
 					Middlewares: map[string]*dynamic.Middleware{
-						"default-my-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
+						"default-my-first-middleware":  {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
+						"default-my-second-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
 					},
 					Services: map[string]*dynamic.Service{
 						"default-http-app-1-my-gateway-web-0-wrr": {
@@ -2565,7 +2572,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
 			},
 		},
 		{
-			desc: "ExtensionRef filter: Unknown",
+			desc: "Unknown ExtensionRef filter",
 			entryPoints: map[string]Entrypoint{"web": {
 				Address: ":80",
 			}},