github.com/xmidt-org/webpa-common@v1.11.9/xhttp/server.go (about) 1 package xhttp 2 3 import ( 4 stdlog "log" 5 "net" 6 "net/http" 7 "time" 8 9 "github.com/go-kit/kit/log" 10 "github.com/go-kit/kit/log/level" 11 "github.com/xmidt-org/webpa-common/logging" 12 ) 13 14 var ( 15 serverKey interface{} = "server" 16 ) 17 18 // ServerKey returns the contextual logging key for the server name 19 func ServerKey() interface{} { 20 return serverKey 21 } 22 23 // NewServerLogger adapts a go-kit Logger onto a golang Logger in a way that is appropriate 24 // for http.Server.ErrorLog. 25 func NewServerLogger(logger log.Logger) *stdlog.Logger { 26 if logger == nil { 27 logger = logging.DefaultLogger() 28 } 29 30 return stdlog.New( 31 log.NewStdlibAdapter(logger), 32 "", // having a prefix gives the adapter trouble 33 stdlog.LstdFlags|stdlog.LUTC, 34 ) 35 } 36 37 // NewServerConnStateLogger adapts a go-kit Logger onto a connection state handler appropriate 38 // for http.Server.ConnState. 39 func NewServerConnStateLogger(logger log.Logger) func(net.Conn, http.ConnState) { 40 if logger == nil { 41 logger = logging.DefaultLogger() 42 } 43 44 return func(c net.Conn, cs http.ConnState) { 45 logger.Log( 46 "remoteAddress", c.RemoteAddr(), 47 "state", cs, 48 ) 49 } 50 } 51 52 // StartOptions represents the subset of server options that have to do with how 53 // an HTTP server is started. 54 type StartOptions struct { 55 // Logger is the go-kit Logger to use for server startup and error logging. If not 56 // supplied, logging.DefaultLogger() is used instead. 57 Logger log.Logger `json:"-"` 58 59 // Listener is the optional net.Listener to use. If not supplied, the http.Server default 60 // listener is used. 61 Listener net.Listener `json:"-"` 62 63 // DisableKeepAlives indicates whether the server should honor keep alives 64 DisableKeepAlives bool `json:"disableKeepAlives,omitempty"` 65 66 // CertificateFile is the HTTPS certificate file(s). If both this field and KeyFile are set, 67 // an HTTPS starter function is created. 68 CertificateFile []string `json:"certificateFile,omitempty"` 69 70 // KeyFile is the HTTPS key file(s). If both this field and CertificateFile are set, 71 // an HTTPS starter function is created. 72 KeyFile []string `json:"keyFile,omitempty"` 73 } 74 75 // NewStarter returns a starter closure for the given HTTP server. The start options are first 76 // applied to the server instance, and the server instance must not have already been started prior 77 // to invoking this method. 78 // 79 // The returned closure will invoke the correct method on the server to start it, e.g. Serve, ServeTLS, etc. 80 // The selection of which server method is based on the options. For example, if CertificateFile and KeyFile 81 // are set, either of the xxxTLS methods will be invoked based on whether there is a Listener configured. 82 // 83 // Note: tlsConfig is expected to already be set 84 func NewStarter(o StartOptions, s httpServer) func() error { 85 if o.Logger == nil { 86 o.Logger = logging.DefaultLogger() 87 } 88 89 s.SetKeepAlivesEnabled(!o.DisableKeepAlives) 90 91 var starter func() error 92 93 if o.Listener != nil { 94 starter = func() error { 95 return s.Serve(o.Listener) 96 } 97 } else { 98 starter = func() error { 99 return s.ListenAndServe() 100 } 101 } 102 103 return func() error { 104 o.Logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "starting server") 105 err := starter() 106 if err == http.ErrServerClosed { 107 o.Logger.Log(level.Key(), level.InfoValue(), logging.MessageKey(), "server closed") 108 } else { 109 o.Logger.Log(level.Key(), level.ErrorValue(), logging.MessageKey(), "server exited", logging.ErrorKey(), err) 110 } 111 112 return err 113 } 114 } 115 116 // httpServer exposes the set of methods expected of an http.Server by this package. 117 type httpServer interface { 118 ListenAndServe() error 119 ListenAndServeTLS(string, string) error 120 121 Serve(net.Listener) error 122 ServeTLS(net.Listener, string, string) error 123 124 SetKeepAlivesEnabled(bool) 125 } 126 127 // ServerOptions describes the superset of options for both construction an http.Server and 128 // starting it. 129 type ServerOptions struct { 130 // Logger is the go-kit Logger to use for server startup and error logging. If not 131 // supplied, logging.DefaultLogger() is used instead. 132 Logger log.Logger `json:"-"` 133 134 // Address is the bind address of the server. If not supplied, defaults to the internal net/http default. 135 Address string `json:"address,omitempty"` 136 137 // ReadTimeout is the maximum duration for reading the entire request. If not supplied, defaults to the 138 // internal net/http default. 139 ReadTimeout time.Duration `json:"readTimeout,omitempty"` 140 141 // ReadHeaderTimeout is the amount of time allowed to read request headers. If not supplied, defaults to 142 // the internal net/http default. 143 ReadHeaderTimeout time.Duration `json:"readHeaderTimeout,omitempty"` 144 145 // WriteTimeout is the maximum duration before timing out writes of the response. If not supplied, defaults 146 // to the internal net/http default. 147 WriteTimeout time.Duration `json:"writeTimeout,omitempty"` 148 149 // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. 150 // If not supplied, defaults to the internal net/http default. 151 IdleTimeout time.Duration `json:"idleTimeout,omitempty"` 152 153 // MaxHeaderBytes controls the maximum number of bytes the server will read parsing the request header's 154 // keys and values. If not supplied, defaults to the internal net/http default. 155 MaxHeaderBytes int `json:"maxHeaderBytes,omitempty"` 156 157 // Listener is the optional net.Listener to use. If not supplied, the http.Server default 158 // listener is used. 159 Listener net.Listener `json:"-"` 160 161 // DisableKeepAlives indicates whether the server should honor keep alives 162 DisableKeepAlives bool `json:"disableKeepAlives,omitempty"` 163 164 // CertificateFile is the HTTPS certificate file. If both this field and KeyFile are set, 165 // an HTTPS starter function is created. 166 CertificateFile []string `json:"certificateFile,omitempty"` 167 168 // KeyFile is the HTTPS key file. If both this field and CertificateFile are set, 169 // an HTTPS starter function is created. 170 KeyFile []string `json:"keyFile,omitempty"` 171 } 172 173 // StartOptions produces a StartOptions with the corresponding values from this ServerOptions 174 func (so *ServerOptions) StartOptions() StartOptions { 175 logger := so.Logger 176 if logger == nil { 177 logger = logging.DefaultLogger() 178 } 179 180 return StartOptions{ 181 Logger: log.With(logger, 182 "address", so.Address, 183 ), 184 Listener: so.Listener, 185 DisableKeepAlives: so.DisableKeepAlives, 186 CertificateFile: so.CertificateFile, 187 KeyFile: so.KeyFile, 188 } 189 } 190 191 // NewServer creates a Server from a supplied set of options. 192 func NewServer(o ServerOptions) *http.Server { 193 return &http.Server{ 194 Addr: o.Address, 195 ReadTimeout: o.ReadTimeout, 196 ReadHeaderTimeout: o.ReadHeaderTimeout, 197 WriteTimeout: o.WriteTimeout, 198 IdleTimeout: o.IdleTimeout, 199 MaxHeaderBytes: o.MaxHeaderBytes, 200 ErrorLog: NewServerLogger(o.Logger), 201 ConnState: NewServerConnStateLogger(o.Logger), 202 } 203 }