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  }