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 }