github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/server.go (about)

     1  package gateway
     2  
     3  import (
     4  	"context"
     5  	"html/template"
     6  	"io/ioutil"
     7  	stdlog "log"
     8  	"log/syslog"
     9  	"net"
    10  	"net/http"
    11  	pprof_http "net/http/pprof"
    12  	"os"
    13  	"path/filepath"
    14  	"runtime"
    15  	"runtime/pprof"
    16  	"strconv"
    17  	"strings"
    18  	"sync"
    19  	textTemplate "text/template"
    20  	"time"
    21  
    22  	logstashHook "github.com/bshuster-repo/logrus-logstash-hook"
    23  	"github.com/evalphobia/logrus_sentry"
    24  	graylogHook "github.com/gemnasium/logrus-graylog-hook"
    25  	"github.com/gorilla/mux"
    26  	"github.com/justinas/alice"
    27  	"github.com/lonelycode/osin"
    28  	newrelic "github.com/newrelic/go-agent"
    29  	"github.com/rs/cors"
    30  	uuid "github.com/satori/go.uuid"
    31  	"github.com/sirupsen/logrus"
    32  	logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
    33  	"rsc.io/letsencrypt"
    34  
    35  	"github.com/TykTechnologies/again"
    36  	gas "github.com/TykTechnologies/goautosocket"
    37  	"github.com/TykTechnologies/gorpc"
    38  	"github.com/TykTechnologies/tyk/apidef"
    39  	"github.com/TykTechnologies/tyk/certs"
    40  	"github.com/TykTechnologies/tyk/checkup"
    41  	"github.com/TykTechnologies/tyk/cli"
    42  	"github.com/TykTechnologies/tyk/config"
    43  	"github.com/TykTechnologies/tyk/dnscache"
    44  	"github.com/TykTechnologies/tyk/headers"
    45  	logger "github.com/TykTechnologies/tyk/log"
    46  	"github.com/TykTechnologies/tyk/regexp"
    47  	"github.com/TykTechnologies/tyk/rpc"
    48  	"github.com/TykTechnologies/tyk/storage"
    49  	"github.com/TykTechnologies/tyk/trace"
    50  	"github.com/TykTechnologies/tyk/user"
    51  )
    52  
    53  var (
    54  	log                      = logger.Get()
    55  	mainLog                  = log.WithField("prefix", "main")
    56  	pubSubLog                = log.WithField("prefix", "pub-sub")
    57  	rawLog                   = logger.GetRaw()
    58  	templates                *template.Template
    59  	templatesRaw         *textTemplate.Template
    60  	analytics                RedisAnalyticsHandler
    61  	GlobalEventsJSVM         JSVM
    62  	memProfFile              *os.File
    63  	MainNotifier             RedisNotifier
    64  	DefaultOrgStore          DefaultSessionManager
    65  	DefaultQuotaStore        DefaultSessionManager
    66  	FallbackKeySesionManager = SessionHandler(&DefaultSessionManager{})
    67  	MonitoringHandler        config.TykEventHandler
    68  	RPCListener              RPCStorageHandler
    69  	DashService              DashboardServiceSender
    70  	CertificateManager       *certs.CertificateManager
    71  	NewRelicApplication      newrelic.Application
    72  
    73  	apisMu          sync.RWMutex
    74  	apiSpecs        []*APISpec
    75  	apisByID        = map[string]*APISpec{}
    76  	apisHandlesByID = new(sync.Map)
    77  
    78  	keyGen DefaultKeyGenerator
    79  
    80  	policiesMu   sync.RWMutex
    81  	policiesByID = map[string]user.Policy{}
    82  
    83  	LE_MANAGER  letsencrypt.Manager
    84  	LE_FIRSTRUN bool
    85  
    86  	muNodeID sync.Mutex // guards NodeID
    87  	NodeID   string
    88  
    89  	runningTestsMu sync.RWMutex
    90  	testMode       bool
    91  
    92  	// confPaths is the series of paths to try to use as config files. The
    93  	// first one to exist will be used. If none exists, a default config
    94  	// will be written to the first path in the list.
    95  	//
    96  	// When --conf=foo is used, this will be replaced by []string{"foo"}.
    97  	confPaths = []string{
    98  		"tyk.conf",
    99  		// TODO: add ~/.config/tyk/tyk.conf here?
   100  		"/etc/tyk/tyk.conf",
   101  	}
   102  
   103  	dnsCacheManager dnscache.IDnsCacheManager
   104  )
   105  
   106  const (
   107  	defReadTimeout  = 120 * time.Second
   108  	defWriteTimeout = 120 * time.Second
   109  	appName         = "tyk-gateway"
   110  )
   111  
   112  // SetNodeID writes NodeID safely.
   113  func SetNodeID(nodeID string) {
   114  	muNodeID.Lock()
   115  	NodeID = nodeID
   116  	muNodeID.Unlock()
   117  }
   118  
   119  // GetNodeID reads NodeID safely.
   120  func GetNodeID() string {
   121  	muNodeID.Lock()
   122  	defer muNodeID.Unlock()
   123  	return NodeID
   124  }
   125  
   126  func isRunningTests() bool {
   127  	runningTestsMu.RLock()
   128  	v := testMode
   129  	runningTestsMu.RUnlock()
   130  	return v
   131  }
   132  
   133  func setTestMode(v bool) {
   134  	runningTestsMu.Lock()
   135  	testMode = v
   136  	runningTestsMu.Unlock()
   137  }
   138  
   139  func getApiSpec(apiID string) *APISpec {
   140  	apisMu.RLock()
   141  	spec := apisByID[apiID]
   142  	apisMu.RUnlock()
   143  	return spec
   144  }
   145  
   146  func apisByIDLen() int {
   147  	apisMu.RLock()
   148  	defer apisMu.RUnlock()
   149  	return len(apisByID)
   150  }
   151  
   152  var redisPurgeOnce sync.Once
   153  var rpcPurgeOnce sync.Once
   154  var purgeTicker = time.Tick(time.Second)
   155  var rpcPurgeTicker = time.Tick(10 * time.Second)
   156  
   157  // Create all globals and init connection handlers
   158  func setupGlobals(ctx context.Context) {
   159  
   160  	reloadMu.Lock()
   161  	defer reloadMu.Unlock()
   162  
   163  	dnsCacheManager = dnscache.NewDnsCacheManager(config.Global().DnsCache.MultipleIPsHandleStrategy)
   164  	if config.Global().DnsCache.Enabled {
   165  		dnsCacheManager.InitDNSCaching(
   166  			time.Duration(config.Global().DnsCache.TTL)*time.Second,
   167  			time.Duration(config.Global().DnsCache.CheckInterval)*time.Second)
   168  	}
   169  
   170  	if config.Global().EnableAnalytics && config.Global().Storage.Type != "redis" {
   171  		mainLog.Fatal("Analytics requires Redis Storage backend, please enable Redis in the tyk.conf file.")
   172  	}
   173  
   174  	// Initialise HostCheckerManager only if uptime tests are enabled.
   175  	if !config.Global().UptimeTests.Disable {
   176  		if config.Global().ManagementNode {
   177  			mainLog.Warn("Running Uptime checks in a management node.")
   178  		}
   179  		healthCheckStore := storage.RedisCluster{KeyPrefix: "host-checker:"}
   180  		InitHostCheckManager(&healthCheckStore)
   181  	}
   182  
   183  	redisStore := storage.RedisCluster{KeyPrefix: "apikey-", HashKeys: config.Global().HashKeys}
   184  	FallbackKeySesionManager.Init(&redisStore)
   185  
   186  	if config.Global().EnableAnalytics && analytics.Store == nil {
   187  		globalConf := config.Global()
   188  		globalConf.LoadIgnoredIPs()
   189  		config.SetGlobal(globalConf)
   190  		mainLog.Debug("Setting up analytics DB connection")
   191  
   192  		analyticsStore := storage.RedisCluster{KeyPrefix: "analytics-"}
   193  		analytics.Store = &analyticsStore
   194  		analytics.Init(globalConf)
   195  
   196  		redisPurgeOnce.Do(func() {
   197  			store := storage.RedisCluster{KeyPrefix: "analytics-"}
   198  			redisPurger := RedisPurger{Store: &store}
   199  			go redisPurger.PurgeLoop(purgeTicker)
   200  		})
   201  
   202  		if config.Global().AnalyticsConfig.Type == "rpc" {
   203  			mainLog.Debug("Using RPC cache purge")
   204  
   205  			rpcPurgeOnce.Do(func() {
   206  				store := storage.RedisCluster{KeyPrefix: "analytics-"}
   207  				purger := rpc.Purger{
   208  					Store: &store,
   209  				}
   210  				purger.Connect()
   211  				go purger.PurgeLoop(rpcPurgeTicker)
   212  			})
   213  		}
   214  		go flushNetworkAnalytics(ctx)
   215  	}
   216  
   217  	// Load all the files that have the "error" prefix.
   218  	templatesDir := filepath.Join(config.Global().TemplatePath, "error*")
   219  	templates = template.Must(template.ParseGlob(templatesDir))
   220  	templatesRaw = textTemplate.Must(textTemplate.ParseGlob(templatesDir))
   221  
   222  	CoProcessInit()
   223  
   224  	// Get the notifier ready
   225  	mainLog.Debug("Notifier will not work in hybrid mode")
   226  	mainNotifierStore := &storage.RedisCluster{}
   227  	mainNotifierStore.Connect()
   228  	MainNotifier = RedisNotifier{mainNotifierStore, RedisPubSubChannel}
   229  
   230  	if config.Global().Monitor.EnableTriggerMonitors {
   231  		h := &WebHookHandler{}
   232  		if err := h.Init(config.Global().Monitor.Config); err != nil {
   233  			mainLog.Error("Failed to initialise monitor! ", err)
   234  		} else {
   235  			MonitoringHandler = h
   236  		}
   237  	}
   238  
   239  	if globalConfig := config.Global(); globalConfig.AnalyticsConfig.NormaliseUrls.Enabled {
   240  		mainLog.Info("Setting up analytics normaliser")
   241  		globalConfig.AnalyticsConfig.NormaliseUrls.CompiledPatternSet = initNormalisationPatterns()
   242  		config.SetGlobal(globalConfig)
   243  	}
   244  
   245  	certificateSecret := config.Global().Secret
   246  	if config.Global().Security.PrivateCertificateEncodingSecret != "" {
   247  		certificateSecret = config.Global().Security.PrivateCertificateEncodingSecret
   248  	}
   249  
   250  	CertificateManager = certs.NewCertificateManager(getGlobalStorageHandler("cert-", false), certificateSecret, log)
   251  
   252  	if config.Global().NewRelic.AppName != "" {
   253  		NewRelicApplication = SetupNewRelic()
   254  	}
   255  }
   256  
   257  func buildConnStr(resource string) string {
   258  
   259  	if config.Global().DBAppConfOptions.ConnectionString == "" && config.Global().DisableDashboardZeroConf {
   260  		mainLog.Fatal("Connection string is empty, failing.")
   261  	}
   262  
   263  	if !config.Global().DisableDashboardZeroConf && config.Global().DBAppConfOptions.ConnectionString == "" {
   264  		mainLog.Info("Waiting for zeroconf signal...")
   265  		for config.Global().DBAppConfOptions.ConnectionString == "" {
   266  			time.Sleep(1 * time.Second)
   267  		}
   268  	}
   269  
   270  	return config.Global().DBAppConfOptions.ConnectionString + resource
   271  }
   272  
   273  func syncAPISpecs() (int, error) {
   274  	loader := APIDefinitionLoader{}
   275  	apisMu.Lock()
   276  	defer apisMu.Unlock()
   277  	var s []*APISpec
   278  	if config.Global().UseDBAppConfigs {
   279  		connStr := buildConnStr("/system/apis")
   280  		tmpSpecs, err := loader.FromDashboardService(connStr, config.Global().NodeSecret)
   281  		if err != nil {
   282  			log.Error("failed to load API specs: ", err)
   283  			return 0, err
   284  		}
   285  
   286  		s = tmpSpecs
   287  
   288  		mainLog.Debug("Downloading API Configurations from Dashboard Service")
   289  	} else if config.Global().SlaveOptions.UseRPC {
   290  		mainLog.Debug("Using RPC Configuration")
   291  
   292  		var err error
   293  		s, err = loader.FromRPC(config.Global().SlaveOptions.RPCKey)
   294  		if err != nil {
   295  			return 0, err
   296  		}
   297  	} else {
   298  		s = loader.FromDir(config.Global().AppPath)
   299  	}
   300  
   301  	mainLog.Printf("Detected %v APIs", len(s))
   302  
   303  	if config.Global().AuthOverride.ForceAuthProvider {
   304  		for i := range s {
   305  			s[i].AuthProvider = config.Global().AuthOverride.AuthProvider
   306  		}
   307  	}
   308  
   309  	if config.Global().AuthOverride.ForceSessionProvider {
   310  		for i := range s {
   311  			s[i].SessionProvider = config.Global().AuthOverride.SessionProvider
   312  		}
   313  	}
   314  	var filter []*APISpec
   315  	for _, v := range s {
   316  		if err := v.Validate(); err != nil {
   317  			mainLog.Infof("Skipping loading spec:%q because it failed validation with error:%v", v.Name, err)
   318  			continue
   319  		}
   320  		filter = append(filter, v)
   321  	}
   322  	apiSpecs = filter
   323  
   324  	tlsConfigCache.Flush()
   325  
   326  	return len(apiSpecs), nil
   327  }
   328  
   329  func syncPolicies() (count int, err error) {
   330  	var pols map[string]user.Policy
   331  
   332  	mainLog.Info("Loading policies")
   333  
   334  	switch config.Global().Policies.PolicySource {
   335  	case "service":
   336  		if config.Global().Policies.PolicyConnectionString == "" {
   337  			mainLog.Fatal("No connection string or node ID present. Failing.")
   338  		}
   339  		connStr := config.Global().Policies.PolicyConnectionString
   340  		connStr = connStr + "/system/policies"
   341  
   342  		mainLog.Info("Using Policies from Dashboard Service")
   343  
   344  		pols = LoadPoliciesFromDashboard(connStr, config.Global().NodeSecret, config.Global().Policies.AllowExplicitPolicyID)
   345  	case "rpc":
   346  		mainLog.Debug("Using Policies from RPC")
   347  		pols, err = LoadPoliciesFromRPC(config.Global().SlaveOptions.RPCKey)
   348  	default:
   349  		// this is the only case now where we need a policy record name
   350  		if config.Global().Policies.PolicyRecordName == "" {
   351  			mainLog.Debug("No policy record name defined, skipping...")
   352  			return 0, nil
   353  		}
   354  		pols = LoadPoliciesFromFile(config.Global().Policies.PolicyRecordName)
   355  	}
   356  	mainLog.Infof("Policies found (%d total):", len(pols))
   357  	for id := range pols {
   358  		mainLog.Debugf(" - %s", id)
   359  	}
   360  
   361  	policiesMu.Lock()
   362  	defer policiesMu.Unlock()
   363  	if len(pols) > 0 {
   364  		policiesByID = pols
   365  	}
   366  
   367  	return len(pols), err
   368  }
   369  
   370  // stripSlashes removes any trailing slashes from the request's URL
   371  // path.
   372  func stripSlashes(next http.Handler) http.Handler {
   373  	fn := func(w http.ResponseWriter, r *http.Request) {
   374  		path := r.URL.Path
   375  		if trim := strings.TrimRight(path, "/"); trim != path {
   376  			r2 := *r
   377  			r2.URL.Path = trim
   378  			r = &r2
   379  		}
   380  		next.ServeHTTP(w, r)
   381  	}
   382  	return http.HandlerFunc(fn)
   383  }
   384  
   385  func controlAPICheckClientCertificate(certLevel string, next http.Handler) http.Handler {
   386  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   387  		if config.Global().Security.ControlAPIUseMutualTLS {
   388  			if err := CertificateManager.ValidateRequestCertificate(config.Global().Security.Certificates.ControlAPI, r); err != nil {
   389  				doJSONWrite(w, http.StatusForbidden, apiError(err.Error()))
   390  				return
   391  			}
   392  		}
   393  
   394  		next.ServeHTTP(w, r)
   395  	})
   396  }
   397  
   398  func loadAPIEndpoints(muxer *mux.Router) {
   399  	hostname := config.Global().HostName
   400  	if config.Global().ControlAPIHostname != "" {
   401  		hostname = config.Global().ControlAPIHostname
   402  	}
   403  
   404  	if muxer == nil {
   405  		cp := config.Global().ControlAPIPort
   406  		muxer = defaultProxyMux.router(cp, "")
   407  		if muxer == nil {
   408  			if cp != 0 {
   409  				log.Error("Can't find control API router")
   410  			}
   411  			return
   412  		}
   413  	}
   414  
   415  	r := mux.NewRouter()
   416  	muxer.PathPrefix("/tyk/").Handler(http.StripPrefix("/tyk",
   417  		stripSlashes(checkIsAPIOwner(controlAPICheckClientCertificate("/gateway/client", InstrumentationMW(r)))),
   418  	))
   419  
   420  	if hostname != "" {
   421  		muxer = muxer.Host(hostname).Subrouter()
   422  		mainLog.Info("Control API hostname set: ", hostname)
   423  	}
   424  
   425  	if *cli.HTTPProfile || config.Global().HTTPProfile {
   426  		muxer.HandleFunc("/debug/pprof/profile", pprof_http.Profile)
   427  		muxer.HandleFunc("/debug/pprof/{_:.*}", pprof_http.Index)
   428  	}
   429  
   430  	r.MethodNotAllowedHandler = MethodNotAllowedHandler{}
   431  
   432  	mainLog.Info("Initialising Tyk REST API Endpoints")
   433  
   434  	// set up main API handlers
   435  	r.HandleFunc("/reload/group", groupResetHandler).Methods("GET")
   436  	r.HandleFunc("/reload", resetHandler(nil)).Methods("GET")
   437  
   438  	if !isRPCMode() {
   439  		r.HandleFunc("/org/keys", orgHandler).Methods("GET")
   440  		r.HandleFunc("/org/keys/{keyName:[^/]*}", orgHandler).Methods("POST", "PUT", "GET", "DELETE")
   441  		r.HandleFunc("/keys/policy/{keyName}", policyUpdateHandler).Methods("POST")
   442  		r.HandleFunc("/keys/create", createKeyHandler).Methods("POST")
   443  		r.HandleFunc("/apis", apiHandler).Methods("GET", "POST", "PUT", "DELETE")
   444  		r.HandleFunc("/apis/{apiID}", apiHandler).Methods("GET", "POST", "PUT", "DELETE")
   445  		r.HandleFunc("/health", healthCheckhandler).Methods("GET")
   446  		r.HandleFunc("/oauth/clients/create", createOauthClient).Methods("POST")
   447  		r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}", oAuthClientHandler).Methods("PUT")
   448  		r.HandleFunc("/oauth/clients/apis/{appID}", getApisForOauthApp).Queries("orgID", "{[0-9]*?}").Methods("GET")
   449  		r.HandleFunc("/oauth/refresh/{keyName}", invalidateOauthRefresh).Methods("DELETE")
   450  		r.HandleFunc("/cache/{apiID}", invalidateCacheHandler).Methods("DELETE")
   451  		r.HandleFunc("/oauth/revoke", RevokeTokenHandler).Methods("POST")
   452  		r.HandleFunc("/oauth/revoke_all", RevokeAllTokensHandler).Methods("POST")
   453  
   454  	} else {
   455  		mainLog.Info("Node is slaved, REST API minimised")
   456  	}
   457  
   458  	r.HandleFunc("/debug", traceHandler).Methods("POST")
   459  
   460  	r.HandleFunc("/keys", keyHandler).Methods("POST", "PUT", "GET", "DELETE")
   461  	r.HandleFunc("/keys/preview", previewKeyHandler).Methods("POST")
   462  	r.HandleFunc("/keys/{keyName:[^/]*}", keyHandler).Methods("POST", "PUT", "GET", "DELETE")
   463  	r.HandleFunc("/certs", certHandler).Methods("POST", "GET")
   464  	r.HandleFunc("/certs/{certID:[^/]*}", certHandler).Methods("POST", "GET", "DELETE")
   465  	r.HandleFunc("/oauth/clients/{apiID}", oAuthClientHandler).Methods("GET", "DELETE")
   466  	r.HandleFunc("/oauth/clients/{apiID}/{keyName:[^/]*}", oAuthClientHandler).Methods("GET", "DELETE")
   467  	r.HandleFunc("/oauth/clients/{apiID}/{keyName}/tokens", oAuthClientTokensHandler).Methods("GET")
   468  
   469  	mainLog.Debug("Loaded API Endpoints")
   470  }
   471  
   472  // checkIsAPIOwner will ensure that the accessor of the tyk API has the
   473  // correct security credentials - this is a shared secret between the
   474  // client and the owner and is set in the tyk.conf file. This should
   475  // never be made public!
   476  func checkIsAPIOwner(next http.Handler) http.Handler {
   477  	secret := config.Global().Secret
   478  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   479  		tykAuthKey := r.Header.Get(headers.XTykAuthorization)
   480  		if tykAuthKey != secret {
   481  			// Error
   482  			mainLog.Warning("Attempted administrative access with invalid or missing key!")
   483  
   484  			doJSONWrite(w, http.StatusForbidden, apiError("Attempted administrative access with invalid or missing key!"))
   485  			return
   486  		}
   487  		next.ServeHTTP(w, r)
   488  	})
   489  }
   490  
   491  func generateOAuthPrefix(apiID string) string {
   492  	return "oauth-data." + apiID + "."
   493  }
   494  
   495  // Create API-specific OAuth handlers and respective auth servers
   496  func addOAuthHandlers(spec *APISpec, muxer *mux.Router) *OAuthManager {
   497  	var pathSeparator string
   498  	if !strings.HasSuffix(spec.Proxy.ListenPath, "/") {
   499  		pathSeparator = "/"
   500  	}
   501  
   502  	apiAuthorizePath := spec.Proxy.ListenPath + pathSeparator + "tyk/oauth/authorize-client{_:/?}"
   503  	clientAuthPath := spec.Proxy.ListenPath + pathSeparator + "oauth/authorize{_:/?}"
   504  	clientAccessPath := spec.Proxy.ListenPath + pathSeparator + "oauth/token{_:/?}"
   505  	revokeToken := spec.Proxy.ListenPath + pathSeparator + "oauth/revoke"
   506  	revokeAllTokens := spec.Proxy.ListenPath + pathSeparator + "oauth/revoke_all"
   507  
   508  	serverConfig := osin.NewServerConfig()
   509  
   510  	if config.Global().OauthErrorStatusCode != 0 {
   511  		serverConfig.ErrorStatusCode = config.Global().OauthErrorStatusCode
   512  	} else {
   513  		serverConfig.ErrorStatusCode = http.StatusForbidden
   514  	}
   515  
   516  	serverConfig.AllowedAccessTypes = spec.Oauth2Meta.AllowedAccessTypes
   517  	serverConfig.AllowedAuthorizeTypes = spec.Oauth2Meta.AllowedAuthorizeTypes
   518  	serverConfig.RedirectUriSeparator = config.Global().OauthRedirectUriSeparator
   519  
   520  	prefix := generateOAuthPrefix(spec.APIID)
   521  	storageManager := getGlobalStorageHandler(prefix, false)
   522  	storageManager.Connect()
   523  	osinStorage := &RedisOsinStorageInterface{storageManager, spec.SessionManager, &storage.RedisCluster{KeyPrefix: prefix, HashKeys: false}, spec.OrgID}
   524  	osinServer := TykOsinNewServer(serverConfig, osinStorage)
   525  
   526  	oauthManager := OAuthManager{spec, osinServer}
   527  	oauthHandlers := OAuthHandlers{oauthManager}
   528  
   529  	muxer.Handle(apiAuthorizePath, checkIsAPIOwner(allowMethods(oauthHandlers.HandleGenerateAuthCodeData, "POST")))
   530  	muxer.HandleFunc(clientAuthPath, allowMethods(oauthHandlers.HandleAuthorizePassthrough, "GET", "POST"))
   531  	muxer.HandleFunc(clientAccessPath, addSecureAndCacheHeaders(allowMethods(oauthHandlers.HandleAccessRequest, "GET", "POST")))
   532  	muxer.HandleFunc(revokeToken, oauthHandlers.HandleRevokeToken)
   533  	muxer.HandleFunc(revokeAllTokens, oauthHandlers.HandleRevokeAllTokens)
   534  	return &oauthManager
   535  }
   536  
   537  func addBatchEndpoint(spec *APISpec, muxer *mux.Router) {
   538  	mainLog.Debug("Batch requests enabled for API")
   539  	apiBatchPath := spec.Proxy.ListenPath + "tyk/batch/"
   540  	batchHandler := BatchRequestHandler{API: spec}
   541  	muxer.HandleFunc(apiBatchPath, batchHandler.HandleBatchRequest)
   542  }
   543  
   544  func loadCustomMiddleware(spec *APISpec) ([]string, apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, []apidef.MiddlewareDefinition, apidef.MiddlewareDriver) {
   545  	mwPaths := []string{}
   546  	var mwAuthCheckFunc apidef.MiddlewareDefinition
   547  	mwPreFuncs := []apidef.MiddlewareDefinition{}
   548  	mwPostFuncs := []apidef.MiddlewareDefinition{}
   549  	mwPostKeyAuthFuncs := []apidef.MiddlewareDefinition{}
   550  	mwDriver := apidef.OttoDriver
   551  
   552  	// Set AuthCheck hook
   553  	if spec.CustomMiddleware.AuthCheck.Name != "" {
   554  		mwAuthCheckFunc = spec.CustomMiddleware.AuthCheck
   555  		if spec.CustomMiddleware.AuthCheck.Path != "" {
   556  			// Feed a JS file to Otto
   557  			mwPaths = append(mwPaths, spec.CustomMiddleware.AuthCheck.Path)
   558  		}
   559  	}
   560  
   561  	// Load from the configuration
   562  	for _, mwObj := range spec.CustomMiddleware.Pre {
   563  		mwPaths = append(mwPaths, mwObj.Path)
   564  		mwPreFuncs = append(mwPreFuncs, mwObj)
   565  		mainLog.Debug("Loading custom PRE-PROCESSOR middleware: ", mwObj.Name)
   566  	}
   567  	for _, mwObj := range spec.CustomMiddleware.Post {
   568  		mwPaths = append(mwPaths, mwObj.Path)
   569  		mwPostFuncs = append(mwPostFuncs, mwObj)
   570  		mainLog.Debug("Loading custom POST-PROCESSOR middleware: ", mwObj.Name)
   571  	}
   572  
   573  	// Load from folders
   574  	for _, folder := range [...]struct {
   575  		name   string
   576  		single *apidef.MiddlewareDefinition
   577  		slice  *[]apidef.MiddlewareDefinition
   578  	}{
   579  		{name: "pre", slice: &mwPreFuncs},
   580  		{name: "auth", single: &mwAuthCheckFunc},
   581  		{name: "post_auth", slice: &mwPostKeyAuthFuncs},
   582  		{name: "post", slice: &mwPostFuncs},
   583  	} {
   584  		globPath := filepath.Join(config.Global().MiddlewarePath, spec.APIID, folder.name, "*.js")
   585  		paths, _ := filepath.Glob(globPath)
   586  		for _, path := range paths {
   587  			mainLog.Debug("Loading file middleware from ", path)
   588  
   589  			mwDef := apidef.MiddlewareDefinition{
   590  				Name: strings.Split(filepath.Base(path), ".")[0],
   591  				Path: path,
   592  			}
   593  			mainLog.Debug("-- Middleware name ", mwDef.Name)
   594  			mwDef.RequireSession = strings.HasSuffix(mwDef.Name, "_with_session")
   595  			if mwDef.RequireSession {
   596  				switch folder.name {
   597  				case "post_auth", "post":
   598  					mainLog.Debug("-- Middleware requires session")
   599  				default:
   600  					mainLog.Warning("Middleware requires session, but isn't post-auth: ", mwDef.Name)
   601  				}
   602  			}
   603  			mwPaths = append(mwPaths, path)
   604  			if folder.single != nil {
   605  				*folder.single = mwDef
   606  			} else {
   607  				*folder.slice = append(*folder.slice, mwDef)
   608  			}
   609  		}
   610  	}
   611  
   612  	// Set middleware driver, defaults to OttoDriver
   613  	if spec.CustomMiddleware.Driver != "" {
   614  		mwDriver = spec.CustomMiddleware.Driver
   615  	}
   616  
   617  	// Load PostAuthCheck hooks
   618  	for _, mwObj := range spec.CustomMiddleware.PostKeyAuth {
   619  		if mwObj.Path != "" {
   620  			// Otto files are specified here
   621  			mwPaths = append(mwPaths, mwObj.Path)
   622  		}
   623  		mwPostKeyAuthFuncs = append(mwPostKeyAuthFuncs, mwObj)
   624  	}
   625  
   626  	return mwPaths, mwAuthCheckFunc, mwPreFuncs, mwPostFuncs, mwPostKeyAuthFuncs, mwDriver
   627  }
   628  
   629  func createResponseMiddlewareChain(spec *APISpec) {
   630  	// Create the response processors
   631  
   632  	responseChain := make([]TykResponseHandler, len(spec.ResponseProcessors))
   633  	for i, processorDetail := range spec.ResponseProcessors {
   634  		processor := responseProcessorByName(processorDetail.Name)
   635  		if processor == nil {
   636  			mainLog.Error("No such processor: ", processorDetail.Name)
   637  			return
   638  		}
   639  		if err := processor.Init(processorDetail.Options, spec); err != nil {
   640  			mainLog.Debug("Failed to init processor: ", err)
   641  		}
   642  		mainLog.Debug("Loading Response processor: ", processorDetail.Name)
   643  		responseChain[i] = processor
   644  	}
   645  	spec.ResponseChain = responseChain
   646  }
   647  
   648  func handleCORS(chain *[]alice.Constructor, spec *APISpec) {
   649  
   650  	if spec.CORS.Enable {
   651  		mainLog.Debug("CORS ENABLED")
   652  		c := cors.New(cors.Options{
   653  			AllowedOrigins:     spec.CORS.AllowedOrigins,
   654  			AllowedMethods:     spec.CORS.AllowedMethods,
   655  			AllowedHeaders:     spec.CORS.AllowedHeaders,
   656  			ExposedHeaders:     spec.CORS.ExposedHeaders,
   657  			AllowCredentials:   spec.CORS.AllowCredentials,
   658  			MaxAge:             spec.CORS.MaxAge,
   659  			OptionsPassthrough: spec.CORS.OptionsPassthrough,
   660  			Debug:              spec.CORS.Debug,
   661  		})
   662  
   663  		*chain = append(*chain, c.Handler)
   664  	}
   665  }
   666  
   667  func isRPCMode() bool {
   668  	return config.Global().AuthOverride.ForceAuthProvider &&
   669  		config.Global().AuthOverride.AuthProvider.StorageEngine == RPCStorageEngine
   670  }
   671  
   672  func rpcReloadLoop(rpcKey string) {
   673  	for {
   674  		RPCListener.CheckForReload(rpcKey)
   675  	}
   676  }
   677  
   678  var reloadMu sync.Mutex
   679  
   680  func DoReload() {
   681  	reloadMu.Lock()
   682  	defer reloadMu.Unlock()
   683  
   684  	// Initialize/reset the JSVM
   685  	if config.Global().EnableJSVM {
   686  		GlobalEventsJSVM.Init(nil, logrus.NewEntry(log))
   687  	}
   688  
   689  	// Load the API Policies
   690  	if _, err := syncPolicies(); err != nil {
   691  		mainLog.Error("Error during syncing policies:", err.Error())
   692  		return
   693  	}
   694  
   695  	// load the specs
   696  	if count, err := syncAPISpecs(); err != nil {
   697  		mainLog.Error("Error during syncing apis:", err.Error())
   698  		return
   699  	} else {
   700  		// skip re-loading only if dashboard service reported 0 APIs
   701  		// and current registry had 0 APIs
   702  		if count == 0 && apisByIDLen() == 0 {
   703  			mainLog.Warning("No API Definitions found, not reloading")
   704  			return
   705  		}
   706  	}
   707  	loadGlobalApps()
   708  
   709  	mainLog.Info("API reload complete")
   710  }
   711  
   712  // shouldReload returns true if we should perform any reload. Reloads happens if
   713  // we have reload callback queued.
   714  func shouldReload() ([]func(), bool) {
   715  	requeueLock.Lock()
   716  	defer requeueLock.Unlock()
   717  	if len(requeue) == 0 {
   718  		return nil, false
   719  	}
   720  	n := requeue
   721  	requeue = []func(){}
   722  	return n, true
   723  }
   724  
   725  func reloadLoop(ctx context.Context, tick <-chan time.Time, complete ...func()) {
   726  	for {
   727  		select {
   728  		case <-ctx.Done():
   729  			return
   730  		// We don't check for reload right away as the gateway peroms this on the
   731  		// startup sequence. We expect to start checking on the first tick after the
   732  		// gateway is up and running.
   733  		case <-tick:
   734  			cb, ok := shouldReload()
   735  			if !ok {
   736  				continue
   737  			}
   738  			start := time.Now()
   739  			mainLog.Info("reload: initiating")
   740  			DoReload()
   741  			mainLog.Info("reload: complete")
   742  			mainLog.Info("Initiating coprocess reload")
   743  			DoCoprocessReload()
   744  			mainLog.Info("coprocess reload complete")
   745  			for _, c := range cb {
   746  				// most of the callbacks are nil, we don't want to execute nil functions to
   747  				// avoid panics.
   748  				if c != nil {
   749  					c()
   750  				}
   751  			}
   752  			if len(complete) != 0 {
   753  				complete[0]()
   754  			}
   755  			mainLog.Infof("reload: cycle completed in %v", time.Since(start))
   756  		}
   757  	}
   758  }
   759  
   760  // reloadQueue is used by reloadURLStructure to queue a reload. It's not
   761  // buffered, as reloadQueueLoop should pick these up immediately.
   762  var reloadQueue = make(chan func())
   763  
   764  var requeueLock sync.Mutex
   765  
   766  // This is a list of callbacks to execute on the next reload. It is protected by
   767  // requeueLock for concurrent use.
   768  var requeue []func()
   769  
   770  func reloadQueueLoop(ctx context.Context, cb ...func()) {
   771  	for {
   772  		select {
   773  		case <-ctx.Done():
   774  			return
   775  		case fn := <-reloadQueue:
   776  			requeueLock.Lock()
   777  			requeue = append(requeue, fn)
   778  			requeueLock.Unlock()
   779  			mainLog.Info("Reload queued")
   780  			if len(cb) != 0 {
   781  				cb[0]()
   782  			}
   783  		}
   784  	}
   785  }
   786  
   787  // reloadURLStructure will queue an API reload. The reload will
   788  // eventually create a new muxer, reload all the app configs for an
   789  // instance and then replace the DefaultServeMux with the new one. This
   790  // enables a reconfiguration to take place without stopping any requests
   791  // from being handled.
   792  //
   793  // done will be called when the reload is finished. Note that if a
   794  // reload is already queued, another won't be queued, but done will
   795  // still be called when said queued reload is finished.
   796  func reloadURLStructure(done func()) {
   797  	reloadQueue <- done
   798  }
   799  
   800  func setupLogger() {
   801  	if config.Global().UseSentry {
   802  		mainLog.Debug("Enabling Sentry support")
   803  
   804  		logLevel := []logrus.Level{}
   805  
   806  		if config.Global().SentryLogLevel == "" {
   807  			logLevel = []logrus.Level{
   808  				logrus.PanicLevel,
   809  				logrus.FatalLevel,
   810  				logrus.ErrorLevel,
   811  			}
   812  		} else if config.Global().SentryLogLevel == "panic" {
   813  			logLevel = []logrus.Level{
   814  				logrus.PanicLevel,
   815  				logrus.FatalLevel,
   816  			}
   817  		}
   818  
   819  		hook, err := logrus_sentry.NewSentryHook(config.Global().SentryCode, logLevel)
   820  
   821  		hook.Timeout = 0
   822  
   823  		if err == nil {
   824  			log.Hooks.Add(hook)
   825  			rawLog.Hooks.Add(hook)
   826  		}
   827  		mainLog.Debug("Sentry hook active")
   828  	}
   829  
   830  	if config.Global().UseSyslog {
   831  		mainLog.Debug("Enabling Syslog support")
   832  		hook, err := logrus_syslog.NewSyslogHook(config.Global().SyslogTransport,
   833  			config.Global().SyslogNetworkAddr,
   834  			syslog.LOG_INFO, "")
   835  
   836  		if err == nil {
   837  			log.Hooks.Add(hook)
   838  			rawLog.Hooks.Add(hook)
   839  		}
   840  		mainLog.Debug("Syslog hook active")
   841  	}
   842  
   843  	if config.Global().UseGraylog {
   844  		mainLog.Debug("Enabling Graylog support")
   845  		hook := graylogHook.NewGraylogHook(config.Global().GraylogNetworkAddr,
   846  			map[string]interface{}{"tyk-module": "gateway"})
   847  
   848  		log.Hooks.Add(hook)
   849  		rawLog.Hooks.Add(hook)
   850  
   851  		mainLog.Debug("Graylog hook active")
   852  	}
   853  
   854  	if config.Global().UseLogstash {
   855  		mainLog.Debug("Enabling Logstash support")
   856  
   857  		var hook *logstashHook.Hook
   858  		var err error
   859  		var conn net.Conn
   860  		if config.Global().LogstashTransport == "udp" {
   861  			mainLog.Debug("Connecting to Logstash with udp")
   862  			hook, err = logstashHook.NewHook(config.Global().LogstashTransport,
   863  				config.Global().LogstashNetworkAddr,
   864  				appName)
   865  		} else {
   866  			mainLog.Debugf("Connecting to Logstash with %s", config.Global().LogstashTransport)
   867  			conn, err = gas.Dial(config.Global().LogstashTransport, config.Global().LogstashNetworkAddr)
   868  			if err == nil {
   869  				hook, err = logstashHook.NewHookWithConn(conn, appName)
   870  			}
   871  		}
   872  
   873  		if err != nil {
   874  			log.Errorf("Error making connection for logstash: %v", err)
   875  		} else {
   876  			log.Hooks.Add(hook)
   877  			rawLog.Hooks.Add(hook)
   878  			mainLog.Debug("Logstash hook active")
   879  		}
   880  	}
   881  
   882  	if config.Global().UseRedisLog {
   883  		hook := newRedisHook()
   884  		log.Hooks.Add(hook)
   885  		rawLog.Hooks.Add(hook)
   886  
   887  		mainLog.Debug("Redis log hook active")
   888  	}
   889  }
   890  
   891  func initialiseSystem(ctx context.Context) error {
   892  	if isRunningTests() && os.Getenv("TYK_LOGLEVEL") == "" {
   893  		// `go test` without TYK_LOGLEVEL set defaults to no log
   894  		// output
   895  		log.Level = logrus.ErrorLevel
   896  		log.Out = ioutil.Discard
   897  		gorpc.SetErrorLogger(func(string, ...interface{}) {})
   898  		stdlog.SetOutput(ioutil.Discard)
   899  	} else if *cli.DebugMode {
   900  		log.Level = logrus.DebugLevel
   901  		mainLog.Debug("Enabling debug-level output")
   902  	}
   903  
   904  	if *cli.Conf != "" {
   905  		mainLog.Debugf("Using %s for configuration", *cli.Conf)
   906  		confPaths = []string{*cli.Conf}
   907  	} else {
   908  		mainLog.Debug("No configuration file defined, will try to use default (tyk.conf)")
   909  	}
   910  
   911  	mainLog.Infof("Tyk API Gateway %s", VERSION)
   912  
   913  	if !isRunningTests() {
   914  		globalConf := config.Config{}
   915  		if err := config.Load(confPaths, &globalConf); err != nil {
   916  			return err
   917  		}
   918  		if globalConf.PIDFileLocation == "" {
   919  			globalConf.PIDFileLocation = "/var/run/tyk/tyk-gateway.pid"
   920  		}
   921  		// It's necessary to set global conf before and after calling afterConfSetup as global conf
   922  		// is being used by dependencies of the even handler init and then conf is modified again.
   923  		config.SetGlobal(globalConf)
   924  		afterConfSetup(&globalConf)
   925  		config.SetGlobal(globalConf)
   926  	}
   927  
   928  	overrideTykErrors()
   929  
   930  	if os.Getenv("TYK_LOGLEVEL") == "" && !*cli.DebugMode {
   931  		level := strings.ToLower(config.Global().LogLevel)
   932  		switch level {
   933  		case "", "info":
   934  			// default, do nothing
   935  		case "error":
   936  			log.Level = logrus.ErrorLevel
   937  		case "warn":
   938  			log.Level = logrus.WarnLevel
   939  		case "debug":
   940  			log.Level = logrus.DebugLevel
   941  		default:
   942  			mainLog.Fatalf("Invalid log level %q specified in config, must be error, warn, debug or info. ", level)
   943  		}
   944  	}
   945  
   946  	if config.Global().Storage.Type != "redis" {
   947  		mainLog.Fatal("Redis connection details not set, please ensure that the storage type is set to Redis and that the connection parameters are correct.")
   948  	}
   949  
   950  	// suply rpc client globals to join it main loging and instrumentation sub systems
   951  	rpc.Log = log
   952  	rpc.Instrument = instrument
   953  
   954  	setupGlobals(ctx)
   955  
   956  	globalConf := config.Global()
   957  
   958  	if *cli.Port != "" {
   959  		portNum, err := strconv.Atoi(*cli.Port)
   960  		if err != nil {
   961  			mainLog.Error("Port specified in flags must be a number: ", err)
   962  		} else {
   963  			globalConf.ListenPort = portNum
   964  			config.SetGlobal(globalConf)
   965  		}
   966  	}
   967  
   968  	// Enable all the loggers
   969  	setupLogger()
   970  
   971  	mainLog.Info("PIDFile location set to: ", config.Global().PIDFileLocation)
   972  
   973  	if err := writePIDFile(); err != nil {
   974  		mainLog.Error("Failed to write PIDFile: ", err)
   975  	}
   976  
   977  	if globalConf.UseDBAppConfigs && globalConf.Policies.PolicySource != config.DefaultDashPolicySource {
   978  		globalConf.Policies.PolicySource = config.DefaultDashPolicySource
   979  		globalConf.Policies.PolicyConnectionString = globalConf.DBAppConfOptions.ConnectionString
   980  		if globalConf.Policies.PolicyRecordName == "" {
   981  			globalConf.Policies.PolicyRecordName = config.DefaultDashPolicyRecordName
   982  		}
   983  	}
   984  
   985  	getHostDetails()
   986  	setupInstrumentation()
   987  
   988  	if config.Global().HttpServerOptions.UseLE_SSL {
   989  		go StartPeriodicStateBackup(&LE_MANAGER)
   990  	}
   991  	return nil
   992  }
   993  
   994  func writePIDFile() error {
   995  	file := config.Global().PIDFileLocation
   996  	if err := os.MkdirAll(filepath.Dir(file), 0755); err != nil {
   997  		return err
   998  	}
   999  	pid := strconv.Itoa(os.Getpid())
  1000  	return ioutil.WriteFile(file, []byte(pid), 0600)
  1001  }
  1002  
  1003  func readPIDFromFile() (int, error) {
  1004  	b, err := ioutil.ReadFile(config.Global().PIDFileLocation)
  1005  	if err != nil {
  1006  		return 0, err
  1007  	}
  1008  	return strconv.Atoi(string(b))
  1009  }
  1010  
  1011  // afterConfSetup takes care of non-sensical config values (such as zero
  1012  // timeouts) and sets up a few globals that depend on the config.
  1013  func afterConfSetup(conf *config.Config) {
  1014  	if conf.SlaveOptions.CallTimeout == 0 {
  1015  		conf.SlaveOptions.CallTimeout = 30
  1016  	}
  1017  
  1018  	if conf.SlaveOptions.PingTimeout == 0 {
  1019  		conf.SlaveOptions.PingTimeout = 60
  1020  	}
  1021  
  1022  	rpc.GlobalRPCPingTimeout = time.Second * time.Duration(conf.SlaveOptions.PingTimeout)
  1023  	rpc.GlobalRPCCallTimeout = time.Second * time.Duration(conf.SlaveOptions.CallTimeout)
  1024  	initGenericEventHandlers(conf)
  1025  	regexp.ResetCache(time.Second*time.Duration(conf.RegexpCacheExpire), !conf.DisableRegexpCache)
  1026  
  1027  	if conf.HealthCheckEndpointName == "" {
  1028  		conf.HealthCheckEndpointName = "hello"
  1029  	}
  1030  }
  1031  
  1032  var hostDetails struct {
  1033  	Hostname string
  1034  	PID      int
  1035  }
  1036  
  1037  func getHostDetails() {
  1038  	var err error
  1039  	if hostDetails.PID, err = readPIDFromFile(); err != nil {
  1040  		mainLog.Error("Failed ot get host pid: ", err)
  1041  	}
  1042  	if hostDetails.Hostname, err = os.Hostname(); err != nil {
  1043  		mainLog.Error("Failed ot get hostname: ", err)
  1044  	}
  1045  }
  1046  
  1047  func getGlobalStorageHandler(keyPrefix string, hashKeys bool) storage.Handler {
  1048  	if config.Global().SlaveOptions.UseRPC {
  1049  		return &RPCStorageHandler{
  1050  			KeyPrefix: keyPrefix,
  1051  			HashKeys:  hashKeys,
  1052  		}
  1053  	}
  1054  	return &storage.RedisCluster{KeyPrefix: keyPrefix, HashKeys: hashKeys}
  1055  }
  1056  
  1057  func Start() {
  1058  	ctx, cancel := context.WithCancel(context.Background())
  1059  	defer cancel()
  1060  	cli.Init(VERSION, confPaths)
  1061  	cli.Parse()
  1062  	// Stop gateway process if not running in "start" mode:
  1063  	if !cli.DefaultMode {
  1064  		os.Exit(0)
  1065  	}
  1066  
  1067  	SetNodeID("solo-" + uuid.NewV4().String())
  1068  
  1069  	if err := initialiseSystem(ctx); err != nil {
  1070  		mainLog.Fatalf("Error initialising system: %v", err)
  1071  	}
  1072  
  1073  	if config.Global().ControlAPIPort == 0 {
  1074  		mainLog.Warn("The control_api_port should be changed for production")
  1075  	}
  1076  	setupPortsWhitelist()
  1077  
  1078  	onFork := func() {
  1079  		mainLog.Warning("PREPARING TO FORK")
  1080  
  1081  		// if controlListener != nil {
  1082  		// 	if err := controlListener.Close(); err != nil {
  1083  		// 		mainLog.Error("Control listen handler exit: ", err)
  1084  		// 	}
  1085  		// 	mainLog.Info("Control listen closed")
  1086  		// }
  1087  
  1088  		if config.Global().UseDBAppConfigs {
  1089  			mainLog.Info("Stopping heartbeat")
  1090  			DashService.StopBeating()
  1091  			mainLog.Info("Waiting to de-register")
  1092  			time.Sleep(10 * time.Second)
  1093  
  1094  			os.Setenv("TYK_SERVICE_NONCE", ServiceNonce)
  1095  			os.Setenv("TYK_SERVICE_NODEID", GetNodeID())
  1096  		}
  1097  	}
  1098  	err := again.ListenFrom(&defaultProxyMux.again, onFork)
  1099  	if err != nil {
  1100  		mainLog.Errorf("Initializing again %s", err)
  1101  	}
  1102  	checkup.Run(config.Global())
  1103  	if tr := config.Global().Tracer; tr.Enabled {
  1104  		trace.SetupTracing(tr.Name, tr.Options)
  1105  		trace.SetLogger(mainLog)
  1106  		defer trace.Close()
  1107  	}
  1108  
  1109  	start(ctx)
  1110  
  1111  	// Wait while Redis connection pools are ready before start serving traffic
  1112  	if !storage.IsConnected() {
  1113  		mainLog.Fatal("Redis connection pools are not ready. Exiting...")
  1114  	}
  1115  	mainLog.Info("Redis connection pools are ready")
  1116  
  1117  	if *cli.MemProfile {
  1118  		mainLog.Debug("Memory profiling active")
  1119  		var err error
  1120  		if memProfFile, err = os.Create("tyk.mprof"); err != nil {
  1121  			panic(err)
  1122  		}
  1123  		defer memProfFile.Close()
  1124  	}
  1125  	if *cli.CPUProfile {
  1126  		mainLog.Info("Cpu profiling active")
  1127  		cpuProfFile, err := os.Create("tyk.prof")
  1128  		if err != nil {
  1129  			panic(err)
  1130  		}
  1131  		pprof.StartCPUProfile(cpuProfFile)
  1132  		defer pprof.StopCPUProfile()
  1133  	}
  1134  	if *cli.BlockProfile {
  1135  		mainLog.Info("Block profiling active")
  1136  		runtime.SetBlockProfileRate(1)
  1137  	}
  1138  	if *cli.MutexProfile {
  1139  		mainLog.Info("Mutex profiling active")
  1140  		runtime.SetMutexProfileFraction(1)
  1141  	}
  1142  
  1143  	// TODO: replace goagain with something that support multiple listeners
  1144  	// Example: https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/
  1145  	startServer()
  1146  
  1147  	if again.Child() {
  1148  		// This is a child process, we need to murder the parent now
  1149  		if err := again.Kill(); err != nil {
  1150  			mainLog.Fatal(err)
  1151  		}
  1152  	}
  1153  	again.Wait(&defaultProxyMux.again)
  1154  	mainLog.Info("Stop signal received.")
  1155  	if err := defaultProxyMux.again.Close(); err != nil {
  1156  		mainLog.Error("Closing listeners: ", err)
  1157  	}
  1158  	// stop analytics workers
  1159  	if config.Global().EnableAnalytics && analytics.Store == nil {
  1160  		analytics.Stop()
  1161  	}
  1162  
  1163  	// write pprof profiles
  1164  	writeProfiles()
  1165  
  1166  	if config.Global().UseDBAppConfigs {
  1167  		mainLog.Info("Stopping heartbeat...")
  1168  		DashService.StopBeating()
  1169  		time.Sleep(2 * time.Second)
  1170  		DashService.DeRegister()
  1171  	}
  1172  
  1173  	mainLog.Info("Terminating.")
  1174  
  1175  	time.Sleep(time.Second)
  1176  }
  1177  
  1178  func writeProfiles() {
  1179  	if *cli.BlockProfile {
  1180  		f, err := os.Create("tyk.blockprof")
  1181  		if err != nil {
  1182  			panic(err)
  1183  		}
  1184  		if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
  1185  			panic(err)
  1186  		}
  1187  		f.Close()
  1188  	}
  1189  	if *cli.MutexProfile {
  1190  		f, err := os.Create("tyk.mutexprof")
  1191  		if err != nil {
  1192  			panic(err)
  1193  		}
  1194  		if err = pprof.Lookup("mutex").WriteTo(f, 0); err != nil {
  1195  			panic(err)
  1196  		}
  1197  		f.Close()
  1198  	}
  1199  }
  1200  
  1201  func start(ctx context.Context) {
  1202  	// Set up a default org manager so we can traverse non-live paths
  1203  	if !config.Global().SupressDefaultOrgStore {
  1204  		mainLog.Debug("Initialising default org store")
  1205  		DefaultOrgStore.Init(getGlobalStorageHandler("orgkey.", false))
  1206  		//DefaultQuotaStore.Init(getGlobalStorageHandler(CloudHandler, "orgkey.", false))
  1207  		DefaultQuotaStore.Init(getGlobalStorageHandler("orgkey.", false))
  1208  	}
  1209  
  1210  	// Start listening for reload messages
  1211  	if !config.Global().SuppressRedisSignalReload {
  1212  		go startPubSubLoop()
  1213  	}
  1214  
  1215  	if slaveOptions := config.Global().SlaveOptions; slaveOptions.UseRPC {
  1216  		mainLog.Debug("Starting RPC reload listener")
  1217  		RPCListener = RPCStorageHandler{
  1218  			KeyPrefix:        "rpc.listener.",
  1219  			SuppressRegister: true,
  1220  		}
  1221  
  1222  		RPCListener.Connect()
  1223  		go rpcReloadLoop(slaveOptions.RPCKey)
  1224  		go RPCListener.StartRPCKeepaliveWatcher()
  1225  		go RPCListener.StartRPCLoopCheck(slaveOptions.RPCKey)
  1226  	}
  1227  
  1228  	// 1s is the minimum amount of time between hot reloads. The
  1229  	// interval counts from the start of one reload to the next.
  1230  	go reloadLoop(ctx, time.Tick(time.Second))
  1231  	go reloadQueueLoop(ctx)
  1232  }
  1233  
  1234  func dashboardServiceInit() {
  1235  	if DashService == nil {
  1236  		DashService = &HTTPDashboardHandler{}
  1237  		DashService.Init()
  1238  	}
  1239  }
  1240  
  1241  func handleDashboardRegistration() {
  1242  	if !config.Global().UseDBAppConfigs {
  1243  		return
  1244  	}
  1245  
  1246  	dashboardServiceInit()
  1247  
  1248  	// connStr := buildConnStr("/register/node")
  1249  	if err := DashService.Register(); err != nil {
  1250  		dashLog.Fatal("Registration failed: ", err)
  1251  	}
  1252  
  1253  	go DashService.StartBeating()
  1254  }
  1255  
  1256  var drlOnce sync.Once
  1257  
  1258  func startDRL() {
  1259  	switch {
  1260  	case config.Global().ManagementNode:
  1261  		return
  1262  	case config.Global().EnableSentinelRateLimiter, config.Global().EnableRedisRollingLimiter:
  1263  		return
  1264  	}
  1265  	mainLog.Info("Initialising distributed rate limiter")
  1266  	setupDRL()
  1267  	startRateLimitNotifications()
  1268  }
  1269  
  1270  func setupPortsWhitelist() {
  1271  	// setup listen and control ports as whitelisted
  1272  	globalConf := config.Global()
  1273  	w := globalConf.PortWhiteList
  1274  	if w == nil {
  1275  		w = make(map[string]config.PortWhiteList)
  1276  	}
  1277  	protocol := "http"
  1278  	if globalConf.HttpServerOptions.UseSSL {
  1279  		protocol = "https"
  1280  	}
  1281  	ls := config.PortWhiteList{}
  1282  	if v, ok := w[protocol]; ok {
  1283  		ls = v
  1284  	}
  1285  	ls.Ports = append(ls.Ports, globalConf.ListenPort)
  1286  	if globalConf.ControlAPIPort != 0 {
  1287  		ls.Ports = append(ls.Ports, globalConf.ControlAPIPort)
  1288  	}
  1289  	w[protocol] = ls
  1290  	globalConf.PortWhiteList = w
  1291  	config.SetGlobal(globalConf)
  1292  }
  1293  
  1294  func startServer() {
  1295  	// Ensure that Control listener and default http listener running on first start
  1296  	muxer := &proxyMux{}
  1297  
  1298  	router := mux.NewRouter()
  1299  	loadAPIEndpoints(router)
  1300  	muxer.setRouter(config.Global().ControlAPIPort, "", router)
  1301  
  1302  	if muxer.router(config.Global().ListenPort, "") == nil {
  1303  		muxer.setRouter(config.Global().ListenPort, "", mux.NewRouter())
  1304  	}
  1305  
  1306  	defaultProxyMux.swap(muxer)
  1307  
  1308  	// handle dashboard registration and nonces if available
  1309  	handleDashboardRegistration()
  1310  
  1311  	// at this point NodeID is ready to use by DRL
  1312  	drlOnce.Do(startDRL)
  1313  
  1314  	mainLog.Infof("Tyk Gateway started (%s)", VERSION)
  1315  	address := config.Global().ListenAddress
  1316  	if config.Global().ListenAddress == "" {
  1317  		address = "(open interface)"
  1318  	}
  1319  	mainLog.Info("--> Listening on address: ", address)
  1320  	mainLog.Info("--> Listening on port: ", config.Global().ListenPort)
  1321  	mainLog.Info("--> PID: ", hostDetails.PID)
  1322  	if !rpc.IsEmergencyMode() {
  1323  		DoReload()
  1324  	}
  1325  }