How to make "uri" and "headers" ANDed together instead of ORed?

We’re working towards deploying Istio in our k8s clusters. Our prototype cluster has a virtualservice defined for a service. Requests to the service are being processed correctly.

I’m now trying to augment this virtualservice with alternate routes with header matches. After adding these, I’m finding that all requests are going to the alternate route, even if the header is not set. I can see why this is happening, because we don’t have a “default” route. The original “default” route still used “match” on the uri. I wish there was a way to AND all the match terms, instead of ORing them, but I believe the conventional solution at this point is to set up a “default” route.

This is what the vs def currently looks like:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  annotations: {}
  name: fooms-vs-ingress
  namespace: com-example
spec:
  gateways:
  - ingress-gateway
  hosts:
  - '*'
  http:
  - match:
    - uri:
        prefix: /msapi/foo
    - headers:
        debug-route-riskassessmentms:
          exact: myuid-1
    route:
    - destination:
        host: fooms.com-example.svc.cluster.local
        port:
          number: 80
        subset: myuid-1
  - match:
    - uri:
        prefix: /msapi/foo
    route:
    - destination:
        host: fooms.com-example.svc.cluster.local
        port:
          number: 80
        subset: blue

What is the proper solution for ensuring that requests that don’t match the “header” match use the second match block instead of the first one? I still need it to match the uri.

Actually, it appears that I’m mistaken about the rules. According to the info in “HTTPRoute” at https://istio.io/docs/reference/config/networking/virtual-service/ , the terms underneath “match” are ANDed, not ORed, so something else must be happening that I don’t understand.

To be clear, when I sent a request with the header " debug-route-riskassessmentms" with value " myuid-1", it went to the pod associated with the " myuid-1" subset, which is correct. However, when I sent a request without that header, or with that header with a different value, it ALSO went to the pod associated with the " myuid-1" subset. Those should have matched the second " match" block, because the URI prefix matched.

Any ideas why this happened?

Istio evaluation of rules is a greedy one. Or may be it could be a bug that it does not take into consideration the complete set of rules in match before routing.

Prefix matches anything that begins with.
Try exact in the uri and see if it helps