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  }