github.com/jlevesy/mattermost-server@v5.3.2-0.20181003190404-7468f35cb0c8+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 "io" 11 "io/ioutil" 12 "net" 13 "net/http" 14 "net/url" 15 "os" 16 "strings" 17 "time" 18 19 "github.com/gorilla/handlers" 20 "github.com/gorilla/mux" 21 "github.com/pkg/errors" 22 "github.com/rs/cors" 23 "golang.org/x/crypto/acme/autocert" 24 25 "github.com/mattermost/mattermost-server/mlog" 26 "github.com/mattermost/mattermost-server/model" 27 "github.com/mattermost/mattermost-server/store" 28 "github.com/mattermost/mattermost-server/utils" 29 ) 30 31 type Server struct { 32 Store store.Store 33 WebSocketRouter *WebSocketRouter 34 35 // RootRouter is the starting point for all HTTP requests to the server. 36 RootRouter *mux.Router 37 38 // Router is the starting point for all web, api4 and ws requests to the server. It differs 39 // from RootRouter only if the SiteURL contains a /subpath. 40 Router *mux.Router 41 42 Server *http.Server 43 ListenAddr *net.TCPAddr 44 RateLimiter *RateLimiter 45 46 didFinishListen chan struct{} 47 } 48 49 var corsAllowedMethods []string = []string{ 50 "POST", 51 "GET", 52 "OPTIONS", 53 "PUT", 54 "PATCH", 55 "DELETE", 56 } 57 58 type RecoveryLogger struct { 59 } 60 61 func (rl *RecoveryLogger) Println(i ...interface{}) { 62 mlog.Error("Please check the std error output for the stack trace") 63 mlog.Error(fmt.Sprint(i...)) 64 } 65 66 const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second 67 68 // golang.org/x/crypto/acme/autocert/autocert.go 69 func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) { 70 if r.Method != "GET" && r.Method != "HEAD" { 71 http.Error(w, "Use HTTPS", http.StatusBadRequest) 72 return 73 } 74 target := "https://" + stripPort(r.Host) + r.URL.RequestURI() 75 http.Redirect(w, r, target, http.StatusFound) 76 } 77 78 // golang.org/x/crypto/acme/autocert/autocert.go 79 func stripPort(hostport string) string { 80 host, _, err := net.SplitHostPort(hostport) 81 if err != nil { 82 return hostport 83 } 84 return net.JoinHostPort(host, "443") 85 } 86 87 func (a *App) StartServer() error { 88 mlog.Info("Starting Server...") 89 90 var handler http.Handler = a.Srv.RootRouter 91 if allowedOrigins := *a.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" { 92 exposedCorsHeaders := *a.Config().ServiceSettings.CorsExposedHeaders 93 allowCredentials := *a.Config().ServiceSettings.CorsAllowCredentials 94 debug := *a.Config().ServiceSettings.CorsDebug 95 corsWrapper := cors.New(cors.Options{ 96 AllowedOrigins: strings.Fields(allowedOrigins), 97 AllowedMethods: corsAllowedMethods, 98 AllowedHeaders: []string{"*"}, 99 ExposedHeaders: strings.Fields(exposedCorsHeaders), 100 MaxAge: 86400, 101 AllowCredentials: allowCredentials, 102 Debug: debug, 103 }) 104 105 // If we have debugging of CORS turned on then forward messages to logs 106 if debug { 107 corsWrapper.Log = a.Log.StdLog(mlog.String("source", "cors")) 108 } 109 110 handler = corsWrapper.Handler(handler) 111 } 112 113 if *a.Config().RateLimitSettings.Enable { 114 mlog.Info("RateLimiter is enabled") 115 116 rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings) 117 if err != nil { 118 return err 119 } 120 121 a.Srv.RateLimiter = rateLimiter 122 handler = rateLimiter.RateLimitHandler(handler) 123 } 124 125 a.Srv.Server = &http.Server{ 126 Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), 127 ReadTimeout: time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second, 128 WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second, 129 ErrorLog: a.Log.StdLog(mlog.String("source", "httpserver")), 130 } 131 132 addr := *a.Config().ServiceSettings.ListenAddress 133 if addr == "" { 134 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 135 addr = ":https" 136 } else { 137 addr = ":http" 138 } 139 } 140 141 listener, err := net.Listen("tcp", addr) 142 if err != nil { 143 errors.Wrapf(err, utils.T("api.server.start_server.starting.critical"), err) 144 return err 145 } 146 a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr) 147 148 mlog.Info(fmt.Sprintf("Server is listening on %v", listener.Addr().String())) 149 150 // Migration from old let's encrypt library 151 if *a.Config().ServiceSettings.UseLetsEncrypt { 152 if stat, err := os.Stat(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile); err == nil && !stat.IsDir() { 153 os.Remove(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile) 154 } 155 } 156 157 m := &autocert.Manager{ 158 Cache: autocert.DirCache(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile), 159 Prompt: autocert.AcceptTOS, 160 } 161 162 if *a.Config().ServiceSettings.Forward80To443 { 163 if host, port, err := net.SplitHostPort(addr); err != nil { 164 mlog.Error("Unable to setup forwarding: " + err.Error()) 165 } else if port != "443" { 166 return fmt.Errorf(utils.T("api.server.start_server.forward80to443.enabled_but_listening_on_wrong_port"), port) 167 } else { 168 httpListenAddress := net.JoinHostPort(host, "http") 169 170 if *a.Config().ServiceSettings.UseLetsEncrypt { 171 server := &http.Server{ 172 Addr: httpListenAddress, 173 Handler: m.HTTPHandler(nil), 174 ErrorLog: a.Log.StdLog(mlog.String("source", "le_forwarder_server")), 175 } 176 go server.ListenAndServe() 177 } else { 178 go func() { 179 redirectListener, err := net.Listen("tcp", httpListenAddress) 180 if err != nil { 181 mlog.Error("Unable to setup forwarding: " + err.Error()) 182 return 183 } 184 defer redirectListener.Close() 185 186 server := &http.Server{ 187 Handler: http.HandlerFunc(handleHTTPRedirect), 188 ErrorLog: a.Log.StdLog(mlog.String("source", "forwarder_server")), 189 } 190 server.Serve(redirectListener) 191 }() 192 } 193 } 194 } else if *a.Config().ServiceSettings.UseLetsEncrypt { 195 return errors.New(utils.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt")) 196 } 197 198 a.Srv.didFinishListen = make(chan struct{}) 199 go func() { 200 var err error 201 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 202 if *a.Config().ServiceSettings.UseLetsEncrypt { 203 204 tlsConfig := &tls.Config{ 205 GetCertificate: m.GetCertificate, 206 } 207 208 tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") 209 210 a.Srv.Server.TLSConfig = tlsConfig 211 err = a.Srv.Server.ServeTLS(listener, "", "") 212 } else { 213 err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile) 214 } 215 } else { 216 err = a.Srv.Server.Serve(listener) 217 } 218 if err != nil && err != http.ErrServerClosed { 219 mlog.Critical(fmt.Sprintf("Error starting server, err:%v", err)) 220 time.Sleep(time.Second) 221 } 222 close(a.Srv.didFinishListen) 223 }() 224 225 return nil 226 } 227 228 func (a *App) StopServer() { 229 if a.Srv.Server != nil { 230 ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) 231 defer cancel() 232 didShutdown := false 233 for a.Srv.didFinishListen != nil && !didShutdown { 234 if err := a.Srv.Server.Shutdown(ctx); err != nil { 235 mlog.Warn(err.Error()) 236 } 237 timer := time.NewTimer(time.Millisecond * 50) 238 select { 239 case <-a.Srv.didFinishListen: 240 didShutdown = true 241 case <-timer.C: 242 } 243 timer.Stop() 244 } 245 a.Srv.Server.Close() 246 a.Srv.Server = nil 247 } 248 } 249 250 func (a *App) OriginChecker() func(*http.Request) bool { 251 if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" { 252 if allowed != "*" { 253 siteURL, err := url.Parse(*a.Config().ServiceSettings.SiteURL) 254 if err == nil { 255 siteURL.Path = "" 256 allowed += " " + siteURL.String() 257 } 258 } 259 260 return utils.OriginChecker(allowed) 261 } 262 return nil 263 } 264 265 // This is required to re-use the underlying connection and not take up file descriptors 266 func consumeAndClose(r *http.Response) { 267 if r.Body != nil { 268 io.Copy(ioutil.Discard, r.Body) 269 r.Body.Close() 270 } 271 }