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  }