github.com/cornelk/go-cloud@v0.17.1/server/server.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit Authors
     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  //     https://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 server provides a preconfigured HTTP server with diagnostic hooks.
    16  package server // import "github.com/cornelk/go-cloud/server"
    17  
    18  import (
    19  	"context"
    20  	"net/http"
    21  	"path"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/cornelk/go-cloud/server/driver"
    26  	"github.com/cornelk/go-cloud/server/health"
    27  	"github.com/cornelk/go-cloud/server/requestlog"
    28  	"github.com/google/wire"
    29  
    30  	"go.opencensus.io/trace"
    31  )
    32  
    33  // Set is a Wire provider set that produces a *Server given the fields of
    34  // Options.
    35  var Set = wire.NewSet(
    36  	New,
    37  	wire.Struct(new(Options), "RequestLogger", "HealthChecks", "TraceExporter", "DefaultSamplingPolicy", "Driver"),
    38  	wire.Value(&DefaultDriver{}),
    39  	wire.Bind(new(driver.Server), new(*DefaultDriver)),
    40  )
    41  
    42  // Server is a preconfigured HTTP server with diagnostic hooks.
    43  // The zero value is a server with the default options.
    44  type Server struct {
    45  	reqlog        requestlog.Logger
    46  	handler       http.Handler
    47  	healthHandler health.Handler
    48  	te            trace.Exporter
    49  	sampler       trace.Sampler
    50  	once          sync.Once
    51  	driver        driver.Server
    52  }
    53  
    54  // Options is the set of optional parameters.
    55  type Options struct {
    56  	// RequestLogger specifies the logger that will be used to log requests.
    57  	RequestLogger requestlog.Logger
    58  
    59  	// HealthChecks specifies the health checks to be run when the
    60  	// /healthz/readiness endpoint is requested.
    61  	HealthChecks []health.Checker
    62  
    63  	// TraceExporter exports sampled trace spans.
    64  	TraceExporter trace.Exporter
    65  
    66  	// DefaultSamplingPolicy is a function that takes a
    67  	// trace.SamplingParameters struct and returns a true or false decision about
    68  	// whether it should be sampled and exported.
    69  	DefaultSamplingPolicy trace.Sampler
    70  
    71  	// Driver serves HTTP requests.
    72  	Driver driver.Server
    73  }
    74  
    75  // New creates a new server. New(nil, nil) is the same as new(Server).
    76  func New(h http.Handler, opts *Options) *Server {
    77  	srv := &Server{handler: h}
    78  	if opts != nil {
    79  		srv.reqlog = opts.RequestLogger
    80  		srv.te = opts.TraceExporter
    81  		for _, c := range opts.HealthChecks {
    82  			srv.healthHandler.Add(c)
    83  		}
    84  		srv.sampler = opts.DefaultSamplingPolicy
    85  		srv.driver = opts.Driver
    86  	}
    87  	return srv
    88  }
    89  
    90  func (srv *Server) init() {
    91  	srv.once.Do(func() {
    92  		if srv.te != nil {
    93  			trace.RegisterExporter(srv.te)
    94  		}
    95  		if srv.sampler != nil {
    96  			trace.ApplyConfig(trace.Config{DefaultSampler: srv.sampler})
    97  		}
    98  		if srv.driver == nil {
    99  			srv.driver = NewDefaultDriver()
   100  		}
   101  		if srv.handler == nil {
   102  			srv.handler = http.DefaultServeMux
   103  		}
   104  	})
   105  }
   106  
   107  // ListenAndServe is a wrapper to use wherever http.ListenAndServe is used.
   108  // It wraps the passed-in http.Handler with a handler that handles tracing and
   109  // request logging. If the handler is nil, then http.DefaultServeMux will be used.
   110  // A configured Requestlogger will log all requests except HealthChecks.
   111  func (srv *Server) ListenAndServe(addr string) error {
   112  	srv.init()
   113  
   114  	// Setup health checks, /healthz route is taken by health checks by default.
   115  	// Note: App Engine Flex uses /_ah/health by default, which can be changed
   116  	// in app.yaml. We may want to do an auto-detection for flex in future.
   117  	hr := "/healthz/"
   118  	hcMux := http.NewServeMux()
   119  	hcMux.HandleFunc(path.Join(hr, "liveness"), health.HandleLive)
   120  	hcMux.Handle(path.Join(hr, "readiness"), &srv.healthHandler)
   121  
   122  	mux := http.NewServeMux()
   123  	mux.Handle(hr, hcMux)
   124  	h := srv.handler
   125  	if srv.reqlog != nil {
   126  		h = requestlog.NewHandler(srv.reqlog, h)
   127  	}
   128  	h = http.Handler(handler{h})
   129  	mux.Handle("/", h)
   130  
   131  	return srv.driver.ListenAndServe(addr, mux)
   132  }
   133  
   134  // Shutdown gracefully shuts down the server without interrupting any active connections.
   135  func (srv *Server) Shutdown(ctx context.Context) error {
   136  	if srv.driver == nil {
   137  		return nil
   138  	}
   139  	return srv.driver.Shutdown(ctx)
   140  }
   141  
   142  // handler is a handler wrapper that handles tracing through OpenCensus for users.
   143  // TODO(shantuo): unify handler types from trace, requestlog, health checks, etc together.
   144  type handler struct {
   145  	handler http.Handler
   146  }
   147  
   148  func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   149  	ctx, span := trace.StartSpan(r.Context(), r.URL.Host+r.URL.Path)
   150  	defer span.End()
   151  
   152  	r = r.WithContext(ctx)
   153  	h.handler.ServeHTTP(w, r)
   154  }
   155  
   156  // DefaultDriver implements the driver.Server interface. The zero value is a valid http.Server.
   157  type DefaultDriver struct {
   158  	Server http.Server
   159  }
   160  
   161  // NewDefaultDriver creates a driver with an http.Server with default timeouts.
   162  func NewDefaultDriver() *DefaultDriver {
   163  	return &DefaultDriver{
   164  		Server: http.Server{
   165  			ReadTimeout:  30 * time.Second,
   166  			WriteTimeout: 30 * time.Second,
   167  			IdleTimeout:  120 * time.Second,
   168  		},
   169  	}
   170  }
   171  
   172  // ListenAndServe sets the address and handler on DefaultDriver's http.Server,
   173  // then calls ListenAndServe on it.
   174  func (dd *DefaultDriver) ListenAndServe(addr string, h http.Handler) error {
   175  	dd.Server.Addr = addr
   176  	dd.Server.Handler = h
   177  	return dd.Server.ListenAndServe()
   178  }
   179  
   180  // Shutdown gracefully shuts down the server without interrupting any active connections,
   181  // by calling Shutdown on DefaultDriver's http.Server
   182  func (dd *DefaultDriver) Shutdown(ctx context.Context) error {
   183  	return dd.Server.Shutdown(ctx)
   184  }