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 }