github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"log"
    14  	"net"
    15  	"net/http"
    16  	"os"
    17  	"strconv"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/wangyougui/gf/v2/container/gtype"
    22  	"github.com/wangyougui/gf/v2/errors/gcode"
    23  	"github.com/wangyougui/gf/v2/errors/gerror"
    24  	"github.com/wangyougui/gf/v2/os/gproc"
    25  	"github.com/wangyougui/gf/v2/os/gres"
    26  	"github.com/wangyougui/gf/v2/text/gstr"
    27  )
    28  
    29  // gracefulServer wraps the net/http.Server with graceful reload/restart feature.
    30  type gracefulServer struct {
    31  	server      *Server      // Belonged server.
    32  	fd          uintptr      // File descriptor for passing to the child process when graceful reload.
    33  	address     string       // Listening address like:":80", ":8080".
    34  	httpServer  *http.Server // Underlying http.Server.
    35  	rawListener net.Listener // Underlying net.Listener.
    36  	rawLnMu     sync.RWMutex // Concurrent safety mutex for `rawListener`.
    37  	listener    net.Listener // Wrapped net.Listener.
    38  	isHttps     bool         // Is HTTPS.
    39  	status      *gtype.Int   // Status of current server. Using `gtype` to ensure concurrent safety.
    40  }
    41  
    42  // newGracefulServer creates and returns a graceful http server with a given address.
    43  // The optional parameter `fd` specifies the file descriptor which is passed from parent server.
    44  func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
    45  	// Change port to address like: 80 -> :80
    46  	if gstr.IsNumeric(address) {
    47  		address = ":" + address
    48  	}
    49  	gs := &gracefulServer{
    50  		server:     s,
    51  		address:    address,
    52  		httpServer: s.newHttpServer(address),
    53  		status:     gtype.NewInt(),
    54  	}
    55  	if len(fd) > 0 && fd[0] > 0 {
    56  		gs.fd = uintptr(fd[0])
    57  	}
    58  	if s.config.Listeners != nil {
    59  		addrArray := gstr.SplitAndTrim(address, ":")
    60  		addrPort, err := strconv.Atoi(addrArray[len(addrArray)-1])
    61  		if err == nil {
    62  			for _, v := range s.config.Listeners {
    63  				if listenerPort := (v.Addr().(*net.TCPAddr)).Port; listenerPort == addrPort {
    64  					gs.rawListener = v
    65  					break
    66  				}
    67  			}
    68  		}
    69  	}
    70  	return gs
    71  }
    72  
    73  // newHttpServer creates and returns an underlying http.Server with a given address.
    74  func (s *Server) newHttpServer(address string) *http.Server {
    75  	server := &http.Server{
    76  		Addr:           address,
    77  		Handler:        http.HandlerFunc(s.config.Handler),
    78  		ReadTimeout:    s.config.ReadTimeout,
    79  		WriteTimeout:   s.config.WriteTimeout,
    80  		IdleTimeout:    s.config.IdleTimeout,
    81  		MaxHeaderBytes: s.config.MaxHeaderBytes,
    82  		ErrorLog:       log.New(&errorLogger{logger: s.config.Logger}, "", 0),
    83  	}
    84  	server.SetKeepAlivesEnabled(s.config.KeepAlive)
    85  	return server
    86  }
    87  
    88  // Fd retrieves and returns the file descriptor of the current server.
    89  // It is available ony in *nix like operating systems like linux, unix, darwin.
    90  func (s *gracefulServer) Fd() uintptr {
    91  	if ln := s.getRawListener(); ln != nil {
    92  		file, err := ln.(*net.TCPListener).File()
    93  		if err == nil {
    94  			return file.Fd()
    95  		}
    96  	}
    97  	return 0
    98  }
    99  
   100  // CreateListener creates listener on configured address.
   101  func (s *gracefulServer) CreateListener() error {
   102  	ln, err := s.getNetListener()
   103  	if err != nil {
   104  		return err
   105  	}
   106  	s.listener = ln
   107  	s.setRawListener(ln)
   108  	return nil
   109  }
   110  
   111  // CreateListenerTLS creates listener on configured address with HTTPS.
   112  // The parameter `certFile` and `keyFile` specify the necessary certification and key files for HTTPS.
   113  // The optional parameter `tlsConfig` specifies the custom TLS configuration.
   114  func (s *gracefulServer) CreateListenerTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error {
   115  	var config *tls.Config
   116  	if len(tlsConfig) > 0 && tlsConfig[0] != nil {
   117  		config = tlsConfig[0]
   118  	} else if s.httpServer.TLSConfig != nil {
   119  		config = s.httpServer.TLSConfig
   120  	} else {
   121  		config = &tls.Config{}
   122  	}
   123  	if config.NextProtos == nil {
   124  		config.NextProtos = []string{"http/1.1"}
   125  	}
   126  	var err error
   127  	if len(config.Certificates) == 0 {
   128  		config.Certificates = make([]tls.Certificate, 1)
   129  		if gres.Contains(certFile) {
   130  			config.Certificates[0], err = tls.X509KeyPair(
   131  				gres.GetContent(certFile),
   132  				gres.GetContent(keyFile),
   133  			)
   134  		} else {
   135  			config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
   136  		}
   137  	}
   138  	if err != nil {
   139  		return gerror.Wrapf(err, `open certFile "%s" and keyFile "%s" failed`, certFile, keyFile)
   140  	}
   141  	ln, err := s.getNetListener()
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	s.listener = tls.NewListener(ln, config)
   147  	s.setRawListener(ln)
   148  	return nil
   149  }
   150  
   151  // Serve starts the serving with blocking way.
   152  func (s *gracefulServer) Serve(ctx context.Context) error {
   153  	if s.rawListener == nil {
   154  		return gerror.NewCode(gcode.CodeInvalidOperation, `call CreateListener/CreateListenerTLS before Serve`)
   155  	}
   156  
   157  	action := "started"
   158  	if s.fd != 0 {
   159  		action = "reloaded"
   160  	}
   161  	s.server.Logger().Infof(
   162  		ctx,
   163  		`pid[%d]: %s server %s listening on [%s]`,
   164  		gproc.Pid(), s.getProto(), action, s.GetListenedAddress(),
   165  	)
   166  	s.status.Set(ServerStatusRunning)
   167  	err := s.httpServer.Serve(s.listener)
   168  	s.status.Set(ServerStatusStopped)
   169  	return err
   170  }
   171  
   172  // GetListenedAddress retrieves and returns the address string which are listened by current server.
   173  func (s *gracefulServer) GetListenedAddress() string {
   174  	if !gstr.Contains(s.address, FreePortAddress) {
   175  		return s.address
   176  	}
   177  	var (
   178  		address      = s.address
   179  		listenedPort = s.GetListenedPort()
   180  	)
   181  	address = gstr.Replace(address, FreePortAddress, fmt.Sprintf(`:%d`, listenedPort))
   182  	return address
   183  }
   184  
   185  // GetListenedPort retrieves and returns one port which is listened to by current server.
   186  // Note that this method is only available if the server is listening on one port.
   187  func (s *gracefulServer) GetListenedPort() int {
   188  	if ln := s.getRawListener(); ln != nil {
   189  		return ln.Addr().(*net.TCPAddr).Port
   190  	}
   191  	return -1
   192  }
   193  
   194  // getProto retrieves and returns the proto string of current server.
   195  func (s *gracefulServer) getProto() string {
   196  	proto := "http"
   197  	if s.isHttps {
   198  		proto = "https"
   199  	}
   200  	return proto
   201  }
   202  
   203  // getNetListener retrieves and returns the wrapped net.Listener.
   204  func (s *gracefulServer) getNetListener() (net.Listener, error) {
   205  	if s.rawListener != nil {
   206  		return s.rawListener, nil
   207  	}
   208  	var (
   209  		ln  net.Listener
   210  		err error
   211  	)
   212  	if s.fd > 0 {
   213  		f := os.NewFile(s.fd, "")
   214  		ln, err = net.FileListener(f)
   215  		if err != nil {
   216  			err = gerror.Wrap(err, "net.FileListener failed")
   217  			return nil, err
   218  		}
   219  	} else {
   220  		ln, err = net.Listen("tcp", s.httpServer.Addr)
   221  		if err != nil {
   222  			err = gerror.Wrapf(err, `net.Listen address "%s" failed`, s.httpServer.Addr)
   223  		}
   224  	}
   225  	return ln, err
   226  }
   227  
   228  // shutdown shuts down the server gracefully.
   229  func (s *gracefulServer) shutdown(ctx context.Context) {
   230  	if s.status.Val() == ServerStatusStopped {
   231  		return
   232  	}
   233  	timeoutCtx, cancelFunc := context.WithTimeout(
   234  		ctx,
   235  		time.Duration(s.server.config.GracefulShutdownTimeout)*time.Second,
   236  	)
   237  	defer cancelFunc()
   238  	if err := s.httpServer.Shutdown(timeoutCtx); err != nil {
   239  		s.server.Logger().Errorf(
   240  			ctx,
   241  			"%d: %s server [%s] shutdown error: %v",
   242  			gproc.Pid(), s.getProto(), s.address, err,
   243  		)
   244  	}
   245  }
   246  
   247  // setRawListener sets `rawListener` with given net.Listener.
   248  func (s *gracefulServer) setRawListener(ln net.Listener) {
   249  	s.rawLnMu.Lock()
   250  	defer s.rawLnMu.Unlock()
   251  	s.rawListener = ln
   252  }
   253  
   254  // setRawListener returns the `rawListener` of current server.
   255  func (s *gracefulServer) getRawListener() net.Listener {
   256  	s.rawLnMu.RLock()
   257  	defer s.rawLnMu.RUnlock()
   258  	return s.rawListener
   259  }
   260  
   261  // close shuts down the server forcibly.
   262  func (s *gracefulServer) close(ctx context.Context) {
   263  	if s.status.Val() == ServerStatusStopped {
   264  		return
   265  	}
   266  	if err := s.httpServer.Close(); err != nil {
   267  		s.server.Logger().Errorf(
   268  			ctx,
   269  			"%d: %s server [%s] closed error: %v",
   270  			gproc.Pid(), s.getProto(), s.address, err,
   271  		)
   272  	}
   273  }