func NewRouter()

in router/core/router.go [245:597]


func NewRouter(opts ...Option) (*Router, error) {
	r := &Router{
		EngineStats: statistics.NewNoopEngineStats(),
	}

	for _, opt := range opts {
		opt(r)
	}

	if r.logger == nil {
		r.logger = zap.NewNop()
	}

	// Default value for graphql path
	if r.graphqlPath == "" {
		r.graphqlPath = "/graphql"
	}

	if r.graphqlWebURL == "" {
		r.graphqlWebURL = r.graphqlPath
	}

	// this is set via the deprecated method
	if !r.playground {
		r.playgroundConfig.Enabled = r.playground
		r.logger.Warn("The playground_enabled option is deprecated. Use the playground.enabled option in the config instead.")
	}
	if r.playgroundPath != "" && r.playgroundPath != "/" {
		r.playgroundConfig.Path = r.playgroundPath
		r.logger.Warn("The playground_path option is deprecated. Use the playground.path option in the config instead.")
	}

	if r.playgroundConfig.Path == "" {
		r.playgroundConfig.Path = "/"
	}

	if r.instanceID == "" {
		r.instanceID = nuid.Next()
	}

	r.processStartTime = time.Now()

	// Create noop tracer and meter to avoid nil pointer panics and to avoid checking for nil everywhere

	r.tracerProvider = sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.NeverSample()))
	r.otlpMeterProvider = sdkmetric.NewMeterProvider()
	r.promMeterProvider = sdkmetric.NewMeterProvider()

	// Default values for trace and metric config

	if r.traceConfig == nil {
		r.traceConfig = rtrace.DefaultConfig(Version)
	}

	if r.metricConfig == nil {
		r.metricConfig = rmetric.DefaultConfig(Version)
	}

	if r.corsOptions == nil {
		r.corsOptions = CorsDefaultOptions()
	}

	if r.subgraphTransportOptions == nil {
		r.subgraphTransportOptions = DefaultSubgraphTransportOptions()
	}

	if r.graphqlMetricsConfig == nil {
		r.graphqlMetricsConfig = DefaultGraphQLMetricsConfig()
	}

	if r.routerTrafficConfig == nil {
		r.routerTrafficConfig = DefaultRouterTrafficConfig()
	}

	if r.fileUploadConfig == nil {
		r.fileUploadConfig = DefaultFileUploadConfig()
	}

	if r.accessController != nil {
		if len(r.accessController.authenticators) == 0 && r.accessController.authenticationRequired {
			r.logger.Warn("authentication is required but no authenticators are configured")
		}
	}

	if r.ipAnonymization == nil {
		r.ipAnonymization = &IPAnonymizationConfig{
			Enabled: true,
			Method:  Redact,
		}
	}

	// Default values for health check paths

	if r.healthCheckPath == "" {
		r.healthCheckPath = "/health"
	}
	if r.readinessCheckPath == "" {
		r.readinessCheckPath = "/health/ready"
	}
	if r.livenessCheckPath == "" {
		r.livenessCheckPath = "/health/live"
	}

	r.headerRules = AddCacheControlPolicyToRules(r.headerRules, r.cacheControlPolicy)
	hr, err := NewHeaderPropagation(r.headerRules)
	if err != nil {
		return nil, err
	}

	if hr.HasRequestRules() {
		r.preOriginHandlers = append(r.preOriginHandlers, hr.OnOriginRequest)
	}
	if hr.HasResponseRules() {
		r.postOriginHandlers = append(r.postOriginHandlers, hr.OnOriginResponse)
	}

	defaultHeaders := []string{
		// Common headers
		"authorization",
		"origin",
		"content-length",
		"content-type",
		// Semi standard client info headers
		"graphql-client-name",
		"graphql-client-version",
		// Apollo client info headers
		"apollographql-client-name",
		"apollographql-client-version",
		// Required for WunderGraph ART
		"x-wg-trace",
		"x-wg-disable-tracing",
		"x-wg-token",
		"x-wg-skip-loader",
		"x-wg-include-query-plan",
		// Required for Trace Context propagation
		"traceparent",
		"tracestate",
		// Required for feature flags
		"x-feature-flag",
	}

	if r.clientHeader.Name != "" {
		defaultHeaders = append(defaultHeaders, r.clientHeader.Name)
	}
	if r.clientHeader.Version != "" {
		defaultHeaders = append(defaultHeaders, r.clientHeader.Version)
	}

	defaultMethods := []string{
		"HEAD", "GET", "POST",
	}
	r.corsOptions.AllowHeaders = stringsx.RemoveDuplicates(append(r.corsOptions.AllowHeaders, defaultHeaders...))
	r.corsOptions.AllowMethods = stringsx.RemoveDuplicates(append(r.corsOptions.AllowMethods, defaultMethods...))

	if r.tlsConfig != nil && r.tlsConfig.Enabled {
		r.baseURL = fmt.Sprintf("https://%s", r.listenAddr)
	} else {
		r.baseURL = fmt.Sprintf("http://%s", r.listenAddr)
	}

	if r.tlsConfig != nil && r.tlsConfig.Enabled {
		if r.tlsConfig.CertFile == "" {
			return nil, errors.New("tls cert file not provided")
		}

		if r.tlsConfig.KeyFile == "" {
			return nil, errors.New("tls key file not provided")
		}

		var caCertPool *x509.CertPool
		clientAuthMode := tls.NoClientCert

		if r.tlsConfig.ClientAuth != nil && r.tlsConfig.ClientAuth.CertFile != "" {
			caCert, err := os.ReadFile(r.tlsConfig.ClientAuth.CertFile)
			if err != nil {
				return nil, fmt.Errorf("failed to read cert file: %w", err)
			}

			// Create a CA an empty cert pool and add the CA cert to it to serve as authority to validate client certs
			caPool := x509.NewCertPool()
			if ok := caPool.AppendCertsFromPEM(caCert); !ok {
				return nil, errors.New("failed to append cert to pool")
			}
			caCertPool = caPool

			if r.tlsConfig.ClientAuth.Required {
				clientAuthMode = tls.RequireAndVerifyClientCert
			} else {
				clientAuthMode = tls.VerifyClientCertIfGiven
			}

			r.logger.Debug("Client auth enabled", zap.String("mode", clientAuthMode.String()))
		}

		// Load the server cert and private key
		cer, err := tls.LoadX509KeyPair(r.tlsConfig.CertFile, r.tlsConfig.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("failed to load tls cert and key: %w", err)
		}

		r.tlsServerConfig = &tls.Config{
			ClientCAs:    caCertPool,
			Certificates: []tls.Certificate{cer},
			ClientAuth:   clientAuthMode,
		}
	}

	if r.traceConfig.Enabled {
		if len(r.traceConfig.Propagators) > 0 {
			propagators, err := rtrace.BuildPropagators(r.traceConfig.Propagators...)
			if err != nil {
				r.logger.Error("creating propagators", zap.Error(err))
				return nil, err
			}

			r.tracePropagators = propagators
		}

		// Add default tracing exporter if needed
		if len(r.traceConfig.Exporters) == 0 && r.traceConfig.TestMemoryExporter == nil {
			if endpoint := otelconfig.DefaultEndpoint(); endpoint != "" {
				r.logger.Debug("Using default trace exporter", zap.String("endpoint", endpoint))
				r.traceConfig.Exporters = append(r.traceConfig.Exporters, &rtrace.ExporterConfig{
					Endpoint: endpoint,
					Exporter: otelconfig.ExporterOLTPHTTP,
					HTTPPath: "/v1/traces",
					Headers:  otelconfig.DefaultEndpointHeaders(r.graphApiToken),
				})
			}
		}

	}

	// Add default metric exporter if none are configured
	if r.metricConfig.OpenTelemetry.Enabled && len(r.metricConfig.OpenTelemetry.Exporters) == 0 && r.metricConfig.OpenTelemetry.TestReader == nil {
		if endpoint := otelconfig.DefaultEndpoint(); endpoint != "" {
			r.logger.Debug("Using default metrics exporter", zap.String("endpoint", endpoint))
			r.metricConfig.OpenTelemetry.Exporters = append(r.metricConfig.OpenTelemetry.Exporters, &rmetric.OpenTelemetryExporter{
				Endpoint: endpoint,
				Exporter: otelconfig.ExporterOLTPHTTP,
				HTTPPath: "/v1/metrics",
				Headers:  otelconfig.DefaultEndpointHeaders(r.graphApiToken),
			})
		}
	}

	var disabledFeatures []string

	// The user might want to start the server with a static config
	// Disable all features that requires a valid graph token and inform the user
	if r.graphApiToken == "" {
		r.graphqlMetricsConfig.Enabled = false

		disabledFeatures = append(disabledFeatures, "Schema Usage Tracking", "Persistent operations")

		if !r.developmentMode {
			disabledFeatures = append(disabledFeatures, "Advanced Request Tracing")
		}

		if r.traceConfig.Enabled {
			defaultExporter := rtrace.DefaultExporter(r.traceConfig)
			if defaultExporter != nil {
				disabledFeatures = append(disabledFeatures, "Cosmo Cloud Tracing")
				defaultExporter.Disabled = true
			}
		}
		if r.metricConfig.OpenTelemetry.Enabled {
			defaultExporter := rmetric.GetDefaultExporter(r.metricConfig)
			if defaultExporter != nil {
				disabledFeatures = append(disabledFeatures, "Cosmo Cloud Metrics")
				defaultExporter.Disabled = true
			}
		}

		r.logger.Warn("No graph token provided. The following Cosmo Cloud features are disabled. Not recommended for Production.",
			zap.Strings("features", disabledFeatures),
		)
	}

	if r.persistedOperationsConfig.Safelist.Enabled && r.automaticPersistedQueriesConfig.Enabled {
		return nil, errors.New("automatic persisted queries and safelist cannot be enabled at the same time (as APQ would permit queries that are not in the safelist)")
	}

	if r.securityConfiguration.DepthLimit != nil {
		r.logger.Warn("The security configuration field 'depth_limit' is deprecated, and will be removed. Use 'security.complexity_limits.depth' instead.")

		if r.securityConfiguration.ComplexityCalculationCache == nil {
			r.securityConfiguration.ComplexityCalculationCache = &config.ComplexityCalculationCache{
				Enabled:   true,
				CacheSize: r.securityConfiguration.DepthLimit.CacheSize,
			}
		}

		if r.securityConfiguration.ComplexityLimits == nil {
			r.securityConfiguration.ComplexityLimits = &config.ComplexityLimits{}
		}
		if r.securityConfiguration.ComplexityLimits.Depth == nil {
			r.securityConfiguration.ComplexityLimits.Depth = &config.ComplexityLimit{
				Enabled:                   r.securityConfiguration.DepthLimit.Enabled,
				Limit:                     r.securityConfiguration.DepthLimit.Limit,
				IgnorePersistedOperations: r.securityConfiguration.DepthLimit.IgnorePersistedOperations,
			}
		} else {
			r.logger.Warn("Ignoring deprecated security configuration field 'depth_limit', in favor of the `security_complexity_limits.depth` configuration")
		}
	}

	if r.developmentMode {
		r.logger.Warn("Development mode enabled. This should only be used for testing purposes")
	}

	if r.healthcheck == nil {
		r.healthcheck = health.New(&health.Options{
			Logger: r.logger,
		})
	}

	for _, source := range r.eventsConfig.Providers.Nats {
		r.logger.Info("Nats Event source enabled", zap.String("provider_id", source.ID))
	}
	for _, source := range r.eventsConfig.Providers.Kafka {
		r.logger.Info("Kafka Event source enabled", zap.String("provider_id", source.ID), zap.Strings("brokers", source.Brokers))
	}

	if !r.engineExecutionConfiguration.EnableNetPoll {
		r.logger.Warn("Net poller is disabled by configuration. Falling back to less efficient connection handling method.")
	} else if err := netpoll.Supported(); err != nil {

		// Disable netPoll if it's not supported. This flag is used everywhere to decide whether to use netPoll or not.
		r.engineExecutionConfiguration.EnableNetPoll = false

		if errors.Is(err, netpoll.ErrUnsupported) {
			r.logger.Warn(
				"Net poller is only available on Linux and MacOS. Falling back to less efficient connection handling method.",
				zap.Error(err),
			)
		} else {
			r.logger.Warn(
				"Net poller is not functional by the environment. Ensure that the system supports epoll/kqueue and that necessary syscall permissions are granted. Falling back to less efficient connection handling method.",
				zap.Error(err),
			)
		}
	}

	if r.hostName == "" {
		r.hostName, err = os.Hostname()
		if err != nil {
			r.logger.Warn("Failed to get hostname", zap.Error(err))
		}
	}

	return r, nil
}