...

Source file src/github.com/palantir/go-baseapp/baseapp/server.go

Documentation: github.com/palantir/go-baseapp/baseapp

     1  // Copyright 2018 Palantir Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package baseapp
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"net/http"
    22  	"os"
    23  	"os/signal"
    24  	"strconv"
    25  	"sync"
    26  	"syscall"
    27  
    28  	"github.com/pkg/errors"
    29  	"github.com/rcrowley/go-metrics"
    30  	"github.com/rs/zerolog"
    31  	"goji.io"
    32  )
    33  
    34  // Server is the base server type. It is usually embedded in an
    35  // application-specific struct.
    36  type Server struct {
    37  	config     HTTPConfig
    38  	middleware []func(http.Handler) http.Handler
    39  	logger     zerolog.Logger
    40  	mux        *goji.Mux
    41  	server     *http.Server
    42  
    43  	registry metrics.Registry
    44  
    45  	// functions that are called once on start
    46  	initFns []func(*Server)
    47  	init    sync.Once
    48  }
    49  
    50  // Param configures a Server instance.
    51  type Param func(b *Server) error
    52  
    53  // NewServer creates a Server instance from configuration and parameters.
    54  func NewServer(c HTTPConfig, params ...Param) (*Server, error) {
    55  	logger := zerolog.Nop()
    56  	base := &Server{
    57  		config:     c,
    58  		middleware: nil,
    59  		logger:     logger,
    60  		mux:        goji.NewMux(),
    61  		registry:   metrics.DefaultRegistry,
    62  	}
    63  
    64  	for _, p := range params {
    65  		if err := p(base); err != nil {
    66  			return base, err
    67  		}
    68  	}
    69  
    70  	if base.middleware == nil {
    71  		base.middleware = DefaultMiddleware(base.logger, base.registry)
    72  	}
    73  
    74  	for _, middleware := range base.middleware {
    75  		base.mux.Use(middleware)
    76  	}
    77  
    78  	if base.server == nil {
    79  		base.server = &http.Server{}
    80  	}
    81  
    82  	if base.server.Addr == "" {
    83  		addr := c.Address + ":" + strconv.Itoa(c.Port)
    84  		base.server.Addr = addr
    85  	}
    86  
    87  	if base.server.Handler == nil {
    88  		base.server.Handler = base.mux
    89  	}
    90  
    91  	return base, nil
    92  }
    93  
    94  // HTTPConfig returns the server configuration.
    95  func (s *Server) HTTPConfig() HTTPConfig {
    96  	return s.config
    97  }
    98  
    99  // HTTPServer returns the underlying HTTP Server.
   100  func (s *Server) HTTPServer() *http.Server {
   101  	return s.server
   102  }
   103  
   104  // Mux returns the root mux for the server.
   105  func (s *Server) Mux() *goji.Mux {
   106  	return s.mux
   107  }
   108  
   109  // Logger returns the root logger for the server.
   110  func (s *Server) Logger() zerolog.Logger {
   111  	return s.logger
   112  }
   113  
   114  // Registry returns the root metrics registry for the server.
   115  func (s *Server) Registry() metrics.Registry {
   116  	return s.registry
   117  }
   118  
   119  // Start starts the server and blocks.
   120  func (s *Server) start() error {
   121  	s.init.Do(func() {
   122  		for _, fn := range s.initFns {
   123  			fn(s)
   124  		}
   125  	})
   126  
   127  	addr := s.config.Address + ":" + strconv.Itoa(s.config.Port)
   128  	s.logger.Info().Msgf("Server listening on %s", addr)
   129  
   130  	tlsConfig := s.config.TLSConfig
   131  	if tlsConfig != nil {
   132  		return s.server.ListenAndServeTLS(tlsConfig.CertFile, tlsConfig.KeyFile)
   133  	}
   134  
   135  	return s.server.ListenAndServe()
   136  }
   137  
   138  // Start starts the server and blocks.
   139  func (s *Server) Start() error {
   140  	// maintain backwards compatibility
   141  	if s.config.ShutdownWaitTime == nil {
   142  		return s.start()
   143  	}
   144  
   145  	quit := make(chan error)
   146  	go func() {
   147  		if err := s.start(); err != nil {
   148  			quit <- err
   149  		}
   150  	}()
   151  
   152  	// SIGKILL and SIGSTOP cannot be caught, so don't bother adding them here
   153  	interrupt := make(chan os.Signal, 2)
   154  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   155  
   156  	select {
   157  	case <-interrupt:
   158  		s.logger.Info().Msg("Caught interrupt, gracefully shutting down")
   159  	case err := <-quit:
   160  		if err != http.ErrServerClosed {
   161  			return err
   162  		}
   163  	}
   164  
   165  	ctx, cancel := context.WithTimeout(context.Background(), *s.config.ShutdownWaitTime)
   166  	defer cancel()
   167  	return errors.Wrap(s.HTTPServer().Shutdown(ctx), "Failed shutting down gracefully")
   168  }
   169  
   170  // WriteJSON writes a JSON response or an error if mashalling the object fails.
   171  func WriteJSON(w http.ResponseWriter, status int, obj interface{}) {
   172  	w.Header().Set("Content-Type", "application/json")
   173  
   174  	b, err := json.Marshal(obj)
   175  	if err != nil {
   176  		w.WriteHeader(http.StatusInternalServerError)
   177  		_, _ = fmt.Fprintf(w, `{"error": %s}`, strconv.Quote(err.Error()))
   178  	} else {
   179  		w.WriteHeader(status)
   180  		_, _ = w.Write(b)
   181  	}
   182  }
   183  

View as plain text