github.com/spline-fu/mattermost-server@v4.10.10+incompatible/app/server.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "context" 8 "crypto/tls" 9 "fmt" 10 "net" 11 "net/http" 12 "os" 13 "strings" 14 "time" 15 16 "github.com/gorilla/handlers" 17 "github.com/gorilla/mux" 18 "github.com/pkg/errors" 19 "golang.org/x/crypto/acme/autocert" 20 21 "github.com/mattermost/mattermost-server/mlog" 22 "github.com/mattermost/mattermost-server/model" 23 "github.com/mattermost/mattermost-server/store" 24 "github.com/mattermost/mattermost-server/utils" 25 ) 26 27 type Server struct { 28 Store store.Store 29 WebSocketRouter *WebSocketRouter 30 Router *mux.Router 31 Server *http.Server 32 ListenAddr *net.TCPAddr 33 RateLimiter *RateLimiter 34 35 didFinishListen chan struct{} 36 } 37 38 var allowedMethods []string = []string{ 39 "POST", 40 "GET", 41 "OPTIONS", 42 "PUT", 43 "PATCH", 44 "DELETE", 45 } 46 47 type RecoveryLogger struct { 48 } 49 50 func (rl *RecoveryLogger) Println(i ...interface{}) { 51 mlog.Error("Please check the std error output for the stack trace") 52 mlog.Error(fmt.Sprint(i...)) 53 } 54 55 type CorsWrapper struct { 56 config model.ConfigFunc 57 router *mux.Router 58 } 59 60 func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { 61 if allowed := *cw.config().ServiceSettings.AllowCorsFrom; allowed != "" { 62 if utils.CheckOrigin(r, allowed) { 63 w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) 64 65 if r.Method == "OPTIONS" { 66 w.Header().Set( 67 "Access-Control-Allow-Methods", 68 strings.Join(allowedMethods, ", ")) 69 70 w.Header().Set( 71 "Access-Control-Allow-Headers", 72 r.Header.Get("Access-Control-Request-Headers")) 73 } 74 } 75 } 76 77 if r.Method == "OPTIONS" { 78 return 79 } 80 81 cw.router.ServeHTTP(w, r) 82 } 83 84 const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second 85 86 func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) { 87 if r.Host == "" { 88 http.Error(w, "Not Found", http.StatusNotFound) 89 } 90 91 url := r.URL 92 url.Host = r.Host 93 url.Scheme = "https" 94 http.Redirect(w, r, url.String(), http.StatusFound) 95 } 96 97 func (a *App) StartServer() error { 98 mlog.Info("Starting Server...") 99 100 var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router} 101 102 if *a.Config().RateLimitSettings.Enable { 103 mlog.Info("RateLimiter is enabled") 104 105 rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings) 106 if err != nil { 107 return err 108 } 109 110 a.Srv.RateLimiter = rateLimiter 111 handler = rateLimiter.RateLimitHandler(handler) 112 } 113 114 a.Srv.Server = &http.Server{ 115 Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), 116 ReadTimeout: time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second, 117 WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second, 118 ErrorLog: a.Log.StdLog(mlog.String("source", "httpserver")), 119 } 120 121 addr := *a.Config().ServiceSettings.ListenAddress 122 if addr == "" { 123 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 124 addr = ":https" 125 } else { 126 addr = ":http" 127 } 128 } 129 130 listener, err := net.Listen("tcp", addr) 131 if err != nil { 132 errors.Wrapf(err, utils.T("api.server.start_server.starting.critical"), err) 133 return err 134 } 135 a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr) 136 137 mlog.Info(fmt.Sprintf("Server is listening on %v", listener.Addr().String())) 138 139 // Migration from old let's encrypt library 140 if *a.Config().ServiceSettings.UseLetsEncrypt { 141 if stat, err := os.Stat(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile); err == nil && !stat.IsDir() { 142 os.Remove(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile) 143 } 144 } 145 146 m := &autocert.Manager{ 147 Cache: autocert.DirCache(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile), 148 Prompt: autocert.AcceptTOS, 149 } 150 151 if *a.Config().ServiceSettings.Forward80To443 { 152 if host, port, err := net.SplitHostPort(addr); err != nil { 153 mlog.Error("Unable to setup forwarding: " + err.Error()) 154 } else if port != "443" { 155 return fmt.Errorf(utils.T("api.server.start_server.forward80to443.enabled_but_listening_on_wrong_port"), port) 156 } else { 157 httpListenAddress := net.JoinHostPort(host, "http") 158 159 if *a.Config().ServiceSettings.UseLetsEncrypt { 160 server := &http.Server{ 161 Addr: httpListenAddress, 162 Handler: m.HTTPHandler(nil), 163 ErrorLog: a.Log.StdLog(mlog.String("source", "le_forwarder_server")), 164 } 165 go server.ListenAndServe() 166 } else { 167 go func() { 168 redirectListener, err := net.Listen("tcp", httpListenAddress) 169 if err != nil { 170 mlog.Error("Unable to setup forwarding: " + err.Error()) 171 return 172 } 173 defer redirectListener.Close() 174 175 server := &http.Server{ 176 Handler: handler, 177 ErrorLog: a.Log.StdLog(mlog.String("source", "forwarder_server")), 178 } 179 server.Serve(redirectListener) 180 }() 181 } 182 } 183 } else if *a.Config().ServiceSettings.UseLetsEncrypt { 184 return errors.New(utils.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt")) 185 } 186 187 a.Srv.didFinishListen = make(chan struct{}) 188 go func() { 189 var err error 190 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 191 if *a.Config().ServiceSettings.UseLetsEncrypt { 192 193 tlsConfig := &tls.Config{ 194 GetCertificate: m.GetCertificate, 195 } 196 197 tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") 198 199 a.Srv.Server.TLSConfig = tlsConfig 200 err = a.Srv.Server.ServeTLS(listener, "", "") 201 } else { 202 err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile) 203 } 204 } else { 205 err = a.Srv.Server.Serve(listener) 206 } 207 if err != nil && err != http.ErrServerClosed { 208 mlog.Critical(fmt.Sprintf("Error starting server, err:%v", err)) 209 time.Sleep(time.Second) 210 } 211 close(a.Srv.didFinishListen) 212 }() 213 214 return nil 215 } 216 217 func (a *App) StopServer() { 218 if a.Srv.Server != nil { 219 ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) 220 defer cancel() 221 didShutdown := false 222 for a.Srv.didFinishListen != nil && !didShutdown { 223 if err := a.Srv.Server.Shutdown(ctx); err != nil { 224 mlog.Warn(err.Error()) 225 } 226 timer := time.NewTimer(time.Millisecond * 50) 227 select { 228 case <-a.Srv.didFinishListen: 229 didShutdown = true 230 case <-timer.C: 231 } 232 timer.Stop() 233 } 234 a.Srv.Server.Close() 235 a.Srv.Server = nil 236 } 237 } 238 239 func (a *App) OriginChecker() func(*http.Request) bool { 240 if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" { 241 return utils.OriginChecker(allowed) 242 } 243 return nil 244 }