func()

in pilot/pkg/networking/core/v1alpha3/gateway.go [324:474]


func (configgen *ConfigGeneratorImpl) buildGatewayHTTPRouteConfig(node *model.Proxy, push *model.PushContext,
	routeName string) *route.RouteConfiguration {
	if node.MergedGateway == nil {
		log.Warnf("buildGatewayRoutes: no gateways for router %v", node.ID)
		return &route.RouteConfiguration{
			Name:             routeName,
			VirtualHosts:     []*route.VirtualHost{},
			ValidateClusters: proto.BoolFalse,
		}
	}

	merged := node.MergedGateway
	log.Debugf("buildGatewayRoutes: gateways after merging: %v", merged)

	// make sure that there is some server listening on this port
	if _, ok := merged.ServersByRouteName[routeName]; !ok {
		log.Warnf("Gateway missing for route %s. This is normal if gateway was recently deleted.", routeName)

		// This can happen when a gateway has recently been deleted. Envoy will still request route
		// information due to the draining of listeners, so we should not return an error.
		return nil
	}

	servers := merged.ServersByRouteName[routeName]

	// When this is true, we add alt-svc header to the response to tell the client
	// that HTTP/3 over QUIC is available on the same port for this host. This is
	// very important for discovering HTTP/3 services
	_, isH3DiscoveryNeeded := merged.HTTP3AdvertisingRoutes[routeName]

	gatewayRoutes := make(map[string]map[string][]*route.Route)
	gatewayVirtualServices := make(map[string][]config.Config)
	vHostDedupMap := make(map[host.Name]*route.VirtualHost)
	for _, server := range servers {
		gatewayName := merged.GatewayNameForServer[server]
		port := int(server.Port.Number)

		var virtualServices []config.Config
		var exists bool

		if virtualServices, exists = gatewayVirtualServices[gatewayName]; !exists {
			virtualServices = push.VirtualServicesForGateway(node.ConfigNamespace, gatewayName)
			gatewayVirtualServices[gatewayName] = virtualServices
		}

		for _, virtualService := range virtualServices {
			virtualServiceHosts := host.NewNames(virtualService.Spec.(*networking.VirtualService).Hosts)
			serverHosts := host.NamesForNamespace(server.Hosts, virtualService.Namespace)

			// We have two cases here:
			// 1. virtualService hosts are 1.foo.com, 2.foo.com, 3.foo.com and server hosts are ns/*.foo.com
			// 2. virtualService hosts are *.foo.com, and server hosts are ns/1.foo.com, ns/2.foo.com, ns/3.foo.com
			intersectingHosts := serverHosts.Intersection(virtualServiceHosts)
			if len(intersectingHosts) == 0 {
				continue
			}

			// Make sure we can obtain services which are visible to this virtualService as much as possible.
			nameToServiceMap := buildNameToServiceMapForHTTPRoutes(node, push, virtualService)

			var routes []*route.Route
			var exists bool
			var err error
			if _, exists = gatewayRoutes[gatewayName]; !exists {
				gatewayRoutes[gatewayName] = make(map[string][]*route.Route)
			}

			vskey := virtualService.Name + "/" + virtualService.Namespace

			if routes, exists = gatewayRoutes[gatewayName][vskey]; !exists {
				hashByDestination := istio_route.GetConsistentHashForVirtualService(push, node, virtualService, nameToServiceMap)
				routes, err = istio_route.BuildHTTPRoutesForVirtualService(node, virtualService, nameToServiceMap,
					hashByDestination, port, map[string]bool{gatewayName: true}, isH3DiscoveryNeeded, push.Mesh)
				if err != nil {
					log.Debugf("%s omitting routes for virtual service %v/%v due to error: %v", node.ID, virtualService.Namespace, virtualService.Name, err)
					continue
				}
				gatewayRoutes[gatewayName][vskey] = routes
			}

			for _, hostname := range intersectingHosts {
				if vHost, exists := vHostDedupMap[hostname]; exists {
					vHost.Routes = append(vHost.Routes, routes...)
					if server.Tls != nil && server.Tls.HttpsRedirect {
						vHost.RequireTls = route.VirtualHost_ALL
					}
				} else {
					newVHost := &route.VirtualHost{
						Name:                       util.DomainName(string(hostname), port),
						Domains:                    buildGatewayVirtualHostDomains(string(hostname), port),
						Routes:                     routes,
						IncludeRequestAttemptCount: true,
					}
					if server.Tls != nil && server.Tls.HttpsRedirect {
						newVHost.RequireTls = route.VirtualHost_ALL
					}
					vHostDedupMap[hostname] = newVHost
				}
			}
		}

		// check all hostname in vHostDedupMap and if is not exist with HttpsRedirect set to true
		// create VirtualHost to redirect
		for _, hostname := range server.Hosts {
			if !server.GetTls().GetHttpsRedirect() {
				continue
			}
			if vHost, exists := vHostDedupMap[host.Name(hostname)]; exists {
				vHost.RequireTls = route.VirtualHost_ALL
				continue
			}
			newVHost := &route.VirtualHost{
				Name:                       util.DomainName(hostname, port),
				Domains:                    buildGatewayVirtualHostDomains(hostname, port),
				IncludeRequestAttemptCount: true,
				RequireTls:                 route.VirtualHost_ALL,
			}
			vHostDedupMap[host.Name(hostname)] = newVHost
		}
	}

	var virtualHosts []*route.VirtualHost
	if len(vHostDedupMap) == 0 {
		port := int(servers[0].Port.Number)
		log.Warnf("constructed http route config for route %s on port %d with no vhosts; Setting up a default 404 vhost", routeName, port)
		virtualHosts = []*route.VirtualHost{{
			Name:    util.DomainName("blackhole", port),
			Domains: []string{"*"},
			// Empty route list will cause Envoy to 404 NR any requests
			Routes: []*route.Route{},
		}}
	} else {
		virtualHosts = make([]*route.VirtualHost, 0, len(vHostDedupMap))
		vHostDedupMap = collapseDuplicateRoutes(vHostDedupMap)
		for _, v := range vHostDedupMap {
			v.Routes = istio_route.CombineVHostRoutes(v.Routes)
			virtualHosts = append(virtualHosts, v)
		}
	}

	util.SortVirtualHosts(virtualHosts)

	routeCfg := &route.RouteConfiguration{
		// Retain the routeName as its used by EnvoyFilter patching logic
		Name:             routeName,
		VirtualHosts:     virtualHosts,
		ValidateClusters: proto.BoolFalse,
	}

	return routeCfg
}