github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+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 "io" 10 "io/ioutil" 11 "net" 12 "net/http" 13 "os" 14 "strings" 15 "time" 16 17 l4g "github.com/alecthomas/log4go" 18 "github.com/gorilla/handlers" 19 "github.com/gorilla/mux" 20 "github.com/pkg/errors" 21 "golang.org/x/crypto/acme/autocert" 22 23 "github.com/mattermost/mattermost-server/model" 24 "github.com/mattermost/mattermost-server/store" 25 "github.com/mattermost/mattermost-server/utils" 26 ) 27 28 type Server struct { 29 Store store.Store 30 WebSocketRouter *WebSocketRouter 31 Router *mux.Router 32 Server *http.Server 33 ListenAddr *net.TCPAddr 34 RateLimiter *RateLimiter 35 36 didFinishListen chan struct{} 37 } 38 39 var allowedMethods []string = []string{ 40 "POST", 41 "GET", 42 "OPTIONS", 43 "PUT", 44 "PATCH", 45 "DELETE", 46 } 47 48 type RecoveryLogger struct { 49 } 50 51 func (rl *RecoveryLogger) Println(i ...interface{}) { 52 l4g.Error("Please check the std error output for the stack trace") 53 l4g.Error(i) 54 } 55 56 type CorsWrapper struct { 57 config model.ConfigFunc 58 router *mux.Router 59 } 60 61 func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { 62 if allowed := *cw.config().ServiceSettings.AllowCorsFrom; allowed != "" { 63 if utils.CheckOrigin(r, allowed) { 64 w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin")) 65 66 if r.Method == "OPTIONS" { 67 w.Header().Set( 68 "Access-Control-Allow-Methods", 69 strings.Join(allowedMethods, ", ")) 70 71 w.Header().Set( 72 "Access-Control-Allow-Headers", 73 r.Header.Get("Access-Control-Request-Headers")) 74 } 75 } 76 } 77 78 if r.Method == "OPTIONS" { 79 return 80 } 81 82 cw.router.ServeHTTP(w, r) 83 } 84 85 const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second 86 87 func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) { 88 if r.Host == "" { 89 http.Error(w, "Not Found", http.StatusNotFound) 90 } 91 92 url := r.URL 93 url.Host = r.Host 94 url.Scheme = "https" 95 http.Redirect(w, r, url.String(), http.StatusFound) 96 } 97 98 func (a *App) StartServer() error { 99 l4g.Info(utils.T("api.server.start_server.starting.info")) 100 101 var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router} 102 103 if *a.Config().RateLimitSettings.Enable { 104 l4g.Info(utils.T("api.server.start_server.rate.info")) 105 106 rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings) 107 if err != nil { 108 return err 109 } 110 111 a.Srv.RateLimiter = rateLimiter 112 handler = rateLimiter.RateLimitHandler(handler) 113 } 114 115 a.Srv.Server = &http.Server{ 116 Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), 117 ReadTimeout: time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second, 118 WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second, 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 l4g.Info(utils.T("api.server.start_server.listening.info"), 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, _, err := net.SplitHostPort(addr); err != nil { 153 l4g.Error("Unable to setup forwarding: " + err.Error()) 154 } else { 155 httpListenAddress := net.JoinHostPort(host, "http") 156 157 if *a.Config().ServiceSettings.UseLetsEncrypt { 158 go http.ListenAndServe(httpListenAddress, m.HTTPHandler(nil)) 159 } else { 160 go func() { 161 redirectListener, err := net.Listen("tcp", httpListenAddress) 162 if err != nil { 163 l4g.Error("Unable to setup forwarding: " + err.Error()) 164 return 165 } 166 defer redirectListener.Close() 167 168 http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS)) 169 }() 170 } 171 } 172 } 173 174 a.Srv.didFinishListen = make(chan struct{}) 175 go func() { 176 var err error 177 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 178 if *a.Config().ServiceSettings.UseLetsEncrypt { 179 180 tlsConfig := &tls.Config{ 181 GetCertificate: m.GetCertificate, 182 } 183 184 tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") 185 186 a.Srv.Server.TLSConfig = tlsConfig 187 err = a.Srv.Server.ServeTLS(listener, "", "") 188 } else { 189 err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile) 190 } 191 } else { 192 err = a.Srv.Server.Serve(listener) 193 } 194 if err != nil && err != http.ErrServerClosed { 195 l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) 196 time.Sleep(time.Second) 197 } 198 close(a.Srv.didFinishListen) 199 }() 200 201 return nil 202 } 203 204 func (a *App) StopServer() { 205 if a.Srv.Server != nil { 206 ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) 207 defer cancel() 208 didShutdown := false 209 for a.Srv.didFinishListen != nil && !didShutdown { 210 if err := a.Srv.Server.Shutdown(ctx); err != nil { 211 l4g.Warn(err.Error()) 212 } 213 timer := time.NewTimer(time.Millisecond * 50) 214 select { 215 case <-a.Srv.didFinishListen: 216 didShutdown = true 217 case <-timer.C: 218 } 219 timer.Stop() 220 } 221 a.Srv.Server.Close() 222 a.Srv.Server = nil 223 } 224 } 225 226 func (a *App) OriginChecker() func(*http.Request) bool { 227 if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" { 228 return utils.OriginChecker(allowed) 229 } 230 return nil 231 } 232 233 // This is required to re-use the underlying connection and not take up file descriptors 234 func consumeAndClose(r *http.Response) { 235 if r.Body != nil { 236 io.Copy(ioutil.Discard, r.Body) 237 r.Body.Close() 238 } 239 }