123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- package websubsvc
- import (
- "errors"
- "fmt"
- "git.swzry.com/zry/zry-go-program-framework/core"
- "github.com/gin-gonic/gin"
- "net"
- "net/http"
- "net/http/httputil"
- "os"
- "strconv"
- "strings"
- "time"
- )
- type WebSubServiceContext struct {
- subSvcCtx *core.SubServiceContext
- core.IModuleLogger
- s *WebSubService
- }
- // GetSubSvcCtx get the SubServiceContext of this sub service
- func (c *WebSubServiceContext) GetSubSvcCtx() *core.SubServiceContext {
- return c.subSvcCtx
- }
- // GetRootRouter get the root RouterGroup of gin engine
- func (c *WebSubServiceContext) GetRootRouter() *gin.RouterGroup {
- return &c.s.ginEngine.RouterGroup
- }
- // DefaultMiddleware will call EnableLogger and EnableRecovery
- func (c *WebSubServiceContext) DefaultMiddleware(logPrefix, recoveryLogPrefix string) {
- c.EnableLogger(logPrefix)
- c.EnableRecovery(recoveryLogPrefix)
- }
- // EnableTLS if you want to use TLS, please call this from IWebSubServiceLogic.GetHttpServer
- func (c *WebSubServiceContext) EnableTLS(certFile, keyFile string) {
- c.s.enbaleTLS = true
- c.s.certFile = certFile
- c.s.keyFile = keyFile
- }
- // SetShutdownTimeout set the graceful shutdown timeout when stopping server.
- // will force stop server when timeout exceeded.
- func (c *WebSubServiceContext) SetShutdownTimeout(d time.Duration) {
- c.s.shutdownTimeout = d
- }
- // MakeHttpServer create a http.Server for IWebSubServiceLogic.GetHttpServer
- // bindAddr: the address to bind
- // config: the config for http server. refer to http.Server. for basic usage, you can give it nil.
- func (c *WebSubServiceContext) MakeHttpServer(bindAddr string, config *HttpServerConfig) *http.Server {
- cfg := config
- if cfg == nil {
- cfg = &HttpServerConfig{}
- }
- h := &http.Server{
- Addr: bindAddr,
- Handler: c.s.ginEngine,
- DisableGeneralOptionsHandler: cfg.DisableGeneralOptionsHandler,
- TLSConfig: cfg.TLSConfig,
- ReadTimeout: cfg.ReadTimeout,
- ReadHeaderTimeout: cfg.WriteTimeout,
- WriteTimeout: cfg.IdleTimeout,
- IdleTimeout: cfg.IdleTimeout,
- MaxHeaderBytes: cfg.MaxHeaderBytes,
- TLSNextProto: cfg.TLSNextProto,
- ConnState: cfg.ConnState,
- ErrorLog: cfg.ErrorLog,
- BaseContext: cfg.BaseContext,
- ConnContext: cfg.ConnContext,
- }
- return h
- }
- // EnableLogger enable gin logger middleware
- func (c *WebSubServiceContext) EnableLogger(logPrefix string) {
- l := c.GetSubLog(logPrefix)
- gf := func(c *gin.Context) {
- start := time.Now()
- path := c.Request.URL.Path
- raw := c.Request.URL.RawQuery
- c.Next()
- end := time.Now()
- latency := end.Sub(start).String()
- clientIP := c.ClientIP()
- method := c.Request.Method
- statusCode := c.Writer.Status()
- comment := c.Errors.ByType(gin.ErrorTypePrivate).String()
- if raw != "" {
- path = path + "?" + raw
- }
- mv := map[string]string{
- "time": ginTimeFormat(end),
- "status": strconv.Itoa(statusCode),
- "latency": latency,
- "clientIP": clientIP,
- "method": method,
- "path": path,
- "comment": comment,
- }
- l.VerboseC(mv)
- }
- c.s.ginEngine.Use(gf)
- }
- // EnableRecovery enable gin recovery middleware
- func (c *WebSubServiceContext) EnableRecovery(recoveryLogPrefix string) {
- l := c.GetSubLog(recoveryLogPrefix)
- fn := func(c *gin.Context) {
- defer func() {
- if err := recover(); err != nil {
- // Check for a broken connection, as it is not really a
- // condition that warrants a panic stack trace.
- var brokenPipe bool
- if ne, ok := err.(*net.OpError); ok {
- var se *os.SyscallError
- if errors.As(ne, &se) {
- seStr := strings.ToLower(se.Error())
- if strings.Contains(seStr, "broken pipe") ||
- strings.Contains(seStr, "connection reset by peer") {
- brokenPipe = true
- }
- }
- }
- stack := ginStack(3)
- httpRequest, _ := httputil.DumpRequest(c.Request, false)
- headers := strings.Split(string(httpRequest), "\r\n")
- for idx, header := range headers {
- current := strings.Split(header, ":")
- if current[0] == "Authorization" {
- headers[idx] = current[0] + ": *"
- }
- }
- headersToStr := strings.Join(headers, "\r\n")
- if brokenPipe {
- l.WarnC(map[string]string{
- "err_type": "connection_broken",
- "raw_err": fmt.Sprintf("%v", err),
- "headers": headersToStr,
- })
- } else if gin.IsDebugging() {
- l.WarnC(map[string]string{
- "err_type": "panic_recovery",
- "raw_err": fmt.Sprintf("%v", err),
- "headers": headersToStr,
- "stack_trace": string(stack),
- })
- } else {
- l.WarnC(map[string]string{
- "err_type": "panic_recovery",
- "raw_err": fmt.Sprintf("%v", err),
- "headers": headersToStr,
- "stack_trace": string(stack),
- })
- }
- if brokenPipe {
- // If the connection is dead, we can't write a status to it.
- c.Error(err.(error)) //nolint: errcheck
- c.Abort()
- } else {
- c.AbortWithStatus(http.StatusInternalServerError)
- }
- }
- }()
- c.Next()
- }
- c.s.ginEngine.Use(fn)
- }
|