github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+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 "strings" 14 "time" 15 16 l4g "github.com/alecthomas/log4go" 17 "github.com/gorilla/handlers" 18 "github.com/gorilla/mux" 19 "github.com/rsc/letsencrypt" 20 "gopkg.in/throttled/throttled.v2" 21 "gopkg.in/throttled/throttled.v2/store/memstore" 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 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 l4g.Error("Please check the std error output for the stack trace") 52 l4g.Error(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 type VaryBy struct{} 87 88 func (m *VaryBy) Key(r *http.Request) string { 89 return utils.GetIpAddress(r) 90 } 91 92 func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) { 93 if r.Host == "" { 94 http.Error(w, "Not Found", http.StatusNotFound) 95 } 96 97 url := r.URL 98 url.Host = r.Host 99 url.Scheme = "https" 100 http.Redirect(w, r, url.String(), http.StatusFound) 101 } 102 103 func (a *App) StartServer() { 104 l4g.Info(utils.T("api.server.start_server.starting.info")) 105 106 var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router} 107 108 if *a.Config().RateLimitSettings.Enable { 109 l4g.Info(utils.T("api.server.start_server.rate.info")) 110 111 store, err := memstore.New(*a.Config().RateLimitSettings.MemoryStoreSize) 112 if err != nil { 113 l4g.Critical(utils.T("api.server.start_server.rate_limiting_memory_store")) 114 return 115 } 116 117 quota := throttled.RateQuota{ 118 MaxRate: throttled.PerSec(*a.Config().RateLimitSettings.PerSec), 119 MaxBurst: *a.Config().RateLimitSettings.MaxBurst, 120 } 121 122 rateLimiter, err := throttled.NewGCRARateLimiter(store, quota) 123 if err != nil { 124 l4g.Critical(utils.T("api.server.start_server.rate_limiting_rate_limiter")) 125 return 126 } 127 128 httpRateLimiter := throttled.HTTPRateLimiter{ 129 RateLimiter: rateLimiter, 130 VaryBy: &VaryBy{}, 131 DeniedHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 132 l4g.Error("%v: Denied due to throttling settings code=429 ip=%v", r.URL.Path, utils.GetIpAddress(r)) 133 throttled.DefaultDeniedHandler.ServeHTTP(w, r) 134 }), 135 } 136 137 handler = httpRateLimiter.RateLimit(handler) 138 } 139 140 a.Srv.Server = &http.Server{ 141 Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler), 142 ReadTimeout: time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second, 143 WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second, 144 } 145 146 addr := *a.Config().ServiceSettings.ListenAddress 147 if addr == "" { 148 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 149 addr = ":https" 150 } else { 151 addr = ":http" 152 } 153 } 154 155 listener, err := net.Listen("tcp", addr) 156 if err != nil { 157 l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) 158 return 159 } 160 a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr) 161 162 l4g.Info(utils.T("api.server.start_server.listening.info"), listener.Addr().String()) 163 164 if *a.Config().ServiceSettings.Forward80To443 { 165 go func() { 166 redirectListener, err := net.Listen("tcp", ":80") 167 if err != nil { 168 listener.Close() 169 l4g.Error("Unable to setup forwarding: " + err.Error()) 170 return 171 } 172 defer redirectListener.Close() 173 174 http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS)) 175 }() 176 } 177 178 a.Srv.didFinishListen = make(chan struct{}) 179 go func() { 180 var err error 181 if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS { 182 if *a.Config().ServiceSettings.UseLetsEncrypt { 183 var m letsencrypt.Manager 184 m.CacheFile(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile) 185 186 tlsConfig := &tls.Config{ 187 GetCertificate: m.GetCertificate, 188 } 189 190 tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2") 191 192 a.Srv.Server.TLSConfig = tlsConfig 193 err = a.Srv.Server.ServeTLS(listener, "", "") 194 } else { 195 err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile) 196 } 197 } else { 198 err = a.Srv.Server.Serve(listener) 199 } 200 if err != nil && err != http.ErrServerClosed { 201 l4g.Critical(utils.T("api.server.start_server.starting.critical"), err) 202 time.Sleep(time.Second) 203 } 204 close(a.Srv.didFinishListen) 205 }() 206 } 207 208 type tcpKeepAliveListener struct { 209 *net.TCPListener 210 } 211 212 func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 213 tc, err := ln.AcceptTCP() 214 if err != nil { 215 return 216 } 217 tc.SetKeepAlive(true) 218 tc.SetKeepAlivePeriod(3 * time.Minute) 219 return tc, nil 220 } 221 222 func (a *App) Listen(addr string) (net.Listener, error) { 223 if addr == "" { 224 addr = ":http" 225 } 226 ln, err := net.Listen("tcp", addr) 227 if err != nil { 228 return nil, err 229 } 230 return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil 231 } 232 233 func (a *App) StopServer() { 234 if a.Srv.Server != nil { 235 ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN) 236 defer cancel() 237 didShutdown := false 238 for a.Srv.didFinishListen != nil && !didShutdown { 239 if err := a.Srv.Server.Shutdown(ctx); err != nil { 240 l4g.Warn(err.Error()) 241 } 242 timer := time.NewTimer(time.Millisecond * 50) 243 select { 244 case <-a.Srv.didFinishListen: 245 didShutdown = true 246 case <-timer.C: 247 } 248 timer.Stop() 249 } 250 a.Srv.Server.Close() 251 a.Srv.Server = nil 252 } 253 } 254 255 func (a *App) OriginChecker() func(*http.Request) bool { 256 if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" { 257 return utils.OriginChecker(allowed) 258 } 259 return nil 260 } 261 262 // This is required to re-use the underlying connection and not take up file descriptors 263 func consumeAndClose(r *http.Response) { 264 if r.Body != nil { 265 io.Copy(ioutil.Discard, r.Body) 266 r.Body.Close() 267 } 268 }