github.com/spline-fu/mattermost-server@v4.10.10+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  	"net"
    11  	"net/http"
    12  	"os"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/gorilla/handlers"
    17  	"github.com/gorilla/mux"
    18  	"github.com/pkg/errors"
    19  	"golang.org/x/crypto/acme/autocert"
    20  
    21  	"github.com/mattermost/mattermost-server/mlog"
    22  	"github.com/mattermost/mattermost-server/model"
    23  	"github.com/mattermost/mattermost-server/store"
    24  	"github.com/mattermost/mattermost-server/utils"
    25  )
    26  
    27  type Server struct {
    28  	Store           store.Store
    29  	WebSocketRouter *WebSocketRouter
    30  	Router          *mux.Router
    31  	Server          *http.Server
    32  	ListenAddr      *net.TCPAddr
    33  	RateLimiter     *RateLimiter
    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  	mlog.Error("Please check the std error output for the stack trace")
    52  	mlog.Error(fmt.Sprint(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  func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
    87  	if r.Host == "" {
    88  		http.Error(w, "Not Found", http.StatusNotFound)
    89  	}
    90  
    91  	url := r.URL
    92  	url.Host = r.Host
    93  	url.Scheme = "https"
    94  	http.Redirect(w, r, url.String(), http.StatusFound)
    95  }
    96  
    97  func (a *App) StartServer() error {
    98  	mlog.Info("Starting Server...")
    99  
   100  	var handler http.Handler = &CorsWrapper{a.Config, a.Srv.Router}
   101  
   102  	if *a.Config().RateLimitSettings.Enable {
   103  		mlog.Info("RateLimiter is enabled")
   104  
   105  		rateLimiter, err := NewRateLimiter(&a.Config().RateLimitSettings)
   106  		if err != nil {
   107  			return err
   108  		}
   109  
   110  		a.Srv.RateLimiter = rateLimiter
   111  		handler = rateLimiter.RateLimitHandler(handler)
   112  	}
   113  
   114  	a.Srv.Server = &http.Server{
   115  		Handler:      handlers.RecoveryHandler(handlers.RecoveryLogger(&RecoveryLogger{}), handlers.PrintRecoveryStack(true))(handler),
   116  		ReadTimeout:  time.Duration(*a.Config().ServiceSettings.ReadTimeout) * time.Second,
   117  		WriteTimeout: time.Duration(*a.Config().ServiceSettings.WriteTimeout) * time.Second,
   118  		ErrorLog:     a.Log.StdLog(mlog.String("source", "httpserver")),
   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  	mlog.Info(fmt.Sprintf("Server is listening on %v", 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, port, err := net.SplitHostPort(addr); err != nil {
   153  			mlog.Error("Unable to setup forwarding: " + err.Error())
   154  		} else if port != "443" {
   155  			return fmt.Errorf(utils.T("api.server.start_server.forward80to443.enabled_but_listening_on_wrong_port"), port)
   156  		} else {
   157  			httpListenAddress := net.JoinHostPort(host, "http")
   158  
   159  			if *a.Config().ServiceSettings.UseLetsEncrypt {
   160  				server := &http.Server{
   161  					Addr:     httpListenAddress,
   162  					Handler:  m.HTTPHandler(nil),
   163  					ErrorLog: a.Log.StdLog(mlog.String("source", "le_forwarder_server")),
   164  				}
   165  				go server.ListenAndServe()
   166  			} else {
   167  				go func() {
   168  					redirectListener, err := net.Listen("tcp", httpListenAddress)
   169  					if err != nil {
   170  						mlog.Error("Unable to setup forwarding: " + err.Error())
   171  						return
   172  					}
   173  					defer redirectListener.Close()
   174  
   175  					server := &http.Server{
   176  						Handler:  handler,
   177  						ErrorLog: a.Log.StdLog(mlog.String("source", "forwarder_server")),
   178  					}
   179  					server.Serve(redirectListener)
   180  				}()
   181  			}
   182  		}
   183  	} else if *a.Config().ServiceSettings.UseLetsEncrypt {
   184  		return errors.New(utils.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt"))
   185  	}
   186  
   187  	a.Srv.didFinishListen = make(chan struct{})
   188  	go func() {
   189  		var err error
   190  		if *a.Config().ServiceSettings.ConnectionSecurity == model.CONN_SECURITY_TLS {
   191  			if *a.Config().ServiceSettings.UseLetsEncrypt {
   192  
   193  				tlsConfig := &tls.Config{
   194  					GetCertificate: m.GetCertificate,
   195  				}
   196  
   197  				tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
   198  
   199  				a.Srv.Server.TLSConfig = tlsConfig
   200  				err = a.Srv.Server.ServeTLS(listener, "", "")
   201  			} else {
   202  				err = a.Srv.Server.ServeTLS(listener, *a.Config().ServiceSettings.TLSCertFile, *a.Config().ServiceSettings.TLSKeyFile)
   203  			}
   204  		} else {
   205  			err = a.Srv.Server.Serve(listener)
   206  		}
   207  		if err != nil && err != http.ErrServerClosed {
   208  			mlog.Critical(fmt.Sprintf("Error starting server, err:%v", err))
   209  			time.Sleep(time.Second)
   210  		}
   211  		close(a.Srv.didFinishListen)
   212  	}()
   213  
   214  	return nil
   215  }
   216  
   217  func (a *App) StopServer() {
   218  	if a.Srv.Server != nil {
   219  		ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
   220  		defer cancel()
   221  		didShutdown := false
   222  		for a.Srv.didFinishListen != nil && !didShutdown {
   223  			if err := a.Srv.Server.Shutdown(ctx); err != nil {
   224  				mlog.Warn(err.Error())
   225  			}
   226  			timer := time.NewTimer(time.Millisecond * 50)
   227  			select {
   228  			case <-a.Srv.didFinishListen:
   229  				didShutdown = true
   230  			case <-timer.C:
   231  			}
   232  			timer.Stop()
   233  		}
   234  		a.Srv.Server.Close()
   235  		a.Srv.Server = nil
   236  	}
   237  }
   238  
   239  func (a *App) OriginChecker() func(*http.Request) bool {
   240  	if allowed := *a.Config().ServiceSettings.AllowCorsFrom; allowed != "" {
   241  		return utils.OriginChecker(allowed)
   242  	}
   243  	return nil
   244  }