summaryrefslogtreecommitdiffstats
path: root/app/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'app/server.go')
-rw-r--r--app/server.go124
1 files changed, 95 insertions, 29 deletions
diff --git a/app/server.go b/app/server.go
index 5f955dd65..d686c1f24 100644
--- a/app/server.go
+++ b/app/server.go
@@ -4,7 +4,10 @@
package app
import (
+ "context"
"crypto/tls"
+ "io"
+ "io/ioutil"
"net"
"net/http"
"strings"
@@ -14,13 +17,11 @@ import (
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/rsc/letsencrypt"
- "github.com/tylerb/graceful"
"gopkg.in/throttled/throttled.v2"
"gopkg.in/throttled/throttled.v2/store/memstore"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
- "github.com/mattermost/mattermost-server/store/sqlstore"
"github.com/mattermost/mattermost-server/utils"
)
@@ -28,7 +29,10 @@ type Server struct {
Store store.Store
WebSocketRouter *WebSocketRouter
Router *mux.Router
- GracefulServer *graceful.Server
+ Server *http.Server
+ ListenAddr *net.TCPAddr
+
+ didFinishListen chan struct{}
}
var allowedMethods []string = []string{
@@ -78,16 +82,6 @@ func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second
-func (a *App) NewServer() {
- l4g.Info(utils.T("api.server.new_server.init.info"))
-
- a.Srv = &Server{}
-}
-
-func (a *App) InitStores() {
- a.Srv.Store = store.NewLayeredStore(sqlstore.NewSqlSupplier(a.Metrics), a.Metrics, a.Cluster)
-}
-
type VaryBy struct{}
func (m *VaryBy) Key(r *http.Request) string {
@@ -161,30 +155,45 @@ func (a *App) StartServer() {
handler = httpRateLimiter.RateLimit(handler)
}
- a.Srv.GracefulServer = &graceful.Server{
- Timeout: TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN,
- Server: &http.Server{
- Addr: *utils.Cfg.ServiceSettings.ListenAddress,
- Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler),
- ReadTimeout: time.Duration(*utils.Cfg.ServiceSettings.ReadTimeout) * time.Second,
- WriteTimeout: time.Duration(*utils.Cfg.ServiceSettings.WriteTimeout) * time.Second,
- },
+ a.Srv.Server = &http.Server{
+ Handler: handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler),
+ ReadTimeout: time.Duration(*utils.Cfg.ServiceSettings.ReadTimeout) * time.Second,
+ WriteTimeout: time.Duration(*utils.Cfg.ServiceSettings.WriteTimeout) * time.Second,
+ }
+
+ addr := *a.Config().ServiceSettings.ListenAddress
+ if addr == "" {
+ if *utils.Cfg.ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
+ addr = ":https"
+ } else {
+ addr = ":http"
+ }
+ }
+
+ listener, err := net.Listen("tcp", addr)
+ if err != nil {
+ l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
+ return
}
- l4g.Info(utils.T("api.server.start_server.listening.info"), *utils.Cfg.ServiceSettings.ListenAddress)
+ a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr)
+
+ l4g.Info(utils.T("api.server.start_server.listening.info"), listener.Addr().String())
if *utils.Cfg.ServiceSettings.Forward80To443 {
go func() {
- listener, err := net.Listen("tcp", ":80")
+ redirectListener, err := net.Listen("tcp", ":80")
if err != nil {
+ listener.Close()
l4g.Error("Unable to setup forwarding")
return
}
- defer listener.Close()
+ defer redirectListener.Close()
- http.Serve(listener, http.HandlerFunc(redirectHTTPToHTTPS))
+ http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS))
}()
}
+ a.Srv.didFinishListen = make(chan struct{})
go func() {
var err error
if *utils.Cfg.ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
@@ -198,16 +207,73 @@ func (a *App) StartServer() {
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
- err = a.Srv.GracefulServer.ListenAndServeTLSConfig(tlsConfig)
+ a.Srv.Server.TLSConfig = tlsConfig
+ err = a.Srv.Server.ServeTLS(listener, "", "")
} else {
- err = a.Srv.GracefulServer.ListenAndServeTLS(*utils.Cfg.ServiceSettings.TLSCertFile, *utils.Cfg.ServiceSettings.TLSKeyFile)
+ err = a.Srv.Server.ServeTLS(listener, *utils.Cfg.ServiceSettings.TLSCertFile, *utils.Cfg.ServiceSettings.TLSKeyFile)
}
} else {
- err = a.Srv.GracefulServer.ListenAndServe()
+ err = a.Srv.Server.Serve(listener)
}
- if err != nil {
+ if err != nil && err != http.ErrServerClosed {
l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
time.Sleep(time.Second)
}
+ close(a.Srv.didFinishListen)
}()
}
+
+type tcpKeepAliveListener struct {
+ *net.TCPListener
+}
+
+func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
+ tc, err := ln.AcceptTCP()
+ if err != nil {
+ return
+ }
+ tc.SetKeepAlive(true)
+ tc.SetKeepAlivePeriod(3 * time.Minute)
+ return tc, nil
+}
+
+func (a *App) Listen(addr string) (net.Listener, error) {
+ if addr == "" {
+ addr = ":http"
+ }
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+ return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
+}
+
+func (a *App) StopServer() {
+ if a.Srv.Server != nil {
+ ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
+ defer cancel()
+ didShutdown := false
+ for a.Srv.didFinishListen != nil && !didShutdown {
+ if err := a.Srv.Server.Shutdown(ctx); err != nil {
+ l4g.Warn(err.Error())
+ }
+ timer := time.NewTimer(time.Millisecond * 50)
+ select {
+ case <-a.Srv.didFinishListen:
+ didShutdown = true
+ case <-timer.C:
+ }
+ timer.Stop()
+ }
+ a.Srv.Server.Close()
+ a.Srv.Server = nil
+ }
+}
+
+// This is required to re-use the underlying connection and not take up file descriptors
+func consumeAndClose(r *http.Response) {
+ if r.Body != nil {
+ io.Copy(ioutil.Discard, r.Body)
+ r.Body.Close()
+ }
+}