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 }