github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server_graceful.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package ghttp 8 9 import ( 10 "context" 11 "crypto/tls" 12 "fmt" 13 "github.com/gogf/gf/errors/gcode" 14 "github.com/gogf/gf/errors/gerror" 15 "github.com/gogf/gf/os/gproc" 16 "github.com/gogf/gf/os/gres" 17 "github.com/gogf/gf/text/gstr" 18 "log" 19 "net" 20 "net/http" 21 "os" 22 ) 23 24 // gracefulServer wraps the net/http.Server with graceful reload/restart feature. 25 type gracefulServer struct { 26 server *Server // Belonged server. 27 fd uintptr // File descriptor for passing to child process when graceful reload. 28 address string // Listening address like:":80", ":8080". 29 httpServer *http.Server // Underlying http.Server. 30 rawListener net.Listener // Underlying net.Listener. 31 listener net.Listener // Wrapped net.Listener. 32 isHttps bool // Is HTTPS. 33 status int // Status of current server. 34 } 35 36 // newGracefulServer creates and returns a graceful http server with given address. 37 // The optional parameter <fd> specifies the file descriptor which is passed from parent server. 38 func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer { 39 // Change port to address like: 80 -> :80 40 if gstr.IsNumeric(address) { 41 address = ":" + address 42 } 43 gs := &gracefulServer{ 44 server: s, 45 address: address, 46 httpServer: s.newHttpServer(address), 47 } 48 if len(fd) > 0 && fd[0] > 0 { 49 gs.fd = uintptr(fd[0]) 50 } 51 return gs 52 } 53 54 // newGracefulServer creates and returns a underlying http.Server with given address. 55 func (s *Server) newHttpServer(address string) *http.Server { 56 server := &http.Server{ 57 Addr: address, 58 Handler: s.config.Handler, 59 ReadTimeout: s.config.ReadTimeout, 60 WriteTimeout: s.config.WriteTimeout, 61 IdleTimeout: s.config.IdleTimeout, 62 MaxHeaderBytes: s.config.MaxHeaderBytes, 63 ErrorLog: log.New(&errorLogger{logger: s.config.Logger}, "", 0), 64 } 65 server.SetKeepAlivesEnabled(s.config.KeepAlive) 66 return server 67 } 68 69 // ListenAndServe starts listening on configured address. 70 func (s *gracefulServer) ListenAndServe() error { 71 ln, err := s.getNetListener() 72 if err != nil { 73 return err 74 } 75 s.listener = ln 76 s.rawListener = ln 77 return s.doServe() 78 } 79 80 // Fd retrieves and returns the file descriptor of current server. 81 // It is available ony in *nix like operation systems like: linux, unix, darwin. 82 func (s *gracefulServer) Fd() uintptr { 83 if s.rawListener != nil { 84 file, err := s.rawListener.(*net.TCPListener).File() 85 if err == nil { 86 return file.Fd() 87 } 88 } 89 return 0 90 } 91 92 // setFd sets the file descriptor for current server. 93 func (s *gracefulServer) setFd(fd int) { 94 s.fd = uintptr(fd) 95 } 96 97 // ListenAndServeTLS starts listening on configured address with HTTPS. 98 // The parameter <certFile> and <keyFile> specify the necessary certification and key files for HTTPS. 99 // The optional parameter <tlsConfig> specifies the custom TLS configuration. 100 func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error { 101 var config *tls.Config 102 if len(tlsConfig) > 0 && tlsConfig[0] != nil { 103 config = tlsConfig[0] 104 } else if s.httpServer.TLSConfig != nil { 105 config = s.httpServer.TLSConfig 106 } else { 107 config = &tls.Config{} 108 } 109 if config.NextProtos == nil { 110 config.NextProtos = []string{"http/1.1"} 111 } 112 err := error(nil) 113 if len(config.Certificates) == 0 { 114 config.Certificates = make([]tls.Certificate, 1) 115 if gres.Contains(certFile) { 116 config.Certificates[0], err = tls.X509KeyPair( 117 gres.GetContent(certFile), 118 gres.GetContent(keyFile), 119 ) 120 } else { 121 config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) 122 } 123 124 } 125 if err != nil { 126 return gerror.WrapCodef(gcode.CodeInternalError, err, `open cert file "%s","%s" failed`, certFile, keyFile) 127 } 128 ln, err := s.getNetListener() 129 if err != nil { 130 return err 131 } 132 133 s.listener = tls.NewListener(ln, config) 134 s.rawListener = ln 135 return s.doServe() 136 } 137 138 // getProto retrieves and returns the proto string of current server. 139 func (s *gracefulServer) getProto() string { 140 proto := "http" 141 if s.isHttps { 142 proto = "https" 143 } 144 return proto 145 } 146 147 // doServe does staring the serving. 148 func (s *gracefulServer) doServe() error { 149 action := "started" 150 if s.fd != 0 { 151 action = "reloaded" 152 } 153 s.server.Logger().Printf( 154 "%d: %s server %s listening on [%s]", 155 gproc.Pid(), s.getProto(), action, s.address, 156 ) 157 s.status = ServerStatusRunning 158 err := s.httpServer.Serve(s.listener) 159 s.status = ServerStatusStopped 160 return err 161 } 162 163 // getNetListener retrieves and returns the wrapped net.Listener. 164 func (s *gracefulServer) getNetListener() (net.Listener, error) { 165 var ln net.Listener 166 var err error 167 if s.fd > 0 { 168 f := os.NewFile(s.fd, "") 169 ln, err = net.FileListener(f) 170 if err != nil { 171 err = fmt.Errorf("%d: net.FileListener error: %v", gproc.Pid(), err) 172 return nil, err 173 } 174 } else { 175 ln, err = net.Listen("tcp", s.httpServer.Addr) 176 if err != nil { 177 err = fmt.Errorf("%d: net.Listen error: %v", gproc.Pid(), err) 178 } 179 } 180 return ln, err 181 } 182 183 // shutdown shuts down the server gracefully. 184 func (s *gracefulServer) shutdown() { 185 if s.status == ServerStatusStopped { 186 return 187 } 188 if err := s.httpServer.Shutdown(context.Background()); err != nil { 189 s.server.Logger().Errorf( 190 "%d: %s server [%s] shutdown error: %v", 191 gproc.Pid(), s.getProto(), s.address, err, 192 ) 193 } 194 } 195 196 // close shuts down the server forcibly. 197 func (s *gracefulServer) close() { 198 if s.status == ServerStatusStopped { 199 return 200 } 201 if err := s.httpServer.Close(); err != nil { 202 s.server.Logger().Errorf( 203 "%d: %s server [%s] closed error: %v", 204 gproc.Pid(), s.getProto(), s.address, err, 205 ) 206 } 207 }