github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server.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  	"bytes"
    11  	"context"
    12  	"github.com/gogf/gf/debug/gdebug"
    13  	"github.com/gogf/gf/errors/gcode"
    14  	"github.com/gogf/gf/errors/gerror"
    15  	"github.com/gogf/gf/internal/intlog"
    16  	"net/http"
    17  	"os"
    18  	"runtime"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/gogf/gf/os/gsession"
    23  
    24  	"github.com/gogf/gf/container/garray"
    25  	"github.com/gogf/gf/container/gtype"
    26  	"github.com/gogf/gf/os/gcache"
    27  	"github.com/gogf/gf/os/genv"
    28  	"github.com/gogf/gf/os/gfile"
    29  	"github.com/gogf/gf/os/glog"
    30  	"github.com/gogf/gf/os/gproc"
    31  	"github.com/gogf/gf/os/gtimer"
    32  	"github.com/gogf/gf/text/gregex"
    33  	"github.com/gogf/gf/util/gconv"
    34  	"github.com/olekukonko/tablewriter"
    35  )
    36  
    37  func init() {
    38  	// Initialize the methods map.
    39  	for _, v := range strings.Split(supportedHttpMethods, ",") {
    40  		methodsMap[v] = struct{}{}
    41  	}
    42  }
    43  
    44  // SetGraceful enables/disables the graceful reload feature for server,
    45  // which is false in default.
    46  //
    47  // Note that this feature switch is not for single server instance but for whole process.
    48  // Deprecated, use configuration of ghttp.Server for controlling this feature.
    49  func SetGraceful(enabled bool) {
    50  	gracefulEnabled = enabled
    51  }
    52  
    53  // serverProcessInit initializes some process configurations, which can only be done once.
    54  func serverProcessInit() {
    55  	if !serverProcessInitialized.Cas(false, true) {
    56  		return
    57  	}
    58  	// This means it is a restart server, it should kill its parent before starting its listening,
    59  	// to avoid duplicated port listening in two processes.
    60  	if genv.Get(adminActionRestartEnvKey) != "" {
    61  		if p, e := os.FindProcess(gproc.PPid()); e == nil {
    62  			p.Kill()
    63  			p.Wait()
    64  		} else {
    65  			glog.Error(e)
    66  		}
    67  	}
    68  
    69  	// Signal handler.
    70  	go handleProcessSignal()
    71  
    72  	// Process message handler.
    73  	// It's enabled only graceful feature is enabled.
    74  	if gracefulEnabled {
    75  		intlog.Printf(context.TODO(), "%d: graceful reload feature is enabled", gproc.Pid())
    76  		go handleProcessMessage()
    77  	} else {
    78  		intlog.Printf(context.TODO(), "%d: graceful reload feature is disabled", gproc.Pid())
    79  	}
    80  
    81  	// It's an ugly calling for better initializing the main package path
    82  	// in source development environment. It is useful only be used in main goroutine.
    83  	// It fails retrieving the main package path in asynchronous goroutines.
    84  	gfile.MainPkgPath()
    85  }
    86  
    87  // GetServer creates and returns a server instance using given name and default configurations.
    88  // Note that the parameter <name> should be unique for different servers. It returns an existing
    89  // server instance if given <name> is already existing in the server mapping.
    90  func GetServer(name ...interface{}) *Server {
    91  	serverName := defaultServerName
    92  	if len(name) > 0 && name[0] != "" {
    93  		serverName = gconv.String(name[0])
    94  	}
    95  	if s := serverMapping.Get(serverName); s != nil {
    96  		return s.(*Server)
    97  	}
    98  	s := &Server{
    99  		name:             serverName,
   100  		plugins:          make([]Plugin, 0),
   101  		servers:          make([]*gracefulServer, 0),
   102  		closeChan:        make(chan struct{}, 10000),
   103  		serverCount:      gtype.NewInt(),
   104  		statusHandlerMap: make(map[string][]HandlerFunc),
   105  		serveTree:        make(map[string]interface{}),
   106  		serveCache:       gcache.New(),
   107  		routesMap:        make(map[string][]registeredRouteItem),
   108  	}
   109  	// Initialize the server using default configurations.
   110  	if err := s.SetConfig(NewConfig()); err != nil {
   111  		panic(gerror.WrapCode(gcode.CodeInvalidConfiguration, err, ""))
   112  	}
   113  	// Record the server to internal server mapping by name.
   114  	serverMapping.Set(serverName, s)
   115  	return s
   116  }
   117  
   118  // Start starts listening on configured port.
   119  // This function does not block the process, you can use function Wait blocking the process.
   120  func (s *Server) Start() error {
   121  	// Register group routes.
   122  	s.handlePreBindItems()
   123  
   124  	// Server process initialization, which can only be initialized once.
   125  	serverProcessInit()
   126  
   127  	// Server can only be run once.
   128  	if s.Status() == ServerStatusRunning {
   129  		return gerror.NewCode(gcode.CodeInvalidOperation, "server is already running")
   130  	}
   131  
   132  	// Logging path setting check.
   133  	if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() {
   134  		if err := s.config.Logger.SetPath(s.config.LogPath); err != nil {
   135  			return err
   136  		}
   137  	}
   138  	// Default session storage.
   139  	if s.config.SessionStorage == nil {
   140  		path := ""
   141  		if s.config.SessionPath != "" {
   142  			path = gfile.Join(s.config.SessionPath, s.name)
   143  			if !gfile.Exists(path) {
   144  				if err := gfile.Mkdir(path); err != nil {
   145  					return gerror.WrapCodef(gcode.CodeInternalError, err, `mkdir failed for "%s"`, path)
   146  				}
   147  			}
   148  		}
   149  		s.config.SessionStorage = gsession.NewStorageFile(path)
   150  	}
   151  	// Initialize session manager when start running.
   152  	s.sessionManager = gsession.New(
   153  		s.config.SessionMaxAge,
   154  		s.config.SessionStorage,
   155  	)
   156  
   157  	// PProf feature.
   158  	if s.config.PProfEnabled {
   159  		s.EnablePProf(s.config.PProfPattern)
   160  	}
   161  
   162  	// Default HTTP handler.
   163  	if s.config.Handler == nil {
   164  		s.config.Handler = s
   165  	}
   166  
   167  	// Install external plugins.
   168  	for _, p := range s.plugins {
   169  		if err := p.Install(s); err != nil {
   170  			s.Logger().Fatal(err)
   171  		}
   172  	}
   173  	// Check the group routes again.
   174  	s.handlePreBindItems()
   175  
   176  	// If there's no route registered  and no static service enabled,
   177  	// it then returns an error of invalid usage of server.
   178  	if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
   179  		return gerror.NewCode(
   180  			gcode.CodeInvalidOperation,
   181  			`there's no route set or static feature enabled, did you forget import the router?`,
   182  		)
   183  	}
   184  
   185  	// Start the HTTP server.
   186  	reloaded := false
   187  	fdMapStr := genv.Get(adminActionReloadEnvKey)
   188  	if len(fdMapStr) > 0 {
   189  		sfm := bufferToServerFdMap([]byte(fdMapStr))
   190  		if v, ok := sfm[s.name]; ok {
   191  			s.startServer(v)
   192  			reloaded = true
   193  		}
   194  	}
   195  	if !reloaded {
   196  		s.startServer(nil)
   197  	}
   198  
   199  	// If this is a child process, it then notifies its parent exit.
   200  	if gproc.IsChild() {
   201  		gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() {
   202  			if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
   203  				intlog.Error(context.TODO(), "server error in process communication:", err)
   204  			}
   205  		})
   206  	}
   207  	s.dumpRouterMap()
   208  	return nil
   209  }
   210  
   211  // DumpRouterMap dumps the router map to the log.
   212  func (s *Server) dumpRouterMap() {
   213  	if s.config.DumpRouterMap && len(s.routesMap) > 0 {
   214  		buffer := bytes.NewBuffer(nil)
   215  		table := tablewriter.NewWriter(buffer)
   216  		table.SetHeader([]string{"SERVER", "DOMAIN", "ADDRESS", "METHOD", "ROUTE", "HANDLER", "MIDDLEWARE"})
   217  		table.SetRowLine(true)
   218  		table.SetBorder(false)
   219  		table.SetCenterSeparator("|")
   220  
   221  		for _, item := range s.GetRouterArray() {
   222  			data := make([]string, 7)
   223  			data[0] = item.Server
   224  			data[1] = item.Domain
   225  			data[2] = item.Address
   226  			data[3] = item.Method
   227  			data[4] = item.Route
   228  			data[5] = item.handler.Name
   229  			data[6] = item.Middleware
   230  			table.Append(data)
   231  		}
   232  		table.Render()
   233  		s.config.Logger.Header(false).Printf("\n%s", buffer.String())
   234  	}
   235  }
   236  
   237  // GetRouterArray retrieves and returns the router array.
   238  // The key of the returned map is the domain of the server.
   239  func (s *Server) GetRouterArray() []RouterItem {
   240  	m := make(map[string]*garray.SortedArray)
   241  	address := s.config.Address
   242  	if s.config.HTTPSAddr != "" {
   243  		if len(address) > 0 {
   244  			address += ","
   245  		}
   246  		address += "tls" + s.config.HTTPSAddr
   247  	}
   248  	for k, registeredItems := range s.routesMap {
   249  		array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k)
   250  		for index, registeredItem := range registeredItems {
   251  			item := RouterItem{
   252  				Server:     s.name,
   253  				Address:    address,
   254  				Domain:     array[4],
   255  				Type:       registeredItem.Handler.Type,
   256  				Middleware: array[1],
   257  				Method:     array[2],
   258  				Route:      array[3],
   259  				Priority:   len(registeredItems) - index - 1,
   260  				handler:    registeredItem.Handler,
   261  			}
   262  			switch item.handler.Type {
   263  			case handlerTypeController, handlerTypeObject, handlerTypeHandler:
   264  				item.IsServiceHandler = true
   265  			case handlerTypeMiddleware:
   266  				item.Middleware = "GLOBAL MIDDLEWARE"
   267  			}
   268  			if len(item.handler.Middleware) > 0 {
   269  				for _, v := range item.handler.Middleware {
   270  					if item.Middleware != "" {
   271  						item.Middleware += ","
   272  					}
   273  					item.Middleware += gdebug.FuncName(v)
   274  				}
   275  			}
   276  			// If the domain does not exist in the dump map, it creates the map.
   277  			// The value of the map is a custom sorted array.
   278  			if _, ok := m[item.Domain]; !ok {
   279  				// Sort in ASC order.
   280  				m[item.Domain] = garray.NewSortedArray(func(v1, v2 interface{}) int {
   281  					item1 := v1.(RouterItem)
   282  					item2 := v2.(RouterItem)
   283  					r := 0
   284  					if r = strings.Compare(item1.Domain, item2.Domain); r == 0 {
   285  						if r = strings.Compare(item1.Route, item2.Route); r == 0 {
   286  							if r = strings.Compare(item1.Method, item2.Method); r == 0 {
   287  								if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type != handlerTypeMiddleware {
   288  									return -1
   289  								} else if item1.handler.Type == handlerTypeMiddleware && item2.handler.Type == handlerTypeMiddleware {
   290  									return 1
   291  								} else if r = strings.Compare(item1.Middleware, item2.Middleware); r == 0 {
   292  									r = item2.Priority - item1.Priority
   293  								}
   294  							}
   295  						}
   296  					}
   297  					return r
   298  				})
   299  			}
   300  			m[item.Domain].Add(item)
   301  		}
   302  	}
   303  	routerArray := make([]RouterItem, 0, 128)
   304  	for _, array := range m {
   305  		for _, v := range array.Slice() {
   306  			routerArray = append(routerArray, v.(RouterItem))
   307  		}
   308  	}
   309  	return routerArray
   310  }
   311  
   312  // Run starts server listening in blocking way.
   313  // It's commonly used for single server situation.
   314  func (s *Server) Run() {
   315  	if err := s.Start(); err != nil {
   316  		s.Logger().Fatal(err)
   317  	}
   318  	// Blocking using channel.
   319  	<-s.closeChan
   320  	// Remove plugins.
   321  	if len(s.plugins) > 0 {
   322  		for _, p := range s.plugins {
   323  			intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name())
   324  			if err := p.Remove(); err != nil {
   325  				intlog.Errorf(context.TODO(), "%+v", err)
   326  			}
   327  		}
   328  	}
   329  	s.Logger().Printf("%d: all servers shutdown", gproc.Pid())
   330  }
   331  
   332  // Wait blocks to wait for all servers done.
   333  // It's commonly used in multiple servers situation.
   334  func Wait() {
   335  	<-allDoneChan
   336  	// Remove plugins.
   337  	serverMapping.Iterator(func(k string, v interface{}) bool {
   338  		s := v.(*Server)
   339  		if len(s.plugins) > 0 {
   340  			for _, p := range s.plugins {
   341  				intlog.Printf(context.TODO(), `remove plugin: %s`, p.Name())
   342  				p.Remove()
   343  			}
   344  		}
   345  		return true
   346  	})
   347  	glog.Printf("%d: all servers shutdown", gproc.Pid())
   348  }
   349  
   350  // startServer starts the underlying server listening.
   351  func (s *Server) startServer(fdMap listenerFdMap) {
   352  	var httpsEnabled bool
   353  	// HTTPS
   354  	if s.config.TLSConfig != nil || (s.config.HTTPSCertPath != "" && s.config.HTTPSKeyPath != "") {
   355  		if len(s.config.HTTPSAddr) == 0 {
   356  			if len(s.config.Address) > 0 {
   357  				s.config.HTTPSAddr = s.config.Address
   358  				s.config.Address = ""
   359  			} else {
   360  				s.config.HTTPSAddr = defaultHttpsAddr
   361  			}
   362  		}
   363  		httpsEnabled = len(s.config.HTTPSAddr) > 0
   364  		var array []string
   365  		if v, ok := fdMap["https"]; ok && len(v) > 0 {
   366  			array = strings.Split(v, ",")
   367  		} else {
   368  			array = strings.Split(s.config.HTTPSAddr, ",")
   369  		}
   370  		for _, v := range array {
   371  			if len(v) == 0 {
   372  				continue
   373  			}
   374  			fd := 0
   375  			itemFunc := v
   376  			array := strings.Split(v, "#")
   377  			if len(array) > 1 {
   378  				itemFunc = array[0]
   379  				// The windows OS does not support socket file descriptor passing
   380  				// from parent process.
   381  				if runtime.GOOS != "windows" {
   382  					fd = gconv.Int(array[1])
   383  				}
   384  			}
   385  			if fd > 0 {
   386  				s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
   387  			} else {
   388  				s.servers = append(s.servers, s.newGracefulServer(itemFunc))
   389  			}
   390  			s.servers[len(s.servers)-1].isHttps = true
   391  		}
   392  	}
   393  	// HTTP
   394  	if !httpsEnabled && len(s.config.Address) == 0 {
   395  		s.config.Address = defaultHttpAddr
   396  	}
   397  	var array []string
   398  	if v, ok := fdMap["http"]; ok && len(v) > 0 {
   399  		array = strings.Split(v, ",")
   400  	} else {
   401  		array = strings.Split(s.config.Address, ",")
   402  	}
   403  	for _, v := range array {
   404  		if len(v) == 0 {
   405  			continue
   406  		}
   407  		fd := 0
   408  		itemFunc := v
   409  		array := strings.Split(v, "#")
   410  		if len(array) > 1 {
   411  			itemFunc = array[0]
   412  			// The windows OS does not support socket file descriptor passing
   413  			// from parent process.
   414  			if runtime.GOOS != "windows" {
   415  				fd = gconv.Int(array[1])
   416  			}
   417  		}
   418  		if fd > 0 {
   419  			s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
   420  		} else {
   421  			s.servers = append(s.servers, s.newGracefulServer(itemFunc))
   422  		}
   423  	}
   424  	// Start listening asynchronously.
   425  	serverRunning.Add(1)
   426  	for _, v := range s.servers {
   427  		go func(server *gracefulServer) {
   428  			s.serverCount.Add(1)
   429  			err := (error)(nil)
   430  			if server.isHttps {
   431  				err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath, s.config.TLSConfig)
   432  			} else {
   433  				err = server.ListenAndServe()
   434  			}
   435  			// The process exits if the server is closed with none closing error.
   436  			if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
   437  				s.Logger().Fatal(err)
   438  			}
   439  			// If all the underlying servers shutdown, the process exits.
   440  			if s.serverCount.Add(-1) < 1 {
   441  				s.closeChan <- struct{}{}
   442  				if serverRunning.Add(-1) < 1 {
   443  					serverMapping.Remove(s.name)
   444  					allDoneChan <- struct{}{}
   445  				}
   446  			}
   447  		}(v)
   448  	}
   449  }
   450  
   451  // Status retrieves and returns the server status.
   452  func (s *Server) Status() int {
   453  	if serverRunning.Val() == 0 {
   454  		return ServerStatusStopped
   455  	}
   456  	// If any underlying server is running, the server status is running.
   457  	for _, v := range s.servers {
   458  		if v.status == ServerStatusRunning {
   459  			return ServerStatusRunning
   460  		}
   461  	}
   462  	return ServerStatusStopped
   463  }
   464  
   465  // getListenerFdMap retrieves and returns the socket file descriptors.
   466  // The key of the returned map is "http" and "https".
   467  func (s *Server) getListenerFdMap() map[string]string {
   468  	m := map[string]string{
   469  		"https": "",
   470  		"http":  "",
   471  	}
   472  	for _, v := range s.servers {
   473  		str := v.address + "#" + gconv.String(v.Fd()) + ","
   474  		if v.isHttps {
   475  			if len(m["https"]) > 0 {
   476  				m["https"] += ","
   477  			}
   478  			m["https"] += str
   479  		} else {
   480  			if len(m["http"]) > 0 {
   481  				m["http"] += ","
   482  			}
   483  			m["http"] += str
   484  		}
   485  	}
   486  	return m
   487  }
   488  
   489  // IsExitError checks if given error is an exit error of server.
   490  // This is used in old version of server for custom error handler.
   491  // Deprecated.
   492  func IsExitError(err interface{}) bool {
   493  	errStr := gconv.String(err)
   494  	if strings.EqualFold(errStr, exceptionExit) ||
   495  		strings.EqualFold(errStr, exceptionExitAll) ||
   496  		strings.EqualFold(errStr, exceptionExitHook) {
   497  		return true
   498  	}
   499  	return false
   500  }