github.com/godaddy-x/freego@v1.0.156/node/node_http.go (about)

     1  package node
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/buaazp/fasthttprouter"
     6  	"github.com/godaddy-x/freego/cache"
     7  	"github.com/godaddy-x/freego/ex"
     8  	"github.com/godaddy-x/freego/utils"
     9  	"github.com/godaddy-x/freego/utils/crypto"
    10  	"github.com/godaddy-x/freego/utils/jwt"
    11  	"github.com/godaddy-x/freego/zlog"
    12  	"github.com/valyala/fasthttp"
    13  	"net/http"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  )
    18  
    19  var emptyMap = map[string]string{}
    20  
    21  type CacheAware func(ds ...string) (cache.Cache, error)
    22  
    23  type HttpNode struct {
    24  	HookNode
    25  	mu      sync.Mutex
    26  	ctxPool sync.Pool
    27  }
    28  
    29  type PostHandle func(*Context) error
    30  
    31  type ErrorHandle func(ctx *Context, throw ex.Throw) error
    32  
    33  func (self *HttpNode) doRequest(handle PostHandle, request *fasthttp.RequestCtx) error {
    34  	ctx := self.ctxPool.Get().(*Context)
    35  	ctx.reset(self.Context, handle, request, self.filters)
    36  	if err := ctx.filterChain.DoFilter(ctx.filterChain, ctx); err != nil {
    37  		self.ctxPool.Put(ctx)
    38  		return err
    39  	}
    40  	self.ctxPool.Put(ctx)
    41  	return nil
    42  }
    43  
    44  func (self *HttpNode) proxy(handle PostHandle, ctx *fasthttp.RequestCtx) {
    45  	if err := self.doRequest(handle, ctx); err != nil {
    46  		zlog.Error("doRequest failed", 0, zlog.AddError(err))
    47  	}
    48  }
    49  
    50  func (self *HttpNode) StartServer(addr string) {
    51  	go func() {
    52  		if self.Context.CacheAware != nil {
    53  			zlog.Printf("cache service has been started successful")
    54  		}
    55  		if self.Context.RSA != nil {
    56  			if self.Context.System.enableECC {
    57  				zlog.Printf("ECC certificate service has been started successful")
    58  			} else {
    59  				zlog.Printf("RSA certificate service has been started successful")
    60  			}
    61  		}
    62  		fs, err := createFilterChain(self.filters)
    63  		if err != nil {
    64  			panic("http service create filter chain failed")
    65  		}
    66  		self.filters = fs
    67  		if len(self.filters) == 0 {
    68  			panic("filter chain is nil")
    69  		}
    70  		zlog.Printf("http【%s】service has been started successful", addr)
    71  		if err := fasthttp.Serve(NewGracefulListener(addr, time.Second*10), self.Context.router.Handler); err != nil {
    72  			panic(err)
    73  		}
    74  	}()
    75  	select {}
    76  }
    77  
    78  func (self *HttpNode) checkContextReady(path string, routerConfig *RouterConfig) {
    79  	self.readyContext()
    80  	self.AddCache(nil)
    81  	self.AddCipher(nil)
    82  	self.addRouterConfig(path, routerConfig)
    83  	self.newRouter()
    84  }
    85  
    86  func (self *HttpNode) addRouter(method, path string, handle PostHandle, routerConfig *RouterConfig) {
    87  	self.checkContextReady(path, routerConfig)
    88  	self.Context.router.Handle(method, path, fasthttp.TimeoutHandler(
    89  		func(ctx *fasthttp.RequestCtx) {
    90  			self.proxy(handle, ctx)
    91  		},
    92  		time.Duration(self.Context.System.AcceptTimeout)*time.Second,
    93  		fmt.Sprintf(`{"c":408,"m":"server actively disconnects the client","d":null,"t":%d,"n":"%s","p":0,"s":""}`, utils.UnixMilli(), utils.RandNonce())))
    94  }
    95  
    96  func (self *HttpNode) Json(ctx *Context, data interface{}) error {
    97  	return ctx.Json(data)
    98  }
    99  
   100  func (self *HttpNode) Text(ctx *Context, data string) error {
   101  	return ctx.Text(data)
   102  }
   103  
   104  func (self *HttpNode) AddFilter(object *FilterObject) {
   105  	self.readyContext()
   106  	if object == nil {
   107  		panic("filter object is nil")
   108  	}
   109  	if len(object.Name) == 0 || object.Filter == nil {
   110  		panic("filter object name/filter is nil")
   111  	}
   112  	self.filters = append(self.filters, object)
   113  	zlog.Printf("add filter [%s] successful", object.Name)
   114  }
   115  
   116  func (self *HttpNode) createCtxPool() sync.Pool {
   117  	return sync.Pool{New: func() interface{} {
   118  		ctx := &Context{}
   119  		ctx.configs = self.Context.configs
   120  		ctx.filterChain = &filterChain{}
   121  		ctx.System = &System{}
   122  		ctx.JsonBody = &JsonBody{}
   123  		ctx.Subject = &jwt.Subject{Header: &jwt.Header{}, Payload: &jwt.Payload{}}
   124  		ctx.Response = &Response{Encoding: UTF8, ContentType: APPLICATION_JSON, ContentEntity: nil}
   125  		ctx.Storage = map[string]interface{}{}
   126  		return ctx
   127  	}}
   128  }
   129  
   130  func (self *HttpNode) readyContext() {
   131  	self.mu.Lock()
   132  	defer self.mu.Unlock()
   133  	if self.Context == nil {
   134  		self.Context = &Context{}
   135  		self.Context.configs = &Configs{}
   136  		self.Context.configs.routerConfigs = make(map[string]*RouterConfig)
   137  		self.Context.configs.langConfigs = make(map[string]map[string]string)
   138  		self.Context.configs.jwtConfig = jwt.JwtConfig{}
   139  		self.Context.System = &System{}
   140  		self.ctxPool = self.createCtxPool()
   141  	}
   142  }
   143  
   144  func (self *HttpNode) AddCache(cacheAware CacheAware) {
   145  	self.readyContext()
   146  	if self.Context.CacheAware == nil {
   147  		if cacheAware == nil {
   148  			cacheAware = func(ds ...string) (cache.Cache, error) {
   149  				return cache.NewLocalCache(30, 2), nil
   150  			}
   151  		}
   152  		self.Context.CacheAware = cacheAware
   153  	}
   154  }
   155  
   156  func (self *HttpNode) AddCipher(cipher crypto.Cipher) {
   157  	self.readyContext()
   158  	if self.Context.RSA == nil {
   159  		if cipher == nil {
   160  			if self.Context.System.enableECC {
   161  				defaultECC := &crypto.EccObj{}
   162  				if err := defaultECC.CreateS256ECC(); err != nil {
   163  					panic("ECC certificate generation failed")
   164  				}
   165  				cipher = defaultECC
   166  			} else {
   167  				defaultRSA := &crypto.RsaObj{}
   168  				if err := defaultRSA.CreateRsa2048(); err != nil {
   169  					panic("RSA certificate generation failed")
   170  				}
   171  				cipher = defaultRSA
   172  			}
   173  		}
   174  		self.Context.RSA = cipher
   175  	}
   176  }
   177  
   178  func (self *HttpNode) AddLanguage(langDs, filePath string) error {
   179  	self.readyContext()
   180  	if len(langDs) == 0 || len(filePath) == 0 {
   181  		return nil
   182  	}
   183  	bs, err := utils.ReadFile(filePath)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	return self.AddLanguageByJson(langDs, bs)
   188  }
   189  
   190  func (self *HttpNode) AddLanguageByJson(langDs string, bs []byte) error {
   191  	self.readyContext()
   192  	if !utils.JsonValid(bs) {
   193  		panic("lang json config invalid: " + langDs)
   194  	}
   195  	kv := map[string]string{}
   196  	if err := utils.JsonUnmarshal(bs, &kv); err != nil {
   197  		panic("lang json unmarshal failed: " + err.Error())
   198  	}
   199  	self.Context.configs.langConfigs[langDs] = kv
   200  	if len(self.Context.configs.defaultLang) == 0 {
   201  		self.Context.configs.defaultLang = langDs
   202  	}
   203  	zlog.Printf("add lang [%s] successful", langDs)
   204  	return nil
   205  }
   206  
   207  func (self *HttpNode) AddRoleRealm(roleRealm func(ctx *Context, onlyRole bool) (*Permission, error)) error {
   208  	self.readyContext()
   209  	self.Context.roleRealm = roleRealm
   210  	zlog.Printf("add permission realm successful")
   211  	return nil
   212  }
   213  
   214  func (self *HttpNode) AddErrorHandle(errorHandle func(ctx *Context, throw ex.Throw) error) error {
   215  	self.readyContext()
   216  	self.Context.errorHandle = errorHandle
   217  	zlog.Printf("add error handle successful")
   218  	return nil
   219  }
   220  
   221  func (self *HttpNode) addRouterConfig(path string, routerConfig *RouterConfig) {
   222  	self.readyContext()
   223  	if routerConfig == nil {
   224  		routerConfig = &RouterConfig{}
   225  	}
   226  	if _, b := self.Context.configs.routerConfigs[path]; !b {
   227  		self.Context.configs.routerConfigs[path] = routerConfig
   228  	}
   229  }
   230  
   231  func (self *HttpNode) newRouter() {
   232  	self.readyContext()
   233  	if self.Context.System.AcceptTimeout <= 0 {
   234  		self.Context.System.AcceptTimeout = 60
   235  	}
   236  	if self.Context.router == nil {
   237  		self.Context.router = fasthttprouter.New()
   238  	}
   239  }
   240  
   241  func (self *HttpNode) AddJwtConfig(config jwt.JwtConfig) {
   242  	self.readyContext()
   243  	if len(config.TokenKey) == 0 {
   244  		panic("jwt config key is nil")
   245  	}
   246  	if config.TokenExp < 0 {
   247  		panic("jwt config exp invalid")
   248  	}
   249  	self.Context.configs.jwtConfig.TokenAlg = config.TokenAlg
   250  	self.Context.configs.jwtConfig.TokenTyp = config.TokenTyp
   251  	self.Context.configs.jwtConfig.TokenKey = config.TokenKey
   252  	self.Context.configs.jwtConfig.TokenExp = config.TokenExp
   253  }
   254  
   255  func (self *HttpNode) EnableECC(enable bool) {
   256  	self.readyContext()
   257  	self.Context.System.enableECC = enable
   258  }
   259  
   260  func (self *HttpNode) SetSystem(name, version string) {
   261  	self.readyContext()
   262  	self.Context.System.Name = name
   263  	self.Context.System.Version = version
   264  }
   265  
   266  func (self *HttpNode) ClearFilterChain() {
   267  	for k, _ := range filterMap {
   268  		delete(filterMap, k)
   269  	}
   270  }
   271  
   272  func ErrorMsgToLang(ctx *Context, msg string, args ...string) string {
   273  	if len(msg) == 0 {
   274  		return msg
   275  	}
   276  	lang := ctx.ClientLanguage()
   277  	if len(lang) == 0 {
   278  		if len(ctx.configs.defaultLang) == 0 {
   279  			return msg
   280  		}
   281  		lang = ctx.configs.defaultLang
   282  	}
   283  	langKV, b := ctx.configs.langConfigs[lang]
   284  	if !b || len(langKV) == 0 {
   285  		if len(ctx.configs.defaultLang) == 0 {
   286  			return msg
   287  		}
   288  		langKV = ctx.configs.langConfigs[ctx.configs.defaultLang]
   289  		if len(langKV) == 0 {
   290  			return msg
   291  		}
   292  	}
   293  	find := utils.SPEL.FindAllStringSubmatch(msg, -1)
   294  	if len(find) == 0 {
   295  		return msg
   296  	}
   297  	for _, v := range find {
   298  		if len(v) != 2 {
   299  			continue
   300  		}
   301  		kv, b := langKV[v[1]]
   302  		if !b || len(kv) == 0 {
   303  			continue
   304  		}
   305  		fill := kv
   306  		for i, arg := range args {
   307  			fill = strings.ReplaceAll(fill, utils.AddStr("$", i+1), arg)
   308  		}
   309  		msg = strings.ReplaceAll(msg, v[0], fill)
   310  	}
   311  	return msg
   312  }
   313  
   314  func defaultRenderError(ctx *Context, err error) error {
   315  	if err == nil {
   316  		return nil
   317  	}
   318  	out := ex.Catch(err)
   319  	if ctx.errorHandle != nil {
   320  		throw, ok := err.(ex.Throw)
   321  		if !ok {
   322  			throw = ex.Throw{Code: out.Code, Msg: out.Msg, Err: err, Arg: out.Arg}
   323  		}
   324  		if err = ctx.errorHandle(ctx, throw); err != nil {
   325  			zlog.Error("response error handle failed", 0, zlog.AddError(err))
   326  		}
   327  	}
   328  	resp := &JsonResp{
   329  		Code:    out.Code,
   330  		Message: ErrorMsgToLang(ctx, out.Msg, out.Arg...),
   331  		Time:    utils.UnixMilli(),
   332  	}
   333  	if !ctx.Authenticated() {
   334  		resp.Nonce = utils.RandNonce()
   335  	} else {
   336  		if ctx.JsonBody == nil || len(ctx.JsonBody.Nonce) == 0 {
   337  			resp.Nonce = utils.RandNonce()
   338  		} else {
   339  			resp.Nonce = ctx.JsonBody.Nonce
   340  		}
   341  	}
   342  	if ctx.RouterConfig.Guest {
   343  		if out.Code <= 600 {
   344  			ctx.Response.StatusCode = out.Code
   345  		}
   346  		ctx.Response.ContentType = TEXT_PLAIN
   347  		ctx.Response.ContentEntityByte.Write(utils.Str2Bytes(resp.Message))
   348  		return nil
   349  	}
   350  	result, err := utils.JsonMarshal(resp)
   351  	if err != nil {
   352  		ctx.Response.ContentType = TEXT_PLAIN
   353  		ctx.Response.ContentEntityByte.Write(utils.Str2Bytes(err.Error()))
   354  		return nil
   355  	}
   356  	ctx.Response.ContentType = APPLICATION_JSON
   357  	ctx.Response.ContentEntityByte.Write(result)
   358  	return nil
   359  }
   360  
   361  func defaultRenderTo(ctx *Context) error {
   362  	ctx.RequestCtx.SetContentType(ctx.Response.ContentType)
   363  	if ctx.Response.StatusCode == 0 {
   364  		ctx.RequestCtx.SetStatusCode(http.StatusOK)
   365  	} else {
   366  		ctx.RequestCtx.SetStatusCode(ctx.Response.StatusCode)
   367  	}
   368  	if _, err := ctx.RequestCtx.Write(ctx.Response.ContentEntityByte.Bytes()); err != nil {
   369  		zlog.Error("response failed", 0, zlog.AddError(err))
   370  	}
   371  	return nil
   372  }
   373  
   374  func defaultRenderPre(ctx *Context) error {
   375  	routerConfig, _ := ctx.configs.routerConfigs[ctx.Path]
   376  	switch ctx.Response.ContentType {
   377  	case TEXT_PLAIN:
   378  		content := ctx.Response.ContentEntity
   379  		if v, b := content.(string); b {
   380  			ctx.Response.ContentEntityByte.Write(utils.Str2Bytes(v))
   381  		} else {
   382  			ctx.Response.ContentEntityByte.Write(utils.Str2Bytes(""))
   383  		}
   384  	case APPLICATION_JSON:
   385  		if ctx.Response.ContentEntity == nil {
   386  			return ex.Throw{Code: http.StatusInternalServerError, Msg: "response ContentEntity is nil"}
   387  		}
   388  		if routerConfig.Guest {
   389  			if result, err := utils.JsonMarshal(ctx.Response.ContentEntity); err != nil {
   390  				return ex.Throw{Code: http.StatusInternalServerError, Msg: "response JSON data failed", Err: err}
   391  			} else {
   392  				ctx.Response.ContentEntityByte.Write(result)
   393  			}
   394  			break
   395  		}
   396  		data, err := utils.JsonMarshal(ctx.Response.ContentEntity)
   397  		if err != nil {
   398  			return ex.Throw{Code: http.StatusInternalServerError, Msg: "response conversion JSON failed", Err: err}
   399  		}
   400  		resp := &JsonResp{
   401  			Code: http.StatusOK,
   402  			Time: utils.UnixMilli(),
   403  		}
   404  		if ctx.JsonBody == nil || len(ctx.JsonBody.Nonce) == 0 {
   405  			resp.Nonce = utils.RandNonce()
   406  		} else {
   407  			resp.Nonce = ctx.JsonBody.Nonce
   408  		}
   409  		var key string
   410  		if routerConfig.UseRSA || routerConfig.UseHAX { // 非登录状态响应
   411  			if ctx.JsonBody.Plan == 2 {
   412  				v := ctx.GetStorage(RandomCode)
   413  				if v == nil {
   414  					return ex.Throw{Msg: "encryption random code is nil"}
   415  				}
   416  				key, _ = v.(string)
   417  				data, err := utils.AesEncrypt(data, key, key)
   418  				if err != nil {
   419  					return ex.Throw{Code: http.StatusInternalServerError, Msg: "AES encryption response data failed", Err: err}
   420  				}
   421  				resp.Data = data
   422  				resp.Plan = 2
   423  				ctx.DelStorage(RandomCode)
   424  			} else if ctx.JsonBody.Plan == 3 {
   425  				resp.Data = utils.Base64Encode(data)
   426  				_, key = ctx.RSA.GetPublicKey()
   427  				resp.Plan = 3
   428  			} else {
   429  				return ex.Throw{Msg: "anonymous response plan invalid"}
   430  			}
   431  		} else if routerConfig.AesResponse {
   432  			data, err := utils.AesEncrypt(data, ctx.GetTokenSecret(), utils.AddStr(resp.Nonce, resp.Time))
   433  			if err != nil {
   434  				return ex.Throw{Code: http.StatusInternalServerError, Msg: "AES encryption response data failed", Err: err}
   435  			}
   436  			resp.Data = data
   437  			resp.Plan = 1
   438  		} else {
   439  			resp.Data = utils.Base64Encode(data)
   440  		}
   441  		resp.Sign = ctx.GetHmac256Sign(resp.Data.(string), resp.Nonce, resp.Time, resp.Plan, key)
   442  		if result, err := utils.JsonMarshal(resp); err != nil {
   443  			return ex.Throw{Code: http.StatusInternalServerError, Msg: "response JSON data failed", Err: err}
   444  		} else {
   445  			ctx.Response.ContentEntityByte.Write(result)
   446  		}
   447  	default:
   448  		return ex.Throw{Code: http.StatusUnsupportedMediaType, Msg: "invalid response ContentType"}
   449  	}
   450  	return nil
   451  }
   452  
   453  func (self *HttpNode) POST(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   454  	self.addRouter(POST, path, handle, routerConfig)
   455  }
   456  
   457  func (self *HttpNode) GET(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   458  	self.addRouter(GET, path, handle, routerConfig)
   459  }
   460  
   461  func (self *HttpNode) DELETE(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   462  	self.addRouter(DELETE, path, handle, routerConfig)
   463  }
   464  
   465  func (self *HttpNode) PUT(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   466  	self.addRouter(PUT, path, handle, routerConfig)
   467  }
   468  
   469  func (self *HttpNode) PATCH(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   470  	self.addRouter(PATCH, path, handle, routerConfig)
   471  }
   472  
   473  func (self *HttpNode) OPTIONS(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   474  	self.addRouter(OPTIONS, path, handle, routerConfig)
   475  }
   476  
   477  func (self *HttpNode) HEAD(path string, handle func(ctx *Context) error, routerConfig *RouterConfig) {
   478  	self.addRouter(HEAD, path, handle, routerConfig)
   479  }