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 }