github.com/aldelo/common@v1.5.1/wrapper/gin/gin.go (about)

     1  package gin
     2  
     3  /*
     4   * Copyright 2020-2023 Aldelo, LP
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at
     9   *
    10   *     http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  import (
    20  	"database/sql"
    21  	"fmt"
    22  	util "github.com/aldelo/common"
    23  	"github.com/aldelo/common/wrapper/gin/ginbindtype"
    24  	"github.com/aldelo/common/wrapper/gin/gingzipcompression"
    25  	"github.com/aldelo/common/wrapper/gin/ginhttpmethod"
    26  	"github.com/aldelo/common/wrapper/xray"
    27  	"github.com/gin-contrib/cors"
    28  	"github.com/gin-contrib/gzip"
    29  	"github.com/gin-contrib/sessions"
    30  	"github.com/gin-contrib/sessions/cookie"
    31  	"github.com/gin-contrib/sessions/redis"
    32  	"github.com/gin-gonic/gin"
    33  	"github.com/gin-gonic/gin/binding"
    34  	"github.com/patrickmn/go-cache"
    35  	"github.com/utrack/gin-csrf"
    36  	"golang.org/x/time/rate"
    37  	"log"
    38  	"reflect"
    39  	"strings"
    40  	"time"
    41  )
    42  
    43  // Gin struct provides a wrapper for gin-gonic based web server operations
    44  //
    45  // Name = (required) web server descriptive display name
    46  // Port = (required) tcp port that this web server will run on
    47  // TlsCertPemFile / TlsCertKeyFile = (optional) when both are set, web server runs secured mode using tls cert; path to pem and key file
    48  // Routes = (required) map of http route handlers to be registered, middleware to be configured,
    49  //
    50  //	for gin engine or route groups,
    51  //	key = * indicates base gin engine routes, otherwise key refers to routeGroup to be created
    52  //
    53  // SessionMiddleware = (optional) defines the cookie or redis session middleware to setup for the gin engine
    54  // CsrfMiddleware = (optional) defines the csrf middleware to setup for the gin engine (requires SessionMiddleware setup)
    55  //
    56  // AWS-XRay Tracing = Passing Parent SegmentID and TraceID to Handler via Gin Context
    57  //
    58  //	Using Headers:
    59  //		"X-Amzn-Seg-Id" = Parent XRay Segment ID to pass into Headers
    60  //		"X-Amzn-Tr-Id" = Parent XRay Trace ID to pass into Headers
    61  type Gin struct {
    62  	// web server descriptive name (used for display and logging only)
    63  	Name string
    64  
    65  	// web server port to run gin web server
    66  	Port uint
    67  
    68  	// web server tls certificate pem and key file path
    69  	TlsCertPemFile string
    70  	TlsCertKeyFile string
    71  
    72  	// google recaptcha v2 secret
    73  	GoogleRecaptchaSecret string
    74  
    75  	// web server routes to handle
    76  	// string = routeGroup path if defined, otherwise, if * refers to base
    77  	Routes map[string]*RouteDefinition
    78  
    79  	// define the session middleware for the gin engine
    80  	SessionMiddleware *SessionConfig
    81  
    82  	// define the csrf middleware for the gin engine
    83  	CsrfMiddleware *CsrfConfig
    84  
    85  	// define html template renderer
    86  	HtmlTemplateRenderer *GinTemplate
    87  
    88  	// define http status error handler
    89  	HttpStatusErrorHandler func(status int, trace string, c *gin.Context)
    90  
    91  	// web server instance
    92  	_ginEngine    *gin.Engine
    93  	_ginJwtAuth   *GinJwt
    94  	_limiterCache *cache.Cache
    95  }
    96  
    97  // RouteDefinition struct contains per route group or gin engine's handlers and middleware
    98  //
    99  // Note:
   100  //  1. route definition's map key = * means gin engine; key named refers to Route Group
   101  //
   102  // Routes = (required) one or more route handlers defined for current route group or base engine
   103  // CorsMiddleware = (optional) current cors middleware to use if setup for current route group or base engine
   104  // MaxLimitMiddleware = (optional) current max rate limit middleware, controls how many concurrent handlers can process actions for the current route group or base engine
   105  // PerClientQpsMiddleware = (optional) to enable per client Ip QPS limiter middleware, all 3 options must be set
   106  // UseAuthMiddleware = (optional) to indicate if this route group uses auth middleware
   107  // CustomMiddleware = (optional) slice of additional custom HandleFunc middleware
   108  type RouteDefinition struct {
   109  	Routes []*Route
   110  
   111  	CorsMiddleware         *cors.Config
   112  	MaxLimitMiddleware     *int
   113  	PerClientQpsMiddleware *PerClientQps
   114  	GZipMiddleware         *GZipConfig
   115  	UseAuthMiddleware      bool
   116  	CustomMiddleware       []gin.HandlerFunc
   117  }
   118  
   119  // Route struct defines each http route handler endpoint
   120  //
   121  // RelativePath = (required) route path such as /HelloWorld, this is the path to trigger the route handler
   122  // Method = (required) GET, POST, PUT, DELETE
   123  // Binding = (optional) various input data binding to target type option
   124  // BindingInputPtr = (conditional) binding input object pointer, required if binding type is set
   125  // Handler = (required) function handler to be executed per method defined (actual logic goes inside handler)
   126  //  1. gin.Context Value Return Helpers:
   127  //     a) c.String(), c.HTML(), c.JSON(), c.JSONP(), c.PureJSON(), c.AsciiJSON(), c.SecureJSON(), c.XML(), c.YAML(),
   128  //     c.ProtoBuf(), c.Redirect(), c.Data(), c.DataFromReader(), c.Render()
   129  type Route struct {
   130  	// relative path to the endpoint for the route to handle
   131  	RelativePath string
   132  
   133  	// http method such as GET, POST, PUT, DELETE
   134  	Method ginhttpmethod.GinHttpMethod
   135  
   136  	// input value binding to be performed
   137  	Binding ginbindtype.GinBindType
   138  
   139  	// binding input object pointer
   140  	BindingInputPtr interface{}
   141  
   142  	// actual handler method to be triggered,
   143  	// bindingInputPtr if any, is the binding resolved object passed into the handler method
   144  	Handler func(c *gin.Context, bindingInputPtr interface{})
   145  }
   146  
   147  // PerClientQps defines options for the PerClientQps based rate limit middleware
   148  type PerClientQps struct {
   149  	Qps   int
   150  	Burst int
   151  	TTL   time.Duration
   152  }
   153  
   154  // GZipConfig defines options for the GZip middleware
   155  type GZipConfig struct {
   156  	Compression        gingzipcompression.GinGZipCompression
   157  	ExcludedExtensions []string
   158  	ExcludedPaths      []string
   159  	ExcludedPathsRegex []string
   160  }
   161  
   162  // GetGZipCompression returns gzip compression value
   163  func (gz *GZipConfig) GetGZipCompression() int {
   164  	switch gz.Compression {
   165  	case gingzipcompression.Default:
   166  		return gzip.DefaultCompression
   167  	case gingzipcompression.BestCompression:
   168  		return gzip.BestCompression
   169  	case gingzipcompression.BestSpeed:
   170  		return gzip.BestSpeed
   171  	default:
   172  		return gzip.NoCompression
   173  	}
   174  }
   175  
   176  // GetGZipExcludedExtensions return gzip option for excluded extensions if any
   177  func (gz *GZipConfig) GetGZipExcludedExtensions() gzip.Option {
   178  	if len(gz.ExcludedExtensions) > 0 {
   179  		return gzip.WithExcludedExtensions(gz.ExcludedExtensions)
   180  	} else {
   181  		return nil
   182  	}
   183  }
   184  
   185  // GetGZipExcludedPaths return gzip option for excluded paths if any
   186  func (gz *GZipConfig) GetGZipExcludedPaths() gzip.Option {
   187  	if len(gz.ExcludedPaths) > 0 {
   188  		return gzip.WithExcludedPaths(gz.ExcludedPaths)
   189  	} else {
   190  		return nil
   191  	}
   192  }
   193  
   194  // GetGZipExcludedPathsRegex return gzip option for excluded paths refex if any
   195  func (gz *GZipConfig) GetGZipExcludedPathsRegex() gzip.Option {
   196  	if len(gz.ExcludedPathsRegex) > 0 {
   197  		return gzip.WithExcludedPathsRegexs(gz.ExcludedPathsRegex)
   198  	} else {
   199  		return nil
   200  	}
   201  }
   202  
   203  // SessionConfig defines redis or cookie session configuration options
   204  //
   205  // SecretKey = (required) for redis or cookie session, the secret key to use
   206  // SessionNames = (required) for redis or cookie session, defined session names to use by middleware
   207  // RedisMaxIdleConnections = (optional) for redis session, maximum number of idle connections to keep for redis client
   208  // RedisHostAndPort = (optional) the redis endpoint host name and port, in host:port format (if not set, then cookie session is assumed)
   209  //
   210  // To Use Sessions in Handler:
   211  //
   212  //	[Single Session]
   213  //		session := sessions.Default(c)
   214  //		v := session.Get("xyz")
   215  //		session.Set("xyz", xyz)
   216  //		session.Save()
   217  //	[Multiple Sessions]
   218  //		sessionA := sessions.DefaultMany(c, "a")
   219  //		sessionB := sessions.DefaultMany(c, "b")
   220  //		sessionA.Get("xyz")
   221  //		sessionA.Set("xyz", xyz)
   222  //		sessionA.Save()
   223  type SessionConfig struct {
   224  	SecretKey               string
   225  	SessionNames            []string
   226  	RedisMaxIdleConnections int
   227  	RedisHostAndPort        string
   228  }
   229  
   230  // CsrfConfig defines csrf protection middleware options
   231  //
   232  // Secret = (required) csrf secret key used for csrf protection
   233  // ErrorFunc = (required) csrf invalid token error handler
   234  // TokenGetter = (optional) csrf get token action override from default (in case implementation not using default keys)
   235  //
   236  //	    default gets token from:
   237  //		   - FormValue("_csrf")
   238  //		   - Url.Query().Get("_csrf")
   239  //		   - Header.Get("X-CSRF-TOKEN")
   240  //		   - Header.Get("X-XSRF-TOKEN")
   241  type CsrfConfig struct {
   242  	Secret      string
   243  	ErrorFunc   func(c *gin.Context)
   244  	TokenGetter func(c *gin.Context) string
   245  }
   246  
   247  // NewServer returns a gin-gongic web server wrapper ready for setup
   248  //
   249  // customRecovery = indicates if the gin engine default recovery will be replaced, with one that has more custom render
   250  // customHttpErrorHandler = func to custom handle http error
   251  //
   252  // if gin default logger is to be replaced, it must be replaced via zaplogger parameter,
   253  // zaplogger must be fully setup and passed into NewServer in order for zaplogger replacement to be effective,
   254  // zaplogger will not be setup after gin object is created
   255  func NewServer(name string, port uint, releaseMode bool, customRecovery bool, customHttpErrorHandler func(status int, trace string, c *gin.Context), zaplogger ...*GinZap) *Gin {
   256  	var z *GinZap
   257  
   258  	if len(zaplogger) > 0 {
   259  		z = zaplogger[0]
   260  	}
   261  
   262  	mode := gin.ReleaseMode
   263  
   264  	if !releaseMode {
   265  		mode = gin.DebugMode
   266  	}
   267  
   268  	gin.SetMode(mode)
   269  
   270  	gw := &Gin{
   271  		Name:                   name,
   272  		Port:                   port,
   273  		HttpStatusErrorHandler: customHttpErrorHandler,
   274  		_ginEngine:             nil,
   275  		_ginJwtAuth:            nil,
   276  		_limiterCache:          cache.New(5*time.Minute, 10*time.Minute),
   277  	}
   278  
   279  	if z != nil && util.LenTrim(z.LogName) > 0 {
   280  		if err := z.Init(); err == nil {
   281  			gw._ginEngine = gin.New()
   282  			gw._ginEngine.Use(z.NormalLogger())
   283  			gw._ginEngine.Use(z.PanicLogger())
   284  			log.Println("Using Zap Logger...")
   285  		} else {
   286  			if customRecovery {
   287  				gw._ginEngine = gin.New()
   288  				gw._ginEngine.Use(gin.Logger())
   289  				gw._ginEngine.Use(NiceRecovery(func(c *gin.Context, err interface{}) {
   290  					if gw.HttpStatusErrorHandler == nil {
   291  						c.String(500, err.(error).Error())
   292  					} else {
   293  						gw.HttpStatusErrorHandler(500, err.(error).Error(), c)
   294  					}
   295  				}))
   296  
   297  				log.Println("Using Custom Recovery...")
   298  			} else {
   299  				gw._ginEngine = gin.Default()
   300  				log.Println("Using Default Recovery, Logger...")
   301  			}
   302  		}
   303  	} else {
   304  		if customRecovery {
   305  			gw._ginEngine = gin.New()
   306  			gw._ginEngine.Use(gin.Logger())
   307  			gw._ginEngine.Use(NiceRecovery(func(c *gin.Context, err interface{}) {
   308  				if gw.HttpStatusErrorHandler == nil {
   309  					c.String(500, err.(error).Error())
   310  				} else {
   311  					gw.HttpStatusErrorHandler(500, err.(error).Error(), c)
   312  				}
   313  			}))
   314  			log.Println("Using Custom Recovery...")
   315  		} else {
   316  			gw._ginEngine = gin.Default()
   317  			log.Println("Using Default Recovery, Logger...")
   318  		}
   319  	}
   320  
   321  	return gw
   322  }
   323  
   324  // NewAuthMiddleware will create a new jwt auth middleware with basic info provided,
   325  // then this new middleware is set into Gin wrapper internal var,
   326  // this middleware additional fields and handlers must then be defined by accessing the AuthMiddleware func,
   327  // once middleware completely prepared, then call the RunServer which automatically builds the auth middleware for use
   328  func (g *Gin) NewAuthMiddleware(realm string, identityKey string, signingSecretKey string, authenticateBinding ginbindtype.GinBindType, setup ...func(j *GinJwt)) bool {
   329  	g._ginJwtAuth = nil
   330  
   331  	if g._ginEngine == nil {
   332  		return false
   333  	}
   334  
   335  	g._ginJwtAuth = NewGinJwtMiddleware(realm, identityKey, signingSecretKey, authenticateBinding)
   336  
   337  	if len(setup) > 0 {
   338  		setup[0](g._ginJwtAuth)
   339  	}
   340  
   341  	return true
   342  }
   343  
   344  // AuthMiddleware returns the GinJwt struct object built by NewAuthMiddleware,
   345  // prepare the necessary field values and handlers via this method's return object access
   346  func (g *Gin) AuthMiddleware() *GinJwt {
   347  	return g._ginJwtAuth
   348  }
   349  
   350  // ExtractJwtClaims will extra jwt claims from context and return via map
   351  func (g *Gin) ExtractJwtClaims(c *gin.Context) map[string]interface{} {
   352  	if g._ginJwtAuth != nil {
   353  		return g._ginJwtAuth.ExtractClaims(c)
   354  	} else {
   355  		return nil
   356  	}
   357  }
   358  
   359  // Engine represents the gin engine itself
   360  func (g *Gin) Engine() *gin.Engine {
   361  	if g._ginEngine == nil {
   362  		return nil
   363  	} else {
   364  		return g._ginEngine
   365  	}
   366  }
   367  
   368  // RunServer starts gin-gonic web server,
   369  // method will run in blocking mode, until gin server exits,
   370  // if run server failed, an error is returned
   371  func (g *Gin) RunServer() error {
   372  	if g._ginEngine == nil {
   373  		return fmt.Errorf("Run Web Server Failed: %s", "Server Engine Not Defined")
   374  	}
   375  
   376  	if util.LenTrim(g.Name) == 0 {
   377  		return fmt.Errorf("Run Web Server Failed: %s", "Web Server Name Not Defined")
   378  	}
   379  
   380  	if g.Port > 65535 {
   381  		return fmt.Errorf("Run Web Server Failed: %s", "Port Number Cannot Exceed 65535")
   382  	}
   383  
   384  	// setup html template renderer
   385  	if g.HtmlTemplateRenderer != nil {
   386  		g.setupHtmlTemplateRenderer()
   387  	}
   388  
   389  	// setup auth middleware
   390  	if g._ginJwtAuth != nil {
   391  		if err := g._ginJwtAuth.BuildGinJwtMiddleware(g); err != nil {
   392  			return fmt.Errorf("Run Web Server Failed: (%s) %s", "Build Auth Middleware Errored", err.Error())
   393  		}
   394  	}
   395  
   396  	// setup routes
   397  	if g.setupRoutes() <= 0 {
   398  		return fmt.Errorf("Run Web Server Failed: %s", "Http Routes Not Defined")
   399  	}
   400  
   401  	log.Println("Web Server '" + g.Name + "' Started..." + util.GetLocalIP() + ":" + util.UintToStr(g.Port))
   402  
   403  	var err error
   404  
   405  	if util.LenTrim(g.TlsCertPemFile) > 0 && util.LenTrim(g.TlsCertKeyFile) > 0 {
   406  		// gin on tls
   407  		log.Println("Web Server Tls Mode")
   408  		err = g._ginEngine.RunTLS(fmt.Sprintf(":%d", g.Port), g.TlsCertPemFile, g.TlsCertKeyFile)
   409  	} else {
   410  		// gin on non-tls
   411  		log.Println("Web Server Non-Tls Mode")
   412  		err = g._ginEngine.Run(fmt.Sprintf(":%d", g.Port))
   413  	}
   414  
   415  	if err != nil {
   416  		return fmt.Errorf("Web Server '" + g.Name + "' Failed To Start: " + err.Error())
   417  	} else {
   418  		log.Println("Web Server '" + g.Name + "' Stopped")
   419  		return nil
   420  	}
   421  }
   422  
   423  // BindPostForm will bind the post form data to outputPtr based on given tag names mapping
   424  func (g *Gin) BindPostForm(outputPtr interface{}, tagName string, c *gin.Context) error {
   425  	if outputPtr == nil {
   426  		return fmt.Errorf("BindPostForm Requires Output Variable Pointer")
   427  	}
   428  
   429  	if util.LenTrim(tagName) == 0 {
   430  		return fmt.Errorf("BindPostForm Requires TagName")
   431  	}
   432  
   433  	if c == nil {
   434  		return fmt.Errorf("BindPostForm Requires Gin Context")
   435  	}
   436  
   437  	s := reflect.ValueOf(outputPtr).Elem()
   438  
   439  	if s.Kind() != reflect.Struct {
   440  		return fmt.Errorf("BindPostForm Requires Struct")
   441  	}
   442  
   443  	for i := 0; i < s.NumField(); i++ {
   444  		field := s.Type().Field(i)
   445  
   446  		if o := s.FieldByName(field.Name); o.IsValid() && o.CanSet() {
   447  			if tag := field.Tag.Get(tagName); util.LenTrim(tag) > 0 {
   448  				if v := c.PostForm(tag); util.LenTrim(v) > 0 {
   449  					switch o.Kind() {
   450  					case reflect.String:
   451  						o.SetString(v)
   452  					case reflect.Bool:
   453  						b, _ := util.ParseBool(v)
   454  						o.SetBool(b)
   455  					case reflect.Int8:
   456  						fallthrough
   457  					case reflect.Int16:
   458  						fallthrough
   459  					case reflect.Int:
   460  						fallthrough
   461  					case reflect.Int32:
   462  						fallthrough
   463  					case reflect.Int64:
   464  						if i64, ok := util.ParseInt64(v); ok {
   465  							if !o.OverflowInt(i64) {
   466  								o.SetInt(i64)
   467  							}
   468  						}
   469  					case reflect.Float32:
   470  						fallthrough
   471  					case reflect.Float64:
   472  						if f64, ok := util.ParseFloat64(v); ok {
   473  							if !o.OverflowFloat(f64) {
   474  								o.SetFloat(f64)
   475  							}
   476  						}
   477  					case reflect.Uint8:
   478  						fallthrough
   479  					case reflect.Uint16:
   480  						fallthrough
   481  					case reflect.Uint:
   482  						fallthrough
   483  					case reflect.Uint32:
   484  						fallthrough
   485  					case reflect.Uint64:
   486  						ui := uint64(util.StrToUint(v))
   487  						if !o.OverflowUint(ui) {
   488  							o.SetUint(ui)
   489  						}
   490  					default:
   491  						switch f := o.Interface().(type) {
   492  						case sql.NullString:
   493  							f = util.ToNullString(v, true)
   494  							o.Set(reflect.ValueOf(f))
   495  						case sql.NullBool:
   496  							b, _ := util.ParseBool(v)
   497  							f = util.ToNullBool(b)
   498  							o.Set(reflect.ValueOf(f))
   499  						case sql.NullFloat64:
   500  							f64, _ := util.ParseFloat64(v)
   501  							f = util.ToNullFloat64(f64, true)
   502  							o.Set(reflect.ValueOf(f))
   503  						case sql.NullInt32:
   504  							i32, _ := util.ParseInt32(v)
   505  							f = util.ToNullInt(i32, true)
   506  							o.Set(reflect.ValueOf(f))
   507  						case sql.NullInt64:
   508  							i64, _ := util.ParseInt64(v)
   509  							f = util.ToNullInt64(i64, true)
   510  							o.Set(reflect.ValueOf(f))
   511  						case sql.NullTime:
   512  							f = util.ToNullTime(util.ParseDateTime(v))
   513  							o.Set(reflect.ValueOf(f))
   514  						case time.Time:
   515  							f = util.ParseDateTime(v)
   516  							o.Set(reflect.ValueOf(f))
   517  						default:
   518  							return fmt.Errorf("BindPostForm Encountered Unhandled Field Type: %s", o.Kind().String())
   519  						}
   520  					}
   521  				}
   522  			}
   523  		}
   524  	}
   525  
   526  	return nil
   527  }
   528  
   529  // bindInput will attempt to bind input data to target binding output, for example json string to struct mapped to json elements
   530  //
   531  // bindObjPtr = pointer to the target object, cannot be nil
   532  func (g *Gin) bindInput(c *gin.Context, bindType ginbindtype.GinBindType, bindObjPtr interface{}) (err error) {
   533  	if c == nil {
   534  		return fmt.Errorf("Binding Context is Nil")
   535  	}
   536  
   537  	if !bindType.Valid() {
   538  		return fmt.Errorf("Binding Type Not Valid")
   539  	}
   540  
   541  	if bindObjPtr == nil {
   542  		return fmt.Errorf("Binding Target Object Pointer Not Defined")
   543  	}
   544  
   545  	switch bindType {
   546  	case ginbindtype.BindHeader:
   547  		err = c.ShouldBindHeader(bindObjPtr)
   548  	case ginbindtype.BindJson:
   549  		err = c.ShouldBindJSON(bindObjPtr)
   550  	case ginbindtype.BindProtoBuf:
   551  		err = c.ShouldBindWith(bindObjPtr, binding.ProtoBuf)
   552  	case ginbindtype.BindQuery:
   553  		err = c.ShouldBindQuery(bindObjPtr)
   554  	case ginbindtype.BindUri:
   555  		err = c.ShouldBindUri(bindObjPtr)
   556  	case ginbindtype.BindXml:
   557  		err = c.ShouldBindXML(bindObjPtr)
   558  	case ginbindtype.BindYaml:
   559  		err = c.ShouldBindYAML(bindObjPtr)
   560  	case ginbindtype.BindPostForm:
   561  		err = g.BindPostForm(bindObjPtr, "json", c)
   562  	default:
   563  		err = c.ShouldBind(bindObjPtr)
   564  	}
   565  
   566  	if err != nil {
   567  		log.Println("Bind Error:", err.Error(), "; Bind Type:", bindType.Key())
   568  		return err
   569  	}
   570  
   571  	return nil
   572  }
   573  
   574  // setupRoutes prepares gin engine with route handlers, middleware and etc.
   575  func (g *Gin) setupRoutes() int {
   576  	if g.Routes == nil {
   577  		return 0
   578  	}
   579  
   580  	count := 0
   581  
   582  	// setup health check route
   583  	g._ginEngine.GET("/health", func(c *gin.Context) {
   584  		c.String(200, "OK")
   585  	})
   586  
   587  	if g.HttpStatusErrorHandler != nil {
   588  		g._ginEngine.NoRoute(func(context *gin.Context) {
   589  			g.HttpStatusErrorHandler(404, "NoRoute", context)
   590  		})
   591  
   592  		g._ginEngine.NoMethod(func(context *gin.Context) {
   593  			g.HttpStatusErrorHandler(404, "NoMethod", context)
   594  		})
   595  	}
   596  
   597  	// setup xray trace middleware
   598  	if xray.XRayServiceOn() {
   599  		g._ginEngine.Use(XRayMiddleware())
   600  	}
   601  
   602  	// setup session if configured
   603  	if g.SessionMiddleware != nil {
   604  		g.setupSessionMiddleware()
   605  	}
   606  
   607  	// setup csrf if configured
   608  	if g.CsrfMiddleware != nil {
   609  		g.setupCsrfMiddleware()
   610  	}
   611  
   612  	// setup routes for engine and route groups
   613  	for k, v := range g.Routes {
   614  		var rg *gin.RouterGroup
   615  
   616  		if k != "*" && k != "base" {
   617  			// route group
   618  			rg = g._ginEngine.Group(k)
   619  		}
   620  
   621  		routeFn := func() gin.IRoutes {
   622  			if rg == nil {
   623  				return g._ginEngine
   624  			} else {
   625  				return rg
   626  			}
   627  		}
   628  
   629  		if v == nil {
   630  			continue
   631  		}
   632  
   633  		//
   634  		// config any middleware first
   635  		//
   636  		if v.CorsMiddleware != nil && !v.CorsMiddleware.AllowAllOrigins && len(v.CorsMiddleware.AllowOrigins) > 0 {
   637  			g.setupCorsMiddleware(routeFn(), v.CorsMiddleware)
   638  		}
   639  
   640  		if v.MaxLimitMiddleware != nil && *v.MaxLimitMiddleware > 0 {
   641  			g.setupMaxLimitMiddleware(routeFn(), *v.MaxLimitMiddleware)
   642  		}
   643  
   644  		if v.PerClientQpsMiddleware != nil {
   645  			g.setupPerClientIpQpsMiddleware(routeFn(), v.PerClientQpsMiddleware.Qps, v.PerClientQpsMiddleware.Burst, v.PerClientQpsMiddleware.TTL)
   646  		}
   647  
   648  		if v.GZipMiddleware != nil {
   649  			g.setupGZipMiddleware(routeFn(), v.GZipMiddleware)
   650  		}
   651  
   652  		if v.UseAuthMiddleware && g._ginJwtAuth != nil && g._ginJwtAuth._ginJwtMiddleware != nil {
   653  			routeFn().Use(g._ginJwtAuth.AuthMiddleware())
   654  			log.Println("Using Jwt Auth Middleware...")
   655  		}
   656  
   657  		if len(v.CustomMiddleware) > 0 {
   658  			routeFn().Use(v.CustomMiddleware...)
   659  			log.Println("Using Custom Middleware...")
   660  		}
   661  
   662  		//
   663  		// setup route handlers
   664  		//
   665  		for _, h := range v.Routes {
   666  			log.Println("Setting Up Route Handler: " + h.RelativePath)
   667  
   668  			if !h.Method.Valid() || h.Method == ginhttpmethod.UNKNOWN {
   669  				continue
   670  			}
   671  
   672  			if !h.Binding.Valid() {
   673  				continue
   674  			}
   675  
   676  			if util.LenTrim(h.RelativePath) == 0 {
   677  				continue
   678  			}
   679  
   680  			if h.Handler == nil {
   681  				continue
   682  			}
   683  
   684  			// add route
   685  			switch h.Method {
   686  			case ginhttpmethod.GET:
   687  				routeFn().GET(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler))
   688  
   689  			case ginhttpmethod.POST:
   690  				routeFn().POST(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler))
   691  
   692  			case ginhttpmethod.PUT:
   693  				routeFn().PUT(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler))
   694  
   695  			case ginhttpmethod.DELETE:
   696  				routeFn().DELETE(h.RelativePath, g.newRouteFunc(h.RelativePath, h.Method.Key(), h.Binding, h.BindingInputPtr, h.Handler))
   697  
   698  			default:
   699  				continue
   700  			}
   701  
   702  			// add handler counter
   703  			count++
   704  		}
   705  	}
   706  
   707  	return count
   708  }
   709  
   710  // newRouteFunc returns closure to route handler setup,
   711  // if we define the route handler within the loop in Route Setup, the handler func were reused (not desired effect),
   712  // however, using closure ensures each relative path uses its own route func
   713  func (g *Gin) newRouteFunc(relativePath string, method string, bindingType ginbindtype.GinBindType, bindingInputPtr interface{},
   714  	handler func(c *gin.Context, bindingInputPtr interface{})) func(context *gin.Context) {
   715  	return func(c *gin.Context) {
   716  		if util.LenTrim(g.GoogleRecaptchaSecret) > 0 {
   717  			c.Set("google_recaptcha_secret", g.GoogleRecaptchaSecret)
   718  		}
   719  
   720  		if bindingInputPtr != nil {
   721  			// will perform binding
   722  			// create new object of bindingInputPtr
   723  			if t := util.ReflectGetType(bindingInputPtr); t != nil {
   724  				bindingInputPtr = util.ReflectObjectNewPtr(t)
   725  
   726  				if bindingInputPtr == nil {
   727  					_ = c.AbortWithError(500, fmt.Errorf("%s %s Failed on %s Binding: %s", method, relativePath, bindingType.Key(), "ReflectObjectNewPtr() Yielded Nil for Target Binding Object"))
   728  					handler(c, nil)
   729  					return
   730  				}
   731  			}
   732  
   733  			if err := g.bindInput(c, bindingType, bindingInputPtr); err != nil {
   734  				// binding error
   735  				_ = c.AbortWithError(500, fmt.Errorf("%s %s Failed on %s Binding: %s", method, relativePath, bindingType.Key(), err.Error()))
   736  				handler(c, nil)
   737  			} else {
   738  				// continue processing
   739  				handler(c, bindingInputPtr)
   740  			}
   741  		} else {
   742  			// no binding requested
   743  			handler(c, nil)
   744  		}
   745  	}
   746  }
   747  
   748  // setupCorsMiddleware is a helper to setup gin middleware
   749  func (g *Gin) setupCorsMiddleware(rg gin.IRoutes, corsConfig *cors.Config) {
   750  	if rg != nil && corsConfig != nil {
   751  		config := cors.DefaultConfig()
   752  
   753  		if len(corsConfig.AllowOrigins) > 0 {
   754  			orig := []string{}
   755  
   756  			for _, v := range corsConfig.AllowOrigins {
   757  				if util.LenTrim(v) > 0 && strings.ToLower(util.Left(v, 4)) == "http" {
   758  					orig = append(orig, v)
   759  				}
   760  			}
   761  
   762  			if len(orig) > 0 {
   763  				config.AllowOrigins = orig
   764  			}
   765  		}
   766  
   767  		if len(corsConfig.AllowMethods) > 0 {
   768  			method := []string{}
   769  
   770  			for _, v := range corsConfig.AllowMethods {
   771  				if util.LenTrim(v) > 0 {
   772  					method = append(method, v)
   773  				}
   774  			}
   775  
   776  			if len(method) > 0 {
   777  				config.AllowMethods = method
   778  			}
   779  		}
   780  
   781  		if len(corsConfig.AllowHeaders) > 0 {
   782  			header := []string{}
   783  
   784  			for _, v := range corsConfig.AllowHeaders {
   785  				if util.LenTrim(v) > 0 {
   786  					header = append(header, v)
   787  				}
   788  			}
   789  
   790  			if len(header) > 0 {
   791  				config.AllowHeaders = header
   792  			}
   793  		}
   794  
   795  		if len(corsConfig.ExposeHeaders) > 0 {
   796  			header := []string{}
   797  
   798  			for _, v := range corsConfig.ExposeHeaders {
   799  				if util.LenTrim(v) > 0 {
   800  					header = append(header, v)
   801  				}
   802  			}
   803  
   804  			if len(header) > 0 {
   805  				config.ExposeHeaders = header
   806  			}
   807  		}
   808  
   809  		if corsConfig.AllowOriginFunc != nil {
   810  			config.AllowOriginFunc = corsConfig.AllowOriginFunc
   811  		}
   812  
   813  		if corsConfig.MaxAge > 0 {
   814  			config.MaxAge = corsConfig.MaxAge
   815  		}
   816  
   817  		config.AllowAllOrigins = corsConfig.AllowAllOrigins
   818  		config.AllowBrowserExtensions = corsConfig.AllowBrowserExtensions
   819  		config.AllowCredentials = corsConfig.AllowCredentials
   820  		config.AllowFiles = corsConfig.AllowFiles
   821  		config.AllowWebSockets = corsConfig.AllowWebSockets
   822  		config.AllowWildcard = corsConfig.AllowWildcard
   823  
   824  		if !config.AllowAllOrigins {
   825  			if len(config.AllowOrigins) == 0 {
   826  				config.AllowAllOrigins = true
   827  			}
   828  		}
   829  
   830  		rg.Use(cors.New(config))
   831  
   832  		log.Println("Using Cors Middleware...")
   833  	}
   834  }
   835  
   836  // setupMaxLimitMiddleware sets up max concurrent handler execution rate limiter middleware
   837  func (g *Gin) setupMaxLimitMiddleware(rg gin.IRoutes, maxLimit int) {
   838  	if rg != nil && maxLimit > 0 {
   839  		rg.Use(func() gin.HandlerFunc {
   840  			s := make(chan struct{}, maxLimit)
   841  
   842  			acquire := func() {
   843  				s <- struct{}{}
   844  			}
   845  
   846  			release := func() {
   847  				<-s
   848  			}
   849  
   850  			return func(c *gin.Context) {
   851  				acquire()
   852  				defer release()
   853  				c.Next()
   854  			}
   855  		}())
   856  
   857  		log.Println("Using Max Limit Middleware...")
   858  	}
   859  }
   860  
   861  // setupPerClientIpQpsMiddleware sets up per client ip qps limiter middleware
   862  func (g *Gin) setupPerClientIpQpsMiddleware(rg gin.IRoutes, qps int, burst int, ttl time.Duration) {
   863  	if rg != nil && g._limiterCache != nil && qps > 0 && qps <= 1000000 && burst > 0 && ttl > 0 {
   864  		fn := func(key func(c *gin.Context) string,
   865  			createLimiter func(c *gin.Context) (*rate.Limiter, time.Duration),
   866  			abort func(c *gin.Context)) gin.HandlerFunc {
   867  			return func(cc *gin.Context) {
   868  				k := key(cc)
   869  				limiter, ok := g._limiterCache.Get(k)
   870  
   871  				if !ok {
   872  					var expire time.Duration
   873  					limiter, expire = createLimiter(cc)
   874  					g._limiterCache.Set(k, limiter, expire)
   875  				}
   876  
   877  				ok = limiter.(*rate.Limiter).Allow()
   878  
   879  				if !ok {
   880  					abort(cc)
   881  					return
   882  				}
   883  
   884  				cc.Next()
   885  			}
   886  		}
   887  
   888  		rg.Use(fn(func(c *gin.Context) string {
   889  			return c.ClientIP()
   890  		}, func(c *gin.Context) (*rate.Limiter, time.Duration) {
   891  			n := 1000000 / qps
   892  			return rate.NewLimiter(rate.Every(time.Duration(n)*time.Microsecond), burst), ttl
   893  		}, func(c *gin.Context) {
   894  			c.AbortWithStatus(429) // exceed rate limit request
   895  		})) // code based on github.com/yangxikun/gin-limit-by-key
   896  
   897  		log.Println("Using Per Client Ip Qps Middleware...")
   898  	}
   899  }
   900  
   901  // setupGZipMiddleware sets up GZip middleware
   902  func (g *Gin) setupGZipMiddleware(rg gin.IRoutes, gz *GZipConfig) {
   903  	if gz != nil && gz.Compression.Valid() && gz.Compression != gingzipcompression.UNKNOWN {
   904  		c := gz.GetGZipCompression()
   905  
   906  		exts := gz.GetGZipExcludedExtensions()
   907  		paths := gz.GetGZipExcludedPaths()
   908  		pregex := gz.GetGZipExcludedPathsRegex()
   909  
   910  		opts := []gzip.Option{}
   911  
   912  		if exts != nil {
   913  			opts = append(opts, exts)
   914  		}
   915  
   916  		if paths != nil {
   917  			opts = append(opts, paths)
   918  		}
   919  
   920  		if pregex != nil {
   921  			opts = append(opts, pregex)
   922  		}
   923  
   924  		if len(opts) > 0 {
   925  			rg.Use(gzip.Gzip(c, opts...))
   926  		} else {
   927  			rg.Use(gzip.Gzip(c))
   928  		}
   929  
   930  		log.Println("Using GZip Middleware...")
   931  	}
   932  }
   933  
   934  // setupSessionMiddleware sets up session middleware,
   935  // session is set up on the gin engine level (rather than route groups)
   936  func (g *Gin) setupSessionMiddleware() {
   937  	if g._ginEngine != nil && g.SessionMiddleware != nil && util.LenTrim(g.SessionMiddleware.SecretKey) > 0 && len(g.SessionMiddleware.SessionNames) > 0 {
   938  		var store sessions.Store
   939  
   940  		if util.LenTrim(g.SessionMiddleware.RedisHostAndPort) == 0 {
   941  			// cookie store
   942  			store = cookie.NewStore([]byte(g.SessionMiddleware.SecretKey))
   943  		} else {
   944  			// redis store
   945  			size := g.SessionMiddleware.RedisMaxIdleConnections
   946  
   947  			if size <= 0 {
   948  				size = 1
   949  			}
   950  
   951  			store, _ = redis.NewStore(size, "tcp", g.SessionMiddleware.RedisHostAndPort, "", []byte(g.SessionMiddleware.SecretKey))
   952  		}
   953  
   954  		if store != nil {
   955  			if len(g.SessionMiddleware.SessionNames) == 1 {
   956  				g._ginEngine.Use(sessions.Sessions(g.SessionMiddleware.SessionNames[0], store))
   957  			} else {
   958  				g._ginEngine.Use(sessions.SessionsMany(g.SessionMiddleware.SessionNames, store))
   959  			}
   960  
   961  			log.Println("Using Session Middleware...")
   962  		}
   963  	}
   964  }
   965  
   966  // setupCsrfMiddleware sets up csrf protection middleware,
   967  // this middleware is set up on the gin engine level (rather than route groups),
   968  // this middleware requires gin-contrib/sessions middleware setup and used before setting this up
   969  func (g *Gin) setupCsrfMiddleware() {
   970  	if g._ginEngine != nil && g.SessionMiddleware != nil && g.CsrfMiddleware != nil && util.LenTrim(g.SessionMiddleware.SecretKey) > 0 && len(g.SessionMiddleware.SessionNames) > 0 && util.LenTrim(g.CsrfMiddleware.Secret) > 0 {
   971  		opt := csrf.Options{
   972  			Secret: g.CsrfMiddleware.Secret,
   973  		}
   974  
   975  		if g.CsrfMiddleware.ErrorFunc != nil {
   976  			opt.ErrorFunc = g.CsrfMiddleware.ErrorFunc
   977  		} else {
   978  			opt.ErrorFunc = func(c *gin.Context) {
   979  				c.String(400, "CSRF Token Mismatch")
   980  				c.Abort()
   981  			}
   982  		}
   983  
   984  		if g.CsrfMiddleware.TokenGetter != nil {
   985  			opt.TokenGetter = g.CsrfMiddleware.TokenGetter
   986  		}
   987  
   988  		g._ginEngine.Use(csrf.Middleware(opt))
   989  		log.Println("Using Csrf Middleware...")
   990  	}
   991  }
   992  
   993  // setupHtmlTemplateRenderer sets up html template renderer with gin engine
   994  func (g *Gin) setupHtmlTemplateRenderer() {
   995  	if g.HtmlTemplateRenderer != nil {
   996  		if err := g.HtmlTemplateRenderer.LoadHtmlTemplates(); err != nil {
   997  			log.Println("Load Html Template Renderer Failed: " + err.Error())
   998  			return
   999  		}
  1000  
  1001  		if err := g.HtmlTemplateRenderer.SetHtmlRenderer(g); err != nil {
  1002  			log.Println("Set Html Template Renderer Failed: " + err.Error())
  1003  			return
  1004  		}
  1005  
  1006  		log.Println("Html Template Renderer Set...")
  1007  	}
  1008  }
  1009  
  1010  /*
  1011  
  1012  // method_descriptions_only lists most of gin's context methods, its method signature, and method descriptions,
  1013  // for simpler reference in one place rather having to refer to documentation separately
  1014  func method_descriptions_only() {
  1015  	c := gin.Context{}
  1016  
  1017  	// -----------------------------------------------------------------------------------------------------------------
  1018  	// http handler return value methods
  1019  	// -----------------------------------------------------------------------------------------------------------------
  1020  
  1021  	// String writes the given string into the response body.
  1022  	c.String(code int, format string, values ...interface{})
  1023  
  1024  	// HTML renders the HTTP template specified by its file name.
  1025  	// It also updates the HTTP code and sets the Content-Type as "text/html".
  1026  	// See http://golang.org/doc/articles/wiki/
  1027  	c.HTML(code int, name string, obj interface{})
  1028  
  1029  	// JSON serializes the given struct as JSON into the response body.
  1030  	// It also sets the Content-Type as "application/json".
  1031  	c.JSON(code int, obj interface{})
  1032  
  1033  	// JSONP serializes the given struct as JSON into the response body.
  1034  	// It add padding to response body to request data from a server residing in a different domain than the client.
  1035  	// It also sets the Content-Type as "application/javascript".
  1036  	c.JSONP(code int, obj interface{})
  1037  
  1038  	// PureJSON serializes the given struct as JSON into the response body.
  1039  	// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
  1040  	c.PureJSON(code int, obj interface{})
  1041  
  1042  	// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
  1043  	// It also sets the Content-Type as "application/json".
  1044  	c.AsciiJSON(code int, obj interface{})
  1045  
  1046  	// SecureJSON serializes the given struct as Secure JSON into the response body.
  1047  	// Default prepends "while(1)," to response body if the given struct is array values.
  1048  	// It also sets the Content-Type as "application/json".
  1049  	c.SecureJSON(code int, obj interface{})
  1050  
  1051  	// XML serializes the given struct as XML into the response body.
  1052  	// It also sets the Content-Type as "application/xml".
  1053  	c.XML(code int, obj interface{})
  1054  
  1055  	// YAML serializes the given struct as YAML into the response body.
  1056  	c.YAML(code int, obj interface{})
  1057  
  1058  	// ProtoBuf serializes the given struct as ProtoBuf into the response body.
  1059  	c.ProtoBuf(code int, obj interface{})
  1060  
  1061  	// Redirect returns a HTTP redirect to the specific location.
  1062  	c.Redirect(code int, location string)
  1063  
  1064  	// Data writes some data into the body stream and updates the HTTP code.
  1065  	c.Data(code int, contentType string, data []byte)
  1066  
  1067  	// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
  1068  	c.DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)
  1069  
  1070  	// Render writes the response headers and calls render.Render to render data.
  1071  	c.Render(code int, r render.Render)
  1072  
  1073  	// -----------------------------------------------------------------------------------------------------------------
  1074  	// request's query-parameter key-value methods
  1075  	// -----------------------------------------------------------------------------------------------------------------
  1076  
  1077  	// DefaultQuery returns the keyed url query value if it exists,
  1078  	// otherwise it returns the specified defaultValue string.
  1079  	// See: Query() and GetQuery() for further information.
  1080  	//    GET /?name=Manu&lastname=
  1081  	//    c.DefaultQuery("name", "unknown") == "Manu"
  1082  	//    c.DefaultQuery("id", "none") == "none"
  1083  	//    c.DefaultQuery("lastname", "none") == ""
  1084  	c.DefaultQuery(key string, defaultValue string) string
  1085  
  1086  	// Query returns the keyed url query value if it exists,
  1087  	// otherwise it returns an empty string `("")`.
  1088  	// It is shortcut for `c.Request.URL.Query().Get(key)`
  1089  	//    GET /path?id=1234&name=Manu&value=
  1090  	//    c.Query("id") == "1234"
  1091  	//    c.Query("name") == "Manu"
  1092  	//    c.Query("value") == ""
  1093  	//    c.Query("wtf") == ""
  1094  	c.Query(key string) string
  1095  
  1096  	// QueryArray returns a slice of strings for a given query key.
  1097  	// The length of the slice depends on the number of params with the given key.
  1098  	c.QueryArray(key string) []string
  1099  
  1100  	// QueryMap returns a map for a given query key.
  1101  	c.QueryMap(key string) map[string]string
  1102  
  1103  	// GetQuery is like Query(),
  1104  	// it returns the keyed url query value if it exists `(value, true)` (even when the value is an empty string),
  1105  	// otherwise it returns `("", false)`. It is shortcut for `c.Request.URL.Query().Get(key)`
  1106  	//    GET /?name=Manu&lastname=
  1107  	//    ("Manu", true) == c.GetQuery("name")
  1108  	//    ("", false) == c.GetQuery("id")
  1109  	//    ("", true) == c.GetQuery("lastname")
  1110  	c.GetQuery(key string) (string, bool)
  1111  
  1112  	// GetQueryArray returns a slice of strings for a given query key,
  1113  	// plus a boolean value whether at least one value exists for the given key.
  1114  	c.GetQueryArray(key string) ([]string, bool)
  1115  
  1116  	// GetQueryMap returns a map for a given query key,
  1117  	// plus a boolean value whether at least one value exists for the given key.
  1118  	c.GetQueryMap(key string) (map[string]string, bool)
  1119  
  1120  	// -----------------------------------------------------------------------------------------------------------------
  1121  	// request's form-post key-value methods
  1122  	// -----------------------------------------------------------------------------------------------------------------
  1123  
  1124  	// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form when it exists,
  1125  	// otherwise it returns the specified defaultValue string.
  1126  	// See: PostForm() and GetPostForm() for further information.
  1127  	c.DefaultPostForm(key string, defaultValue string) string
  1128  
  1129  	// PostForm returns the specified key from a POST urlencoded form or multipart form when it exists,
  1130  	// otherwise it returns an empty string `("")`.
  1131  	c.PostForm(key string) string
  1132  
  1133  	// PostFormArray returns a slice of strings for a given form key.
  1134  	// The length of the slice depends on the number of params with the given key.
  1135  	c.PostFormArray(key string) []string
  1136  
  1137  	// PostFormMap returns a map for a given form key.
  1138  	c.PostFormMap(key string) map[string]string
  1139  
  1140  	// GetPostForm is like PostForm(key).
  1141  	// It returns the specified key from a POST urlencoded form or multipart form when it exists `(value, true)` (even when the value is an empty string),
  1142  	// otherwise it returns ("", false). For example, during a PATCH request to update the user's email:
  1143  	//    email=mail@example.com  -->  ("mail@example.com", true) := GetPostForm("email") 	// set email to "mail@example.com"
  1144  	//    email=                  -->  ("", true) := GetPostForm("email") 					// set email to ""
  1145  	//                            -->  ("", false) := GetPostForm("email") 					// do nothing with email
  1146  	c.GetPostForm(key string) (string, bool)
  1147  
  1148  	// GetPostFormArray returns a slice of strings for a given form key,
  1149  	// plus a boolean value whether at least one value exists for the given key.
  1150  	c.GetPostFormArray(key string) ([]string, bool)
  1151  
  1152  	// GetPostFormMap returns a map for a given form key,
  1153  	// plus a boolean value whether at least one value exists for the given key.
  1154  	c.GetPostFormMap(key string) (map[string]string, bool)
  1155  
  1156  	// MultipartForm is the parsed multipart form, including file uploads.
  1157  	c.MultipartForm() (*multipart.Form, error)
  1158  
  1159  	// FormFile returns the first file for the provided form key.
  1160  	c.FormFile(name string) (*multipart.FileHeader, error)
  1161  
  1162  	// -----------------------------------------------------------------------------------------------------------------
  1163  	// request abort methods
  1164  	// -----------------------------------------------------------------------------------------------------------------
  1165  
  1166  	// Abort prevents pending handlers from being called.
  1167  	// Note that this will not stop the current handler.
  1168  	// Let's say you have an authorization middleware that validates that the current request is authorized.
  1169  	// If the authorization fails (ex: the password does not match),
  1170  	// call Abort to ensure the remaining handlers for this request are not called.
  1171  	c.Abort()
  1172  
  1173  	// AbortWithError calls `AbortWithStatus()` and `Error()` internally.
  1174  	// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`.
  1175  	// See Context.Error() for more details.
  1176  	c.AbortWithError(code int, err error) *Error
  1177  
  1178  	// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
  1179  	// For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).
  1180  	c.AbortWithStatus(code int)
  1181  
  1182  	// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
  1183  	// This method stops the chain, writes the status code and return a JSON body.
  1184  	// It also sets the Content-Type as "application/json".
  1185  	c.AbortWithStatusJSON(code int, jsonObj interface{})
  1186  
  1187  	// -----------------------------------------------------------------------------------------------------------------
  1188  	// request and response related objects
  1189  	// -----------------------------------------------------------------------------------------------------------------
  1190  
  1191  	// access to the underlying http request object
  1192  	c.Request *http.Request
  1193  
  1194  	// access to the underlying http response object
  1195  	c.Writer ResponseWriter
  1196  
  1197  	// access to the context keys object
  1198  	c.Keys map[string]interface{}
  1199  
  1200  	// access to the context params object
  1201  	c.Params Params
  1202  
  1203  	// -----------------------------------------------------------------------------------------------------------------
  1204  	// request and response helper methods
  1205  	// -----------------------------------------------------------------------------------------------------------------
  1206  
  1207  	// ClientIP implements a best effort algorithm to return the real client IP,
  1208  	// it parses X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy.
  1209  	// Use X-Forwarded-For before X-Real-Ip as nginx uses X-Real-Ip with the proxy's IP.
  1210  	c.ClientIP() string
  1211  
  1212  	// ContentType returns the Content-Type header of the request.
  1213  	c.ContentType() string
  1214  
  1215  	// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
  1216  	// It writes a header in the response.
  1217  	// If value == "", this method removes the header `c.Writer.Header().Del(key)`
  1218  	c.Header(key string, value string)
  1219  
  1220  	// Cookie returns the named cookie provided in the request or ErrNoCookie if not found.
  1221  	// And return the named cookie is unescaped.
  1222  	// If multiple cookies match the given name, only one cookie will be returned.
  1223  	c.Cookie(name string) (string, error)
  1224  
  1225  	// FullPath returns a matched route full path. For not found routes returns an empty string.
  1226  	//    router.GET("/user/:id", func(c *gin.Context) {
  1227  	//        c.FullPath() == "/user/:id" // true
  1228  	//    })
  1229  	c.FullPath() string
  1230  
  1231  	// SaveUploadedFile uploads the form file to specific dst.
  1232  	c.SaveUploadedFile(file *multipart.FileHeader, dst string) error
  1233  
  1234  	// Status sets the HTTP response code.
  1235  	c.Status(code int)
  1236  
  1237  	// Value returns the value associated with this context for key,
  1238  	// or nil if no value is associated with key.
  1239  	// Successive calls to Value with the same key returns the same result.
  1240  	c.Value(key interface{}) interface{}
  1241  
  1242  	// Param returns the value of the URL param.
  1243  	// It is a shortcut for c.Params.ByName(key)
  1244  	//    router.GET("/user/:id", func(c *gin.Context) {
  1245  	//        // a GET request to /user/john
  1246  	//        id := c.Param("id") // id == "john"
  1247  	//    })
  1248  	c.Param(key string) string
  1249  
  1250  	// File writes the specified file into the body stream in a efficient way.
  1251  	c.File(filepath string)
  1252  
  1253  	// FileAttachment writes the specified file into the body stream in an efficient way On the client side,
  1254  	// the file will typically be downloaded with the given filename
  1255  	c.FileAttachment(filepath string, filename string)
  1256  
  1257  	// FileFromFS writes the specified file from http.FileSytem into the body stream in an efficient way.
  1258  	c.FileFromFS(filepath string, fs http.FileSystem)
  1259  
  1260  	// Error attaches an error to the current context.
  1261  	// The error is pushed to a list of errors.
  1262  	// It's a good idea to call Error for each error that occurred during the resolution of a request.
  1263  	//
  1264  	// A middleware can be used to collect all the errors and push them to a database together,
  1265  	// print a log, or append it in the HTTP response.
  1266  	// Error will panic if err is nil.
  1267  	c.Error(err error) *Error
  1268  
  1269  	// -----------------------------------------------------------------------------------------------------------------
  1270  	// set helpers
  1271  	// -----------------------------------------------------------------------------------------------------------------
  1272  
  1273  	// Set is used to store a new key/value pair exclusively for this context.
  1274  	// It also lazy initializes c.Keys if it was not used previously.
  1275  	// c.Set(key string, value interface{})
  1276  
  1277  	// SetAccepted sets Accept header data.
  1278  	c.SetAccepted(formats ...string)
  1279  
  1280  	// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
  1281  	// The provided cookie must have a valid Name. Invalid cookies may be silently dropped.
  1282  	c.SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)
  1283  
  1284  	// SetSameSite with cookie
  1285  	c.SetSameSite(samesite http.SameSite)
  1286  
  1287  	// -----------------------------------------------------------------------------------------------------------------
  1288  	// get helpers
  1289  	// -----------------------------------------------------------------------------------------------------------------
  1290  
  1291  	// Get returns the value for the given key, ie: (value, true).
  1292  	// If the value does not exists it returns (nil, false)
  1293  	c.Get(key string) (value interface{}, exists bool)
  1294  
  1295  	// GetHeader returns value from request headers.
  1296  	c.GetHeader(key string) string
  1297  
  1298  	// GetInt returns the value associated with the key as an integer.
  1299  	c.GetInt(key string) (i int)
  1300  
  1301  	// GetInt64 returns the value associated with the key as an integer.
  1302  	c.GetInt64(key string) (i64 int64)
  1303  
  1304  	// GetFloat64 returns the value associated with the key as a float64
  1305  	c.GetFloat64(key string) (f64 float64)
  1306  
  1307  	// GetTime returns the value associated with the key as time.
  1308  	c.GetTime(key string) (t time.Time)
  1309  
  1310  	// GetDuration returns the value associated with the key as a duration.
  1311  	c.GetDuration(key string) (d time.Duration)
  1312  
  1313  	// GetBool returns the value associated with the key as a boolean.
  1314  	c.GetBool(key string) (b bool)
  1315  
  1316  	// GetString returns the value associated with the key as a string.
  1317  	c.GetString(key string) (s string)
  1318  
  1319  	// GetStringMap returns the value associated with the key as a map of interfaces.
  1320  	c.GetStringMap(key string) (sm map[string]interface{})
  1321  
  1322  	// GetStringMapString returns the value associated with the key as a map of strings.
  1323  	c.GetStringMapString(key string) (sms map[string]string)
  1324  
  1325  	// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
  1326  	c.GetStringMapStringSlice(key string) (smss map[string][]string)
  1327  
  1328  	// -----------------------------------------------------------------------------------------------------------------
  1329  	// stream helpers
  1330  	// -----------------------------------------------------------------------------------------------------------------
  1331  
  1332  	// Stream sends a streaming response and returns a boolean indicates "Is client disconnected in middle of stream"
  1333  	c.Stream(step func(w io.Writer) bool) bool
  1334  
  1335  	// GetRawData return stream data.
  1336  	c.GetRawData() ([]byte, error)
  1337  
  1338  	// SSEvent writes a Server-Sent Event into the body stream.
  1339  	c.SSEvent(name string, message interface{})
  1340  
  1341  	// -----------------------------------------------------------------------------------------------------------------
  1342  	// other helpers
  1343  	// -----------------------------------------------------------------------------------------------------------------
  1344  
  1345  	// Copy returns a copy of the current context that can be safely used outside the request's scope.
  1346  	// This has to be used when the context has to be passed to a goroutine.
  1347  	c.Copy() *Context
  1348  
  1349  	// IsAborted returns true if the current context was aborted.
  1350  	c.IsAborted() bool
  1351  
  1352  	// IsWebsocket returns true if the request headers
  1353  	// indicate that a websocket handshake is being initiated by the client.
  1354  	c.IsWebsocket() bool
  1355  
  1356  	// Negotiate calls different Render according acceptable Accept format.
  1357  	c.Negotiate(code int, config Negotiate)
  1358  
  1359  	// NegotiateFormat returns an acceptable Accept format.
  1360  	c.NegotiateFormat(offered ...string) string
  1361  }
  1362  
  1363  */
  1364  
  1365  /*
  1366  	Additional Gin Middleware That Can Be Added via CustomMiddleware Slice:
  1367  
  1368  	*) Request Response Interceptor / Tracer:
  1369  			https://github.com/averageflow/goscope
  1370  			https://github.com/tpkeeper/gin-dump
  1371  			https://github.com/gin-contrib/opengintracing
  1372  	*) Prometheus Export:
  1373  			https://github.com/zsais/go-gin-prometheus
  1374  			https://github.com/chenjiandongx/ginprom
  1375  	*) OAuth2:
  1376  			https://github.com/zalando/gin-oauth2
  1377  	*) Static Bin
  1378  			https://github.com/olebedev/staticbin
  1379  			https://github.com/gin-contrib/static
  1380  	*) Server Send Event (SSE):
  1381  			https://github.com/gin-contrib/sse
  1382  */