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  }