github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/tequilapi/http_api_server.go (about)

     1  /*
     2   * Copyright (C) 2017 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package tequilapi
    19  
    20  import (
    21  	"net"
    22  	"net/http"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/gin-contrib/cors"
    27  	"github.com/mysteriumnetwork/go-rest/apierror"
    28  	"github.com/mysteriumnetwork/node/tequilapi/middlewares"
    29  
    30  	"github.com/mysteriumnetwork/node/core/node"
    31  
    32  	"github.com/gin-gonic/gin"
    33  
    34  	"github.com/pkg/errors"
    35  	"github.com/rs/zerolog/log"
    36  )
    37  
    38  var corsConfig = cors.Config{
    39  	MaxAge: 30 * 24 * time.Hour,
    40  	AllowOriginFunc: func(_ string) bool {
    41  		return false
    42  	},
    43  }
    44  
    45  // APIServer interface represents control methods for underlying http api server
    46  type APIServer interface {
    47  	Wait() error
    48  	StartServing()
    49  	Stop()
    50  	Address() (string, error)
    51  }
    52  
    53  type apiServer struct {
    54  	errorChannel chan error
    55  	listener     net.Listener
    56  
    57  	gin *gin.Engine
    58  }
    59  
    60  type jwtAuthenticator interface {
    61  	ValidateToken(token string) (bool, error)
    62  }
    63  
    64  // NewServer creates http api server for given address port and http handler
    65  func NewServer(
    66  	listener net.Listener,
    67  	nodeOptions node.Options,
    68  	authenticator jwtAuthenticator,
    69  	handlers []func(e *gin.Engine) error,
    70  ) (APIServer, error) {
    71  	gin.SetMode(modeFromOptions(nodeOptions))
    72  	g := gin.New()
    73  	g.Use(middlewares.ApplyCacheConfigMiddleware)
    74  	g.Use(gin.Recovery())
    75  	g.Use(cors.New(corsConfig))
    76  	g.Use(middlewares.NewHostFilter())
    77  	g.Use(apierror.ErrorHandler)
    78  
    79  	if nodeOptions.TequilapiSecured {
    80  		g.Use(middlewares.ApplyMiddlewareTokenAuth(authenticator))
    81  	}
    82  
    83  	// Set to protect localhost-only endpoints due to use of nodeUI proxy
    84  	// With this set, context.ClientIP() will return only IP set by trusted proxy, not by a client!
    85  	g.SetTrustedProxies([]string{"127.0.0.1"})
    86  
    87  	for _, h := range handlers {
    88  		err := h(g)
    89  		if err != nil {
    90  			return nil, err
    91  		}
    92  	}
    93  
    94  	server := apiServer{
    95  		errorChannel: make(chan error, 1),
    96  		listener:     listener,
    97  
    98  		gin: g,
    99  	}
   100  
   101  	return &server, nil
   102  }
   103  
   104  func modeFromOptions(options node.Options) string {
   105  	if options.FlagTequilapiDebugMode {
   106  		return gin.DebugMode
   107  	}
   108  
   109  	return gin.ReleaseMode
   110  }
   111  
   112  // Stop method stops underlying http server
   113  func (server *apiServer) Stop() {
   114  	server.listener.Close()
   115  }
   116  
   117  // Wait method waits for http server to finish handling requests (i.e. when Stop() was called)
   118  func (server *apiServer) Wait() error {
   119  	return <-server.errorChannel
   120  }
   121  
   122  // Address method returns bind port for given http server (useful when random port is used)
   123  func (server *apiServer) Address() (string, error) {
   124  	return extractBoundAddress(server.listener)
   125  }
   126  
   127  // StartServing starts http request serving
   128  func (server *apiServer) StartServing() {
   129  	go server.serve()
   130  	address, err := server.Address()
   131  	if err != nil {
   132  		log.Error().Err(err).Msg("Could not get tequila server address")
   133  		return
   134  	}
   135  	log.Info().Msgf("API started on: %s", address)
   136  }
   137  
   138  func (server *apiServer) serve() {
   139  	server.errorChannel <- http.Serve(server.listener, server.gin)
   140  }
   141  
   142  func extractBoundAddress(listener net.Listener) (string, error) {
   143  	addr := listener.Addr()
   144  	parts := strings.Split(addr.String(), ":")
   145  	if len(parts) < 2 {
   146  		return "", errors.New("Unable to locate address: " + addr.String())
   147  	}
   148  	return addr.String(), nil
   149  }