github.com/dschalla/mattermost-server@v4.8.1-rc1+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  type VaryBy struct {
    88  	useIP   bool
    89  	useAuth bool
    90  }
    91  
    92  func (m *VaryBy) Key(r *http.Request) string {
    93  	key := ""
    94  
    95  	if m.useAuth {
    96  		token, tokenLocation := ParseAuthTokenFromRequest(r)
    97  		if tokenLocation != TokenLocationNotFound {
    98  			key += token
    99  		} else if m.useIP { // If we don't find an authentication token and IP based is enabled, fall back to IP
   100  			key += utils.GetIpAddress(r)
   101  		}
   102  	} else if m.useIP { // Only if Auth based is not enabed do we use a plain IP based
   103  		key = utils.GetIpAddress(r)
   104  	}
   105  
   106  	return key
   107  }
   108  
   109  func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
   110  	if r.Host == "" {
   111  		http.Error(w, "Not Found", http.StatusNotFound)
   112  	}
   113  
   114  	url := r.URL
   115  	url.Host = r.Host
   116  	url.Scheme = "https"
   117  	http.Redirect(w, r, url.String(), http.StatusFound)
   118  }
   119  
   120  func (a *App) StartServer() error {
   121  	l4g.Info(utils.T("api.server.start_server.starting.info"))
   122  
   123  	var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router}
   124  
   125  	if *a.Config().RateLimitSettings.Enable {
   126  		l4g.Info(utils.T("api.server.start_server.rate.info"))
   127  
   128  		rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings)
   129  		if err != nil {
   130  			return err
   131  		}
   132  
   133  		a.Srv.RateLimiter = rateLimiter
   134  		handler = rateLimiter.RateLimitHandler(handler)
   135  	}
   136  
   137  	a.Srv.Server = &http.Server{
   138  		Handler:      handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler),
   139  		ReadTimeout:  time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second,
   140  		WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second,
   141  	}
   142  
   143  	addr := *a.Config().ServiceSettings.ListenAddress
   144  	if addr == "" {
   145  		if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
   146  			addr = ":https"
   147  		} else {
   148  			addr = ":http"
   149  		}
   150  	}
   151  
   152  	listener, err := net.Listen("tcp", addr)
   153  	if err != nil {
   154  		errors.Wrapf(err, utils.T("api.server.start_server.starting.critical"), err)
   155  		return err
   156  	}
   157  	a.Srv.ListenAddr = listener.Addr().(*net.TCPAddr)
   158  
   159  	l4g.Info(utils.T("api.server.start_server.listening.info"), listener.Addr().String())
   160  
   161  	// Migration from old let's encrypt library
   162  	if *a.Config().ServiceSettings.UseLetsEncrypt {
   163  		if stat, err := os.Stat(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile); err == nil && !stat.IsDir() {
   164  			os.Remove(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile)
   165  		}
   166  	}
   167  
   168  	m := &autocert.Manager{
   169  		Cache:  autocert.DirCache(*a.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
   170  		Prompt: autocert.AcceptTOS,
   171  	}
   172  
   173  	if *a.Config().ServiceSettings.Forward80To443 {
   174  		if host, _, err := net.SplitHostPort(addr); err != nil {
   175  			l4g.Error("Unable to setup forwarding: " + err.Error())
   176  		} else {
   177  			httpListenAddress := net.JoinHostPort(host, "http")
   178  
   179  			if *a.Config().ServiceSettings.UseLetsEncrypt {
   180  				go http.ListenAndServe(httpListenAddress, m.HTTPHandler(nil))
   181  			} else {
   182  				go func() {
   183  					redirectListener, err := net.Listen("tcp", httpListenAddress)
   184  					if err != nil {
   185  						l4g.Error("Unable to setup forwarding: " + err.Error())
   186  						return
   187  					}
   188  					defer redirectListener.Close()
   189  
   190  					http.Serve(redirectListener, http.HandlerFunc(redirectHTTPToHTTPS))
   191  				}()
   192  			}
   193  		}
   194  	}
   195  
   196  	a.Srv.didFinishListen = make(chan struct{})
   197  	go func() {
   198  		var err error
   199  		if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
   200  			if *a.Config().ServiceSettings.UseLetsEncrypt {
   201  
   202  				tlsConfig := &tls.Config{
   203  					GetCertificate: m.GetCertificate,
   204  				}
   205  
   206  				tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
   207  
   208  				a.Srv.Server.TLSConfig = tlsConfig
   209  				err = a.Srv.Server.ServeTLS(listener, "", "")
   210  			} else {
   211  				err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile)
   212  			}
   213  		} else {
   214  			err = a.Srv.Server.Serve(listener)
   215  		}
   216  		if err != nil && err != http.ErrServerClosed {
   217  			l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
   218  			time.Sleep(time.Second)
   219  		}
   220  		close(a.Srv.didFinishListen)
   221  	}()
   222  
   223  	return nil
   224  }
   225  
   226  type tcpKeepAliveListener struct {
   227  	*net.TCPListener
   228  }
   229  
   230  func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
   231  	tc, err := ln.AcceptTCP()
   232  	if err != nil {
   233  		return
   234  	}
   235  	tc.SetKeepAlive(true)
   236  	tc.SetKeepAlivePeriod(3 * time.Minute)
   237  	return tc, nil
   238  }
   239  
   240  func (a *App) Listen(addr string) (net.Listener, error) {
   241  	if addr == "" {
   242  		addr = ":http"
   243  	}
   244  	ln, err := net.Listen("tcp", addr)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
   249  }
   250  
   251  func (a *App) StopServer() {
   252  	if a.Srv.Server != nil {
   253  		ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
   254  		defer cancel()
   255  		didShutdown := false
   256  		for a.Srv.didFinishListen != nil && !didShutdown {
   257  			if err := a.Srv.Server.Shutdown(ctx); err != nil {
   258  				l4g.Warn(err.Error())
   259  			}
   260  			timer := time.NewTimer(time.Millisecond * 50)
   261  			select {
   262  			case <-a.Srv.didFinishListen:
   263  				didShutdown = true
   264  			case <-timer.C:
   265  			}
   266  			timer.Stop()
   267  		}
   268  		a.Srv.Server.Close()
   269  		a.Srv.Server = nil
   270  	}
   271  }
   272  
   273  func (a *App) OriginChecker() func(*http.Request) bool {
   274  	if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" {
   275  		return utils.OriginChecker(allowed)
   276  	}
   277  	return nil
   278  }
   279  
   280  // This is required to re-use the underlying connection and not take up file descriptors
   281  func consumeAndClose(r *http.Response) {
   282  	if r.Body != nil {
   283  		io.Copy(ioutil.Discard, r.Body)
   284  		r.Body.Close()
   285  	}
   286  }