github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/znet/web.go (about) 1 package znet 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "html/template" 8 "log" 9 "net/http" 10 "net/url" 11 "os" 12 "strings" 13 "sync" 14 "time" 15 16 "github.com/sohaha/zlsgo/zdi" 17 "github.com/sohaha/zlsgo/zfile" 18 "github.com/sohaha/zlsgo/zjson" 19 "github.com/sohaha/zlsgo/zutil" 20 "github.com/sohaha/zlsgo/zutil/daemon" 21 22 "github.com/sohaha/zlsgo/zcache" 23 "github.com/sohaha/zlsgo/zlog" 24 "github.com/sohaha/zlsgo/zshell" 25 ) 26 27 type ( 28 // Context context 29 Context struct { 30 startTime time.Time 31 render render 32 Writer http.ResponseWriter 33 injector zdi.Injector 34 stopHandle *zutil.Bool 35 prevData *PrevData 36 customizeData map[string]interface{} 37 header map[string][]string 38 Request *http.Request 39 cacheJSON *zjson.Res 40 cacheForm url.Values 41 done *zutil.Bool 42 Engine *Engine 43 Log *zlog.Logger 44 // Deprecated: Please maintain your own cache 45 Cache *zcache.Table 46 renderError ErrHandlerFunc 47 cacheQuery url.Values 48 rawData []byte 49 middleware []handlerFn 50 mu sync.RWMutex 51 } 52 // Engine is a simple HTTP route multiplexer that parses a request path 53 Engine struct { 54 pool sync.Pool 55 injector zdi.Injector 56 preHandler Handler 57 views Template 58 Cache *zcache.Table 59 template *tpl 60 Log *zlog.Logger 61 templateFuncMap template.FuncMap 62 router *router 63 BindTag string 64 webModeName string 65 BindStructDelimiter string 66 BindStructCase func(string) string 67 BindStructSuffix string 68 customMethodType string 69 addr []addrSt 70 shutdowns []func() 71 MaxMultipartMemory int64 72 webMode int 73 writeTimeout time.Duration 74 readTimeout time.Duration 75 ShowFavicon bool 76 AllowQuerySemicolons bool 77 } 78 TlsCfg struct { 79 HTTPProcessing interface{} 80 Config *tls.Config 81 Cert string 82 Key string 83 HTTPAddr string 84 } 85 tpl struct { 86 tpl *template.Template 87 templateFuncMap template.FuncMap 88 pattern string 89 } 90 addrSt struct { 91 TlsCfg 92 addr string 93 } 94 router struct { 95 trees map[string]*Tree 96 notFound handlerFn 97 prefix string 98 parameters Parameters 99 middleware []handlerFn 100 } 101 // Handler handler func 102 Handler interface{} 103 firstHandler [1]Handler 104 // HandlerFunc old handler func 105 HandlerFunc func(c *Context) 106 handlerFn func(c *Context) error 107 // MiddlewareFunc Middleware Func 108 MiddlewareFunc func(c *Context, fn Handler) 109 // ErrHandlerFunc ErrHandlerFunc 110 ErrHandlerFunc func(c *Context, err error) 111 // MiddlewareType is a public type that is used for middleware 112 MiddlewareType Handler 113 // Parameters records some parameters 114 Parameters struct { 115 routeName string 116 } 117 serverMap struct { 118 engine *Engine 119 srv *http.Server 120 } 121 ) 122 123 const ( 124 defaultMultipartMemory = 32 << 20 // 32 MB 125 // DebugMode dev 126 DebugMode = "dev" 127 // ProdMode release 128 ProdMode = "prod" 129 // TestMode test 130 TestMode = "test" 131 // QuietMode quiet 132 QuietMode = "quiet" 133 defaultServerName = "Z" 134 defaultBindTag = "json" 135 quietCode = -1 136 prodCode = 0 137 debugCode = iota 138 testCode 139 ) 140 141 var ( 142 // Log Log 143 Log = zlog.New(zlog.ColorTextWrap(zlog.ColorGreen, "[Z] ")) 144 Cache = zcache.New("__ZNET__") 145 // shutdownDone Shutdown Done executed after shutting down the server 146 shutdownDone func() 147 // CloseHotRestart Close Hot Restart 148 CloseHotRestart bool 149 zservers = map[string]*Engine{} 150 defaultAddr = addrSt{ 151 addr: ":3788", 152 } 153 // BindStructDelimiter structure route delimiter 154 BindStructDelimiter = "-" 155 // BindStructSuffix structure route suffix 156 BindStructSuffix = "" 157 ) 158 159 func init() { 160 Log.ResetFlags(zlog.BitTime | zlog.BitLevel) 161 } 162 163 // New returns a newly initialized Engine object that implements the Engine 164 func New(serverName ...string) *Engine { 165 name := defaultServerName 166 if len(serverName) > 0 { 167 name = serverName[0] 168 } 169 170 log := zlog.New("[" + name + "] ") 171 log.ResetFlags(zlog.BitTime | zlog.BitLevel) 172 log.SetLogLevel(zlog.LogInfo) 173 174 route := &router{ 175 prefix: "/", 176 trees: make(map[string]*Tree), 177 } 178 r := &Engine{ 179 Log: log, 180 Cache: Cache, 181 MaxMultipartMemory: defaultMultipartMemory, 182 BindTag: defaultBindTag, 183 BindStructDelimiter: BindStructDelimiter, 184 BindStructSuffix: BindStructSuffix, 185 router: route, 186 readTimeout: 0 * time.Second, 187 writeTimeout: 0 * time.Second, 188 webModeName: ProdMode, 189 webMode: prodCode, 190 addr: []addrSt{defaultAddr}, 191 templateFuncMap: template.FuncMap{}, 192 injector: zdi.New(), 193 shutdowns: make([]func(), 0), 194 } 195 r.pool.New = func() interface{} { 196 return r.NewContext(nil, nil) 197 } 198 if _, ok := zservers[name]; ok { 199 r.Log.Fatal("serverName: [", name, "] it already exists") 200 } 201 zservers[name] = r 202 return r 203 } 204 205 // WrapFirstMiddleware Wrapping a function in the first position of the middleware 206 func WrapFirstMiddleware(fn Handler) firstHandler { 207 return firstHandler{fn} 208 } 209 210 // Server Server 211 func Server(serverName ...string) (engine *Engine, ok bool) { 212 name := defaultServerName 213 if len(serverName) > 0 { 214 name = serverName[0] 215 } 216 if engine, ok = zservers[name]; !ok { 217 engine = New(name) 218 engine.Log.Warnf("serverName: %s is not", name) 219 } 220 return 221 } 222 223 // OnShutdown On Shutdown Func 224 func OnShutdown(done func()) { 225 shutdownDone = done 226 } 227 228 // SetAddr SetAddr 229 func (e *Engine) SetAddr(addrString string, tlsConfig ...TlsCfg) { 230 e.addr = []addrSt{ 231 resolveAddr(addrString, tlsConfig...), 232 } 233 } 234 235 // AddAddr AddAddr 236 func (e *Engine) AddAddr(addrString string, tlsConfig ...TlsCfg) { 237 e.addr = append(e.addr, resolveAddr(addrString, tlsConfig...)) 238 } 239 240 // SetCustomMethodField Set Custom Method Field 241 func (e *Engine) SetCustomMethodField(field string) { 242 e.customMethodType = field 243 } 244 245 // Deprecated: If you need to verify if a program is trustworthy, please implement it yourself. 246 // CloseHotRestartFileMd5 CloseHotRestartFileMd5 247 func CloseHotRestartFileMd5() { 248 } 249 250 // Deprecated: please use SetTemplate(znet.NewHTML()) 251 // SetTemplateFuncMap Set Template Func 252 func (e *Engine) SetTemplateFuncMap(funcMap template.FuncMap) { 253 if e.views == nil { 254 // compatible with the old version at present 255 e.templateFuncMap = funcMap 256 return 257 } 258 259 if t, ok := e.views.(*htmlEngine); ok { 260 t.SetFuncMap(funcMap) 261 } 262 } 263 264 // Injector Call Injector 265 func (e *Engine) Injector() zdi.TypeMapper { 266 return e.injector 267 } 268 269 // Deprecated: please use SetTemplate(znet.NewHTML()) 270 // SetHTMLTemplate Set HTML Template 271 func (e *Engine) SetHTMLTemplate(t *template.Template) { 272 val := &tpl{ 273 tpl: t, 274 templateFuncMap: template.FuncMap{}, 275 } 276 e.template = val 277 } 278 279 // LoadHTMLGlob Load Glob HTML 280 func (e *Engine) LoadHTMLGlob(pattern string) { 281 if !strings.Contains(pattern, "*") { 282 h := newGoTemplate(e, pattern) 283 e.views = h 284 return 285 } 286 287 // compatible with the old version at present 288 pattern = zfile.RealPath(pattern) 289 t, err := template.New("").Funcs(e.templateFuncMap).ParseGlob(pattern) 290 if err != nil { 291 e.Log.Fatalf("Template loading failed: %s\n", err) 292 return 293 } 294 isDebug := e.IsDebug() 295 val := &tpl{ 296 pattern: pattern, 297 tpl: t, 298 templateFuncMap: template.FuncMap{}, 299 } 300 if isDebug { 301 templatesDebug(e, t) 302 val.templateFuncMap = e.templateFuncMap 303 } 304 e.template = val 305 } 306 307 // SetMode Setting Server Mode 308 func (e *Engine) SetMode(value string) { 309 var level int 310 switch value { 311 case ProdMode, "": 312 level = zlog.LogSuccess 313 e.webMode = prodCode 314 case QuietMode: 315 level = zlog.LogPanic 316 e.webMode = quietCode 317 case DebugMode: 318 level = zlog.LogDump 319 e.webMode = debugCode 320 case TestMode: 321 level = zlog.LogDebug 322 e.webMode = testCode 323 default: 324 e.Log.Panic("web mode unknown: " + value) 325 } 326 if value == "" { 327 value = ProdMode 328 } 329 e.webModeName = value 330 e.Log.SetLogLevel(level) 331 } 332 333 // GetMode Get Mode 334 func (e *Engine) GetMode() string { 335 switch e.webMode { 336 case prodCode: 337 return ProdMode 338 case quietCode: 339 return QuietMode 340 case debugCode: 341 return DebugMode 342 case testCode: 343 return TestMode 344 default: 345 return "unknown" 346 } 347 } 348 349 // IsDebug IsDebug 350 func (e *Engine) IsDebug() bool { 351 return e.webMode > prodCode 352 } 353 354 // SetTimeout set Timeout 355 func (e *Engine) SetTimeout(Timeout time.Duration, WriteTimeout ...time.Duration) { 356 if len(WriteTimeout) > 0 { 357 e.writeTimeout = WriteTimeout[0] 358 e.readTimeout = Timeout 359 } else { 360 e.writeTimeout = Timeout 361 e.readTimeout = Timeout 362 } 363 } 364 365 func (e *Engine) StartUp() []*serverMap { 366 var wg sync.WaitGroup 367 var srvMap sync.Map 368 for _, cfg := range e.addr { 369 wg.Add(1) 370 371 go func(cfg addrSt, e *Engine) { 372 if e.AllowQuerySemicolons { 373 e.Log.SetIgnoreLog(errURLQuerySemicolon) 374 } 375 errChan := make(chan error, 1) 376 isTls := cfg.Cert != "" || cfg.Config != nil 377 addr := getAddr(cfg.addr) 378 hostname := getHostname(addr, isTls) 379 srv := &http.Server{ 380 Addr: addr, 381 Handler: e, 382 ReadTimeout: e.readTimeout, 383 WriteTimeout: e.writeTimeout, 384 // MaxHeaderBytes: 1 << 20, 385 ErrorLog: log.New(e.Log, "", 0), 386 } 387 388 srvMap.Store(addr, &serverMap{e, srv}) 389 390 wg.Done() 391 392 go func() { 393 select { 394 case <-errChan: 395 default: 396 wrapPid := e.Log.ColorTextWrap(zlog.ColorLightGrey, fmt.Sprintf("Pid: %d", os.Getpid())) 397 wrapMode := "" 398 if e.webMode > 0 { 399 wrapMode = e.Log.ColorTextWrap(zlog.ColorYellow, fmt.Sprintf("%s ", strings.ToUpper(e.webModeName))) 400 } 401 e.Log.Successf("%s %s %s%s\n", "Listen:", e.Log.ColorTextWrap(zlog.ColorLightGreen, e.Log.OpTextWrap(zlog.OpBold, hostname)), wrapMode, wrapPid) 402 } 403 }() 404 405 if isTls { 406 if cfg.Config != nil { 407 srv.TLSConfig = cfg.Config 408 } 409 if cfg.HTTPAddr != "" { 410 httpAddr := getAddr(cfg.HTTPAddr) 411 go func(e *Engine) { 412 newHostname := "http://" + resolveHostname(httpAddr) 413 e.Log.Success(e.Log.ColorBackgroundWrap(zlog.ColorYellow, zlog.ColorDefault, e.Log.OpTextWrap(zlog.OpBold, "Listen: "+newHostname))) 414 var err error 415 switch processing := cfg.HTTPProcessing.(type) { 416 case string: 417 err = http.ListenAndServe(httpAddr, &tlsRedirectHandler{Domain: processing}) 418 case http.Handler: 419 err = http.ListenAndServe(httpAddr, processing) 420 default: 421 err = http.ListenAndServe(httpAddr, e) 422 } 423 e.Log.Errorf("HTTP Listen: %s\n", err) 424 }(e) 425 } 426 errChan <- srv.ListenAndServeTLS(cfg.Cert, cfg.Key) 427 } else { 428 errChan <- srv.ListenAndServe() 429 } 430 431 err := <-errChan 432 if err != nil && err != http.ErrServerClosed { 433 e.Log.Fatalf("Listen: %s\n", err) 434 } else if err != http.ErrServerClosed { 435 e.Log.Info(err) 436 } 437 438 }(cfg, e) 439 } 440 441 wg.Wait() 442 443 srvs := make([]*serverMap, 0) 444 srvMap.Range(func(addr, value interface{}) bool { 445 srvs = append(srvs, value.(*serverMap)) 446 return true 447 }) 448 return srvs 449 } 450 451 func Shutdown() { 452 shutdown(true) 453 } 454 455 func shutdown(sigkill bool) { 456 ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) 457 defer cancel() 458 459 for _, s := range srvs { 460 r := s.engine 461 if sigkill { 462 r.Log.Info("Shutdown server ...") 463 } 464 for _, shutdown := range r.shutdowns { 465 shutdown() 466 } 467 err := s.srv.Shutdown(ctx) 468 if err != nil { 469 if sigkill { 470 r.Log.Error("Timeout forced close") 471 } 472 _ = s.srv.Close() 473 } else { 474 if sigkill { 475 r.Log.Success("Shutdown server done") 476 } 477 } 478 wg.Done() 479 } 480 481 wg.Wait() 482 if shutdownDone != nil { 483 shutdownDone() 484 } 485 } 486 487 var ( 488 srvs []*serverMap 489 wg sync.WaitGroup 490 ) 491 492 // Run serve 493 func Run(cb ...func(name, addr string)) { 494 RunContext(context.Background(), cb...) 495 } 496 497 func RunContext(ctx context.Context, cb ...func(name, addr string)) { 498 for n, e := range zservers { 499 ss := e.StartUp() 500 wg.Add(len(ss)) 501 srvs = append(srvs, ss...) 502 if len(cb) == 0 { 503 continue 504 } 505 for _, v := range ss { 506 cb[0](n, v.GetAddr()) 507 } 508 } 509 510 select { 511 case <-ctx.Done(): 512 shutdown(true) 513 case signal := <-daemon.SingleKillSignal(): 514 if !signal && !CloseHotRestart { 515 if err := runNewProcess(); err != nil { 516 Log.Error(err) 517 } 518 } 519 520 shutdown(signal) 521 } 522 } 523 524 func runNewProcess() error { 525 args := os.Args 526 _, err := zshell.RunNewProcess(args[0], args) 527 return err 528 }