github.com/vmware/transport-go@v1.3.4/plank/pkg/server/server.go (about)

     1  // Copyright 2019-2021 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package server
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"net"
    14  	"net/http"
    15  	"os"
    16  	"os/signal"
    17  	"path"
    18  	"reflect"
    19  	"sync"
    20  	"sync/atomic"
    21  	"syscall"
    22  	"time"
    23  
    24  	"github.com/spf13/cobra"
    25  	"github.com/spf13/pflag"
    26  	"github.com/vmware/transport-go/model"
    27  
    28  	"github.com/gorilla/handlers"
    29  	"github.com/gorilla/mux"
    30  	"github.com/vmware/transport-go/bus"
    31  	"github.com/vmware/transport-go/plank/pkg/middleware"
    32  	"github.com/vmware/transport-go/plank/utils"
    33  	"github.com/vmware/transport-go/service"
    34  )
    35  
    36  const PLANK_SERVER_ONLINE_CHANNEL = bus.TRANSPORT_INTERNAL_CHANNEL_PREFIX + "plank-online-notify"
    37  const AllMethodsWildcard = "*" // every method, open the gates!
    38  
    39  // NewPlatformServer configures and returns a new platformServer instance
    40  func NewPlatformServer(config *PlatformServerConfig) PlatformServer {
    41  	if !checkConfigForLogConfig(config) {
    42  		utils.Log.Error("unable to create new platform server, log config not found")
    43  		return nil
    44  	}
    45  
    46  	ps := new(platformServer)
    47  	sanitizeConfigRootPath(config)
    48  	ps.serverConfig = config
    49  	ps.ServerAvailability = &ServerAvailability{}
    50  	ps.routerConcurrencyProtection = new(int32)
    51  	ps.messageBridgeMap = make(map[string]*MessageBridge)
    52  	ps.eventbus = bus.GetBus()
    53  	ps.initialize()
    54  	return ps
    55  }
    56  
    57  func checkConfigForLogConfig(config *PlatformServerConfig) bool {
    58  	if config.LogConfig != nil {
    59  		return true
    60  	}
    61  	return false
    62  }
    63  
    64  // NewPlatformServerFromConfig returns a new instance of PlatformServer based on the config JSON file provided as configPath
    65  func NewPlatformServerFromConfig(configPath string) (PlatformServer, error) {
    66  	var config PlatformServerConfig
    67  
    68  	// no config no server
    69  	configBytes, err := ioutil.ReadFile(configPath)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	// malformed config no server as well
    75  	if err = json.Unmarshal(configBytes, &config); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	ps := new(platformServer)
    80  	ps.eventbus = bus.GetBus()
    81  	sanitizeConfigRootPath(&config)
    82  
    83  	// ensure references to file system paths are relative to config.RootDir
    84  	config.LogConfig.OutputLog = utils.JoinBasePathIfRelativeRegularFilePath(config.LogConfig.Root, config.LogConfig.OutputLog)
    85  	config.LogConfig.AccessLog = utils.JoinBasePathIfRelativeRegularFilePath(config.LogConfig.Root, config.LogConfig.AccessLog)
    86  	config.LogConfig.ErrorLog = utils.JoinBasePathIfRelativeRegularFilePath(config.LogConfig.Root, config.LogConfig.ErrorLog)
    87  
    88  	// handle invalid duration by setting it to the default value of 5 minutes
    89  	if config.ShutdownTimeout <= 0 {
    90  		config.ShutdownTimeout = 5
    91  	}
    92  
    93  	// handle invalid duration by setting it to the default value of 1 minute
    94  	if config.RestBridgeTimeout <= 0 {
    95  		config.RestBridgeTimeout = 1
    96  	}
    97  
    98  	// the raw value from the config.json needs to be multiplied by time.Minute otherwise it's interpreted as nanosecond
    99  	config.ShutdownTimeout = config.ShutdownTimeout * time.Minute
   100  
   101  	// the raw value from the config.json needs to be multiplied by time.Minute otherwise it's interpreted as nanosecond
   102  	config.RestBridgeTimeout = config.RestBridgeTimeout * time.Minute
   103  
   104  	if config.TLSCertConfig != nil {
   105  		if !path.IsAbs(config.TLSCertConfig.CertFile) {
   106  			config.TLSCertConfig.CertFile = path.Clean(path.Join(config.RootDir, config.TLSCertConfig.CertFile))
   107  		}
   108  
   109  		if !path.IsAbs(config.TLSCertConfig.KeyFile) {
   110  			config.TLSCertConfig.KeyFile = path.Clean(path.Join(config.RootDir, config.TLSCertConfig.KeyFile))
   111  		}
   112  	}
   113  
   114  	ps.serverConfig = &config
   115  	ps.ServerAvailability = &ServerAvailability{}
   116  	ps.routerConcurrencyProtection = new(int32)
   117  	ps.initialize()
   118  	return ps, nil
   119  }
   120  
   121  // CreateServerConfig creates a new instance of PlatformServerConfig and returns the pointer to it.
   122  func CreateServerConfig() (*PlatformServerConfig, error) {
   123  	factory := &serverConfigFactory{}
   124  	factory.configureFlags(pflag.CommandLine)
   125  	factory.parseFlags(os.Args)
   126  	return generatePlatformServerConfig(factory)
   127  }
   128  
   129  // CreateServerConfigForCobraCommand performs the same as CreateServerConfig but loads the flags to
   130  // the provided cobra Command's *pflag.FlagSet instead of the global FlagSet instance.
   131  func CreateServerConfigForCobraCommand(cmd *cobra.Command) (*PlatformServerConfig, error) {
   132  	factory := &serverConfigFactory{}
   133  	factory.configureFlags(cmd.Flags())
   134  	factory.parseFlags(os.Args)
   135  	return generatePlatformServerConfig(factory)
   136  }
   137  
   138  // StartServer starts listening on the host and port as specified by ServerConfig
   139  func (ps *platformServer) StartServer(syschan chan os.Signal) {
   140  	connClosed := make(chan struct{})
   141  
   142  	ps.SyscallChan = syschan
   143  	signal.Notify(ps.SyscallChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
   144  
   145  	// ensure port is available
   146  	ps.checkPortAvailability()
   147  
   148  	// finalize handler by setting out writer
   149  	ps.loadGlobalHttpHandler(ps.router)
   150  
   151  	// configure SPA
   152  	// NOTE: the reason SPA app route is configured during server startup is that if the base uri is `/` for SPA
   153  	// then all other routes registered after SPA route will be masked away.
   154  	ps.configureSPA()
   155  
   156  	go func() {
   157  		ps.ServerAvailability.Http = true
   158  		if ps.serverConfig.TLSCertConfig != nil {
   159  			utils.Log.Infof("[plank] Starting HTTP server at %s:%d with TLS", ps.serverConfig.Host, ps.serverConfig.Port)
   160  			if err := ps.HttpServer.ListenAndServeTLS(ps.serverConfig.TLSCertConfig.CertFile, ps.serverConfig.TLSCertConfig.KeyFile); err != nil {
   161  				if !errors.Is(err, http.ErrServerClosed) {
   162  					utils.Log.Fatalln(wrapError(errServerInit, err))
   163  				}
   164  			}
   165  		} else {
   166  			utils.Log.Infof("[plank] Starting HTTP server at %s:%d", ps.serverConfig.Host, ps.serverConfig.Port)
   167  			if err := ps.HttpServer.ListenAndServe(); err != nil {
   168  				if !errors.Is(err, http.ErrServerClosed) {
   169  					utils.Log.Fatalln(wrapError(errServerInit, err))
   170  				}
   171  			}
   172  		}
   173  	}()
   174  
   175  	// if Fabric broker configuration is found, start the broker
   176  	if ps.serverConfig.FabricConfig != nil {
   177  		go func() {
   178  			fabricPort := ps.serverConfig.Port
   179  			fabricEndpoint := ps.serverConfig.FabricConfig.FabricEndpoint
   180  			if ps.serverConfig.FabricConfig.UseTCP {
   181  				// if using TCP adjust port accordingly and drop endpoint
   182  				fabricPort = ps.serverConfig.FabricConfig.TCPPort
   183  				fabricEndpoint = ""
   184  			}
   185  			brokerLocation := fmt.Sprintf("%s:%d%s", ps.serverConfig.Host, fabricPort, fabricEndpoint)
   186  			utils.Log.Infof("[plank] Starting Transport broker at %s", brokerLocation)
   187  			ps.ServerAvailability.Fabric = true
   188  
   189  			if err := ps.eventbus.StartFabricEndpoint(ps.fabricConn, *ps.serverConfig.FabricConfig.EndpointConfig); err != nil {
   190  				utils.Log.Fatalln(wrapError(errServerInit, err))
   191  			}
   192  		}()
   193  	}
   194  
   195  	// spawn another goroutine to respond to syscall to shut down servers and terminate the main thread
   196  	go func() {
   197  		<-ps.SyscallChan
   198  		// notify subscribers that the server is shutting down
   199  		_ = ps.eventbus.SendResponseMessage(PLANK_SERVER_ONLINE_CHANNEL, false, nil)
   200  		ps.StopServer()
   201  		close(connClosed)
   202  	}()
   203  
   204  	// notify subscribers that the server is ready to interact with
   205  	httpReady := false
   206  	for {
   207  		_, err := net.Dial("tcp", fmt.Sprintf(":%d", ps.serverConfig.Port))
   208  		httpReady = err == nil
   209  		if !httpReady {
   210  			time.Sleep(1 * time.Millisecond)
   211  			utils.Log.Debugln("waiting for http server to be ready to accept connections")
   212  			continue
   213  		}
   214  		_ = ps.eventbus.SendResponseMessage(PLANK_SERVER_ONLINE_CHANNEL, true, nil)
   215  		break
   216  	}
   217  
   218  	<-connClosed
   219  }
   220  
   221  // StopServer attempts to gracefully stop the HTTP and STOMP server if running
   222  func (ps *platformServer) StopServer() {
   223  	utils.Log.Infoln("[plank] Server shutting down")
   224  	ps.ServerAvailability.Http = false
   225  
   226  	baseCtx := context.Background()
   227  	shutdownCtx, cancel := context.WithTimeout(baseCtx, ps.serverConfig.ShutdownTimeout)
   228  
   229  	go func() {
   230  		select {
   231  		case <-shutdownCtx.Done():
   232  			if errors.Is(shutdownCtx.Err(), context.DeadlineExceeded) {
   233  				utils.Log.Fatalf(
   234  					"Server failed to gracefully shut down after %s",
   235  					ps.serverConfig.ShutdownTimeout.String())
   236  			}
   237  		}
   238  	}()
   239  	defer cancel()
   240  
   241  	// call all registered services' OnServerShutdown() hook
   242  	svcRegistry := service.GetServiceRegistry()
   243  	lcm := service.GetServiceLifecycleManager()
   244  	wg := sync.WaitGroup{}
   245  	for _, svcChannel := range svcRegistry.GetAllServiceChannels() {
   246  		hooks := lcm.GetServiceHooks(svcChannel)
   247  		if hooks != nil {
   248  			utils.Log.Infof("Teardown in progress for service at '%s'", svcChannel)
   249  			wg.Add(1)
   250  			go func(cName string, h service.ServiceLifecycleHookEnabled) {
   251  				h.OnServerShutdown()
   252  				utils.Log.Infof("Teardown completed for service at '%s'", cName)
   253  				wg.Done()
   254  
   255  			}(svcChannel, hooks)
   256  		}
   257  	}
   258  
   259  	// start graceful shutdown
   260  	err := ps.HttpServer.Shutdown(shutdownCtx)
   261  	if err != nil {
   262  		utils.Log.Errorln(err)
   263  	}
   264  
   265  	if ps.fabricConn != nil {
   266  		err = ps.eventbus.StopFabricEndpoint()
   267  		if err != nil {
   268  			utils.Log.Errorln(err)
   269  		}
   270  		ps.ServerAvailability.Fabric = false
   271  	}
   272  
   273  	// wait for all teardown jobs to be done. if shutdown deadline arrives earlier
   274  	// the main thread will be terminated forcefully
   275  	wg.Wait()
   276  }
   277  
   278  // SetStaticRoute adds a route where static resources will be served
   279  func (ps *platformServer) SetStaticRoute(prefix, fullpath string, middlewareFn ...mux.MiddlewareFunc) {
   280  	ps.router.Handle(prefix, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   281  		http.Redirect(w, r, prefix+"/", http.StatusMovedPermanently)
   282  	}))
   283  
   284  	ndir := NoDirFileSystem{http.Dir(fullpath)}
   285  	endpointHandlerMapKey := prefix + "*"
   286  	compositeHandler := http.StripPrefix(prefix, middleware.BasicSecurityHeaderMiddleware()(http.FileServer(ndir)))
   287  
   288  	for _, mw := range middlewareFn {
   289  		compositeHandler = mw(compositeHandler)
   290  	}
   291  
   292  	ps.endpointHandlerMap[endpointHandlerMapKey] = compositeHandler.(http.HandlerFunc)
   293  	ps.router.PathPrefix(prefix + "/").Name(endpointHandlerMapKey).Handler(ps.endpointHandlerMap[endpointHandlerMapKey])
   294  }
   295  
   296  // RegisterService registers a Fabric service with Bifrost
   297  func (ps *platformServer) RegisterService(svc service.FabricService, svcChannel string) error {
   298  	sr := service.GetServiceRegistry()
   299  	err := sr.RegisterService(svc, svcChannel)
   300  	svcType := reflect.TypeOf(svc)
   301  
   302  	if err == nil {
   303  		utils.Log.Infof("[plank] Service '%s' registered at channel '%s'", svcType.String(), svcChannel)
   304  		svcLifecycleManager := service.GetServiceLifecycleManager()
   305  		var hooks service.ServiceLifecycleHookEnabled
   306  		if hooks = svcLifecycleManager.GetServiceHooks(svcChannel); hooks == nil {
   307  			// if service has no lifecycle hooks mark the channel as ready straight up
   308  			storeManager := ps.eventbus.GetStoreManager()
   309  			store := storeManager.GetStore(service.ServiceReadyStore)
   310  			store.Put(svcChannel, true, service.ServiceInitStateChange)
   311  			utils.Log.Infof("[plank] Service '%s' initialized successfully", svcType.String())
   312  		}
   313  	}
   314  	return err
   315  }
   316  
   317  // SetHttpChannelBridge establishes a conduit between the transport service channel and an HTTP endpoint
   318  // that allows a client to invoke the service via REST.
   319  func (ps *platformServer) SetHttpChannelBridge(bridgeConfig *service.RESTBridgeConfig) {
   320  	ps.lock.Lock()
   321  	defer ps.lock.Unlock()
   322  
   323  	endpointHandlerKey := bridgeConfig.Uri + "-" + bridgeConfig.Method
   324  
   325  	if _, ok := ps.endpointHandlerMap[endpointHandlerKey]; ok {
   326  		utils.Log.Warnf("[plank] Endpoint '%s (%s)' is already associated with a handler. "+
   327  			"Try another endpoint or remove it before assigning a new handler", bridgeConfig.Uri, bridgeConfig.Method)
   328  		return
   329  	}
   330  
   331  	// create a map for service channel - bridges mapping if it does not exist
   332  	if ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] == nil {
   333  		ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] = make([]string, 0)
   334  	}
   335  
   336  	if _, exists := ps.messageBridgeMap[bridgeConfig.ServiceChannel]; !exists {
   337  		handler, _ := ps.eventbus.ListenStream(bridgeConfig.ServiceChannel)
   338  		handler.Handle(func(message *model.Message) {
   339  			ps.messageBridgeMap[bridgeConfig.ServiceChannel].payloadChannel <- message
   340  		}, func(err error) {})
   341  
   342  		ps.messageBridgeMap[bridgeConfig.ServiceChannel] = &MessageBridge{
   343  			ServiceListenStream: handler,
   344  			payloadChannel:      make(chan *model.Message, 100),
   345  		}
   346  	}
   347  
   348  	// NOTE: mux.Router does not have mutex or any locking mechanism so it could sometimes lead to concurrency write
   349  	// panics. the following is to ensure the modification to ps.router can happen only once per thread, this atomic
   350  	// counter also protects against concurrent writing to ps.endpointHandlerMap
   351  	for !atomic.CompareAndSwapInt32(ps.routerConcurrencyProtection, 0, 1) {
   352  		time.Sleep(1 * time.Nanosecond)
   353  	}
   354  
   355  	// build endpoint handler
   356  	ps.endpointHandlerMap[endpointHandlerKey] = ps.buildEndpointHandler(
   357  		bridgeConfig.ServiceChannel,
   358  		bridgeConfig.FabricRequestBuilder,
   359  		ps.serverConfig.RestBridgeTimeout,
   360  		ps.messageBridgeMap[bridgeConfig.ServiceChannel].payloadChannel)
   361  
   362  	ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] = append(
   363  		ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel], endpointHandlerKey)
   364  
   365  	permittedMethods := []string{bridgeConfig.Method}
   366  	if bridgeConfig.AllowHead {
   367  		permittedMethods = append(permittedMethods, http.MethodHead)
   368  	}
   369  	if bridgeConfig.AllowOptions {
   370  		permittedMethods = append(permittedMethods, http.MethodOptions)
   371  	}
   372  
   373  	ps.router.
   374  		Path(bridgeConfig.Uri).
   375  		Methods(permittedMethods...).
   376  		Name(fmt.Sprintf("%s-%s", bridgeConfig.Uri, bridgeConfig.Method)).
   377  		Handler(ps.endpointHandlerMap[endpointHandlerKey])
   378  	if !atomic.CompareAndSwapInt32(ps.routerConcurrencyProtection, 1, 0) {
   379  		panic("Concurrency write on router detected when running ")
   380  	}
   381  
   382  	utils.Log.Infof(
   383  		"[plank] Service channel '%s' is now bridged to a REST endpoint %s (%s)",
   384  		bridgeConfig.ServiceChannel, bridgeConfig.Uri, bridgeConfig.Method)
   385  }
   386  
   387  // SetHttpPathPrefixChannelBridge establishes a conduit between the transport service channel and a path prefix
   388  // every request on this prefix will be sent through to the target service, all methods, all sub paths, lock, stock and barrel.
   389  func (ps *platformServer) SetHttpPathPrefixChannelBridge(bridgeConfig *service.RESTBridgeConfig) {
   390  	ps.lock.Lock()
   391  	defer ps.lock.Unlock()
   392  
   393  	endpointHandlerKey := bridgeConfig.Uri + "-" + AllMethodsWildcard
   394  
   395  	if _, ok := ps.endpointHandlerMap[endpointHandlerKey]; ok {
   396  		utils.Log.Warnf("[plank] Path prefix '%s (%s)' is already being handled. "+
   397  			"Try another prefix or remove it before assigning a new handler", bridgeConfig.Uri, bridgeConfig.Method)
   398  		return
   399  	}
   400  
   401  	// create a map for service channel - bridges mapping if it does not exist
   402  	if ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] == nil {
   403  		ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] = make([]string, 0)
   404  	}
   405  
   406  	if _, exists := ps.messageBridgeMap[bridgeConfig.ServiceChannel]; !exists {
   407  		handler, _ := ps.eventbus.ListenStream(bridgeConfig.ServiceChannel)
   408  		handler.Handle(func(message *model.Message) {
   409  			ps.messageBridgeMap[bridgeConfig.ServiceChannel].payloadChannel <- message
   410  		}, func(err error) {})
   411  
   412  		ps.messageBridgeMap[bridgeConfig.ServiceChannel] = &MessageBridge{
   413  			ServiceListenStream: handler,
   414  			payloadChannel:      make(chan *model.Message, 100),
   415  		}
   416  	}
   417  
   418  	// build endpoint handler
   419  	ps.endpointHandlerMap[endpointHandlerKey] = ps.buildEndpointHandler(
   420  		bridgeConfig.ServiceChannel,
   421  		bridgeConfig.FabricRequestBuilder,
   422  		ps.serverConfig.RestBridgeTimeout,
   423  		ps.messageBridgeMap[bridgeConfig.ServiceChannel].payloadChannel)
   424  
   425  	ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel] = append(
   426  		ps.serviceChanToBridgeEndpoints[bridgeConfig.ServiceChannel], endpointHandlerKey)
   427  
   428  	// NOTE: mux.Router does not have mutex or any locking mechanism so it could sometimes lead to concurrency write
   429  	// panics. the following is to ensure the modification to ps.router can happen only once per thread
   430  	for !atomic.CompareAndSwapInt32(ps.routerConcurrencyProtection, 0, 1) {
   431  		time.Sleep(1 * time.Nanosecond)
   432  	}
   433  	ps.router.
   434  		PathPrefix(bridgeConfig.Uri).
   435  		Name(endpointHandlerKey).
   436  		Handler(ps.endpointHandlerMap[endpointHandlerKey])
   437  	if !atomic.CompareAndSwapInt32(ps.routerConcurrencyProtection, 1, 0) {
   438  		panic("Concurrency write on router detected when running SetHttpPathPrefixChannelBridge()")
   439  	}
   440  
   441  	utils.Log.Infof(
   442  		"[plank] Service channel '%s' is now bridged to a REST path prefix '%s'",
   443  		bridgeConfig.ServiceChannel, bridgeConfig.Uri)
   444  
   445  }
   446  
   447  // GetMiddlewareManager returns the MiddleManager instance
   448  func (ps *platformServer) GetMiddlewareManager() middleware.MiddlewareManager {
   449  	return ps.middlewareManager
   450  }
   451  
   452  func (ps *platformServer) GetRestBridgeSubRoute(uri, method string) (*mux.Route, error) {
   453  	route, err := ps.getSubRoute(fmt.Sprintf("%s-%s", uri, method))
   454  	if route == nil {
   455  		return nil, fmt.Errorf("no route exists at %s (%s) exists", uri, method)
   456  	}
   457  	return route, err
   458  }
   459  
   460  // CustomizeTLSConfig is used to create a customized TLS configuration for use with http.Server.
   461  // this function needs to be called before the server starts, otherwise it will error out.
   462  func (c *platformServer) CustomizeTLSConfig(tls *tls.Config) error {
   463  	if c.ServerAvailability.Http || c.ServerAvailability.Fabric {
   464  		return fmt.Errorf("TLS configuration can be provided only if the server is not running")
   465  	}
   466  	c.HttpServer.TLSConfig = tls
   467  	return nil
   468  }
   469  
   470  // clearHttpChannelBridgesForService takes serviceChannel, gets all mux.Route instances associated with
   471  // the service and removes them while keeping the rest of the routes intact. returns the pointer
   472  // of a new instance of mux.Router.
   473  func (ps *platformServer) clearHttpChannelBridgesForService(serviceChannel string) *mux.Router {
   474  	ps.lock.Lock()
   475  	defer ps.lock.Unlock()
   476  
   477  	// NOTE: gorilla mux doesn't allow us to mutate routes field of the Router struct which is critical in rerouting incoming
   478  	// requests to the new route. there is not a public API that allows us to do it so we're instead creating a new instance of
   479  	// Router and assigning the existing config and route. this means `ps.route` is treated as immutable and will be
   480  	// replaced with a new instance of mux.Router by the operation performed in this function
   481  
   482  	// walk over existing routes and store them temporarily EXCEPT the ones that are being overwritten which can
   483  	// be tracked by the service channel
   484  	newRouter := mux.NewRouter().Schemes("http", "https").Subrouter()
   485  	lookupMap := make(map[string]bool)
   486  	for _, key := range ps.serviceChanToBridgeEndpoints[serviceChannel] {
   487  		lookupMap[key] = true
   488  	}
   489  
   490  	ps.router.Walk(func(r *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
   491  		name := r.GetName()
   492  		path, _ := r.GetPathTemplate()
   493  		handler := r.GetHandler()
   494  		methods, _ := r.GetMethods()
   495  		// do not want to copy over the routes that will be overridden
   496  		if lookupMap[name] {
   497  			utils.Log.Debugf("[plank] route '%s' will be overridden so not copying over to the new router instance", name)
   498  		} else {
   499  			newRouter.Name(name).Path(path).Methods(methods...).Handler(handler)
   500  		}
   501  		return nil
   502  	})
   503  
   504  	// if in override mode delete existing mappings associated with the service
   505  	existingMappings := ps.serviceChanToBridgeEndpoints[serviceChannel]
   506  	ps.serviceChanToBridgeEndpoints[serviceChannel] = make([]string, 0)
   507  	for _, handlerKey := range existingMappings {
   508  		utils.Log.Infof("[plank] Removing existing service - REST mapping '%s' for service '%s'", handlerKey, serviceChannel)
   509  		delete(ps.endpointHandlerMap, handlerKey)
   510  	}
   511  	return newRouter
   512  }
   513  
   514  func (ps *platformServer) getSubRoute(name string) (*mux.Route, error) {
   515  	route := ps.router.Get(name)
   516  	if route == nil {
   517  		return nil, fmt.Errorf("no route exists under name %s", name)
   518  	}
   519  	return route, nil
   520  }
   521  
   522  func (ps *platformServer) loadGlobalHttpHandler(h *mux.Router) {
   523  	ps.lock.Lock()
   524  	defer ps.lock.Unlock()
   525  	ps.router = h
   526  	ps.HttpServer.Handler = handlers.RecoveryHandler()(
   527  		handlers.CompressHandler(
   528  			handlers.ProxyHeaders(
   529  				handlers.CombinedLoggingHandler(
   530  					ps.serverConfig.LogConfig.GetAccessLogFilePointer(), ps.router))))
   531  }
   532  
   533  func (ps *platformServer) checkPortAvailability() {
   534  	// is the port free?
   535  	_, err := net.Dial("tcp", fmt.Sprintf(":%d", ps.serverConfig.Port))
   536  
   537  	// connection should fail otherwise it means there's already a listener on the host+port combination, in which case we stop here
   538  	if err == nil {
   539  		utils.Log.Fatalf("Server could not start at %s:%d because another process is using it. Please try another endpoint.",
   540  			ps.serverConfig.Host, ps.serverConfig.Port)
   541  	}
   542  }
   543  
   544  func (ps *platformServer) setEventBusRef(evtBus bus.EventBus) {
   545  	ps.lock.Lock()
   546  	ps.eventbus = evtBus
   547  	ps.lock.Unlock()
   548  }