func()

in xds/server/listener_wrapper.go [210:319]


func (l *listenerWrapper) Accept() (net.Conn, error) {
	var retries int
	for {
		conn, err := l.Listener.Accept()
		if err != nil {
			// Temporary() method is implemented by certain error types returned
			// from the net package, and it is useful for us to not shutdown the
			// server in these conditions. The listen queue being full is one
			// such case.
			if ne, ok := err.(interface{ Temporary() bool }); !ok || !ne.Temporary() {
				return nil, err
			}
			retries++
			timer := time.NewTimer(backoffFunc(retries))
			select {
			case <-timer.C:
			case <-l.closed.Done():
				timer.Stop()
				// Continuing here will cause us to call Accept() again
				// which will return a non-temporary error.
				continue
			}
			continue
		}
		// Reset retries after a successful Accept().
		retries = 0

		// Since the net.Conn represents an incoming connection, the source and
		// destination address can be retrieved from the local address and
		// remote address of the net.Conn respectively.
		destAddr, ok1 := conn.LocalAddr().(*net.TCPAddr)
		srcAddr, ok2 := conn.RemoteAddr().(*net.TCPAddr)
		if !ok1 || !ok2 {
			// If the incoming connection is not a TCP connection, which is
			// really unexpected since we check whether the provided listener is
			// a TCP listener in Serve(), we return an error which would cause
			// us to stop serving.
			return nil, fmt.Errorf("received connection with non-TCP address (local: %T, remote %T)", conn.LocalAddr(), conn.RemoteAddr())
		}

		l.mu.RLock()
		if l.mode == connectivity.ServingModeNotServing {
			// Close connections as soon as we accept them when we are in
			// "not-serving" mode. Since we accept a net.Listener from the user
			// in Serve(), we cannot close the listener when we move to
			// "not-serving". Closing the connection immediately upon accepting
			// is one of the other ways to implement the "not-serving" mode as
			// outlined in gRFC A36.
			l.mu.RUnlock()
			conn.Close()
			continue
		}
		fc, err := l.filterChains.Lookup(resource.FilterChainLookupParams{
			IsUnspecifiedListener: l.isUnspecifiedAddr,
			DestAddr:              destAddr.IP,
			SourceAddr:            srcAddr.IP,
			SourcePort:            srcAddr.Port,
		})
		l.mu.RUnlock()
		if err != nil {
			// When a matching filter chain is not found, we close the
			// connection right away, but do not return an error back to
			// `grpc.Serve()` from where this Accept() was invoked. Returning an
			// error to `grpc.Serve()` causes the server to shutdown. If we want
			// to avoid the server from shutting down, we would need to return
			// an error type which implements the `Temporary() bool` method,
			// which is invoked by `grpc.Serve()` to see if the returned error
			// represents a temporary condition. In the case of a temporary
			// error, `grpc.Serve()` method sleeps for a small duration and
			// therefore ends up blocking all connection attempts during that
			// time frame, which is also not ideal for an error like this.
			l.logger.Warnf("connection from %s to %s failed to find any matching filter chain", conn.RemoteAddr().String(), conn.LocalAddr().String())
			conn.Close()
			continue
		}
		if !envconfig.XDSRBAC {
			return &connWrapper{Conn: conn, filterChain: fc, parent: l}, nil
		}
		var rc resource.RouteConfigUpdate
		if fc.InlineRouteConfig != nil {
			rc = *fc.InlineRouteConfig
		} else {
			rcPtr := atomic.LoadPointer(&l.rdsUpdates)
			rcuPtr := (*map[string]resource.RouteConfigUpdate)(rcPtr)
			// This shouldn't happen, but this error protects against a panic.
			if rcuPtr == nil {
				return nil, errors.New("route configuration pointer is nil")
			}
			rcu := *rcuPtr
			rc = rcu[fc.RouteConfigName]
		}
		// The filter chain will construct a usuable route table on each
		// connection accept. This is done because preinstantiating every route
		// table before it is needed for a connection would potentially lead to
		// a lot of cpu time and memory allocated for route tables that will
		// never be used. There was also a thought to cache this configuration,
		// and reuse it for the next accepted connection. However, this would
		// lead to a lot of code complexity (RDS Updates for a given route name
		// can come it at any time), and connections aren't accepted too often,
		// so this reinstantation of the Route Configuration is an acceptable
		// tradeoff for simplicity.
		vhswi, err := fc.ConstructUsableRouteConfiguration(rc)
		if err != nil {
			l.logger.Warnf("route configuration construction: %v", err)
			conn.Close()
			continue
		}
		return &connWrapper{Conn: conn, filterChain: fc, parent: l, virtualHosts: vhswi}, nil
	}
}