func (m *middleware) CheckCORS() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckCORS")
		if ctx.Request.Method != "OPTIONS" || ctx.Request.Header.Get("Origin") == "" {
			ctx.Next()
			return
		}
		if err := m.corser.ValidateHeader(ctx); err != nil {
			m.logger.Error("CheckCORS", zap.Error(err))
			ctx.AbortWithStatus(http.StatusBadRequest)
			return
		}
	}
}
CheckCORS checks CORS
package server
import (
	"fmt"
	"net/http"
	"strings"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"github.com/hiromaily/go-gin-wrapper/pkg/auth/jwts"
	"github.com/hiromaily/go-gin-wrapper/pkg/config"
	"github.com/hiromaily/go-gin-wrapper/pkg/files"
	"github.com/hiromaily/go-gin-wrapper/pkg/reverseproxy/types"
	"github.com/hiromaily/go-gin-wrapper/pkg/server/ginctx"
	"github.com/hiromaily/go-gin-wrapper/pkg/server/ginsession"
	"github.com/hiromaily/go-gin-wrapper/pkg/server/ginurl"
	hh "github.com/hiromaily/go-gin-wrapper/pkg/server/httpheader"
	"github.com/hiromaily/go-gin-wrapper/pkg/server/httpheader/cors"
	str "github.com/hiromaily/go-gin-wrapper/pkg/strings"
)
type Middlewarer interface {
	GlobalRecover() gin.HandlerFunc
	FilterIP() gin.HandlerFunc
	CheckGinError() gin.HandlerFunc
	SetMetaData() gin.HandlerFunc
	UpdateUserSession() gin.HandlerFunc
	CheckHTTPReferer() gin.HandlerFunc
	CheckCSRF() gin.HandlerFunc
	RejectNonHTTPS() gin.HandlerFunc
	CheckHTTPHeader() gin.HandlerFunc
	CheckJWT() gin.HandlerFunc
	CheckCORS() gin.HandlerFunc
	SetResponseHeader() gin.HandlerFunc
	SetCORSHeader() gin.HandlerFunc
}
type middleware struct {
	logger      *zap.Logger
	session     ginsession.Sessioner
	jwter       jwts.JWTer
	corser      cors.CORSer
	rejectIPs   []string
	serverConf  *config.Server
	proxyConf   *config.Proxy
	apiConf     *config.API
	developConf *config.Develop
}
func NewMiddleware(
	logger *zap.Logger,
	session ginsession.Sessioner,
	jwter jwts.JWTer,
	corser cors.CORSer,
	rejectIPs []string,
	serverConf *config.Server,
	proxyConf *config.Proxy,
	apiConf *config.API,
	developConf *config.Develop,
) Middlewarer {
	return &middleware{
		logger:      logger,
		session:     session,
		jwter:       jwter,
		corser:      corser,
		rejectIPs:   rejectIPs,
		serverConf:  serverConf,
		proxyConf:   proxyConf,
		apiConf:     apiConf,
		developConf: developConf,
	}
}
var RefererURLs = map[string]string{
	"/login": "login",
	"/user":  "user",
}
func (m *middleware) GlobalRecover() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		defer func(ctx *gin.Context) {
			if !preCheck(ctx) {
				return
			}
			m.logger.Info("middleware GlobalRecover()")
			if ctx.IsAborted() {
				m.logger.Debug("GlobalRecover", zap.Bool("c.IsAborted()", ctx.IsAborted()))
				m.setResponse(ctx, getErrMsg(ctx), ctx.Writer.Status())
				return
			}
			if m.developConf.RecoverEnable {
				if rec := recover(); rec != nil {
					m.logger.Debug("GlobalRecover", zap.Any("recover()", rec))
					m.setResponse(ctx, str.Itos(rec), http.StatusInternalServerError)
					return
				}
			}
		}(ctx)
		ctx.Next()
	}
}
func (m *middleware) setResponse(ctx *gin.Context, errMsg string, code int) {
	referer := ctx.Request.Header.Get("Referer")
	if referer == "" {
		referer = "/"
	}
	if m.isAcceptHeaderJSON(ctx) {
		ctx.JSON(ctx.Writer.Status(), gin.H{
			"code":  fmt.Sprintf("%d", code),
			"error": errMsg,
		})
		return
	}
	ctx.HTML(ctx.Writer.Status(), "pages/errors/error.tmpl", gin.H{
		"code":    fmt.Sprintf("%d", code),
		"message": errMsg,
		"url":     referer,
	})
}
func getErrMsg(ctx *gin.Context) string {
	if ctx.Errors != nil && ctx.Errors.Last() != nil {
		return ctx.Errors.Last().Err.Error()
	}
	switch ctx.Writer.Status() {
	case 400:
		return http.StatusText(http.StatusBadRequest)
	case 401:
		return http.StatusText(http.StatusUnauthorized)
	case 403:
		return http.StatusText(http.StatusForbidden)
	case 404:
		return http.StatusText(http.StatusNotFound)
	case 405:
		return http.StatusText(http.StatusMethodNotAllowed)
	case 406:
		return http.StatusText(http.StatusNotAcceptable)
	case 407:
		return http.StatusText(http.StatusProxyAuthRequired)
	case 408:
		return http.StatusText(http.StatusRequestTimeout)
	case 500:
		return http.StatusText(http.StatusInternalServerError)
	}
	return fmt.Sprintf("unexpected error: http status: %d", ctx.Writer.Status())
}
func (m *middleware) FilterIP() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware FilterIP")
		ip := ctx.ClientIP()
		if m.proxyConf.Mode != types.NoProxy && m.proxyConf.Server.Host != ip {
			ctx.AbortWithStatus(http.StatusForbidden)
			return
		}
		for _, rejectIP := range m.rejectIPs {
			if ip == rejectIP {
				ctx.AbortWithStatus(http.StatusForbidden)
				return
			}
		}
		ctx.Next()
	}
}
func (m *middleware) CheckGinError() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckGinError",
			zap.Any("errors", ctx.Errors),
			zap.Error(ctx.Err()),
		)
		ctx.Next()
	}
}
func (m *middleware) SetMetaData() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware SetMetaData")
		if m.isXHR(ctx) {
			ctx.Set("ajax", "1")
		} else {
			ctx.Set("ajax", "0")
		}
		if m.isAcceptHeaderJSON(ctx) {
			ctx.Set("responseData", "json")
		} else {
			ctx.Set("responseData", "html")
		}
		if m.isContentTypeJSON(ctx) {
			ctx.Set("requestData", "json")
		} else {
			ctx.Set("requestData", "data")
		}
		ctx.Set("userAgent", m.getUserAgent(ctx))
		ctx.Set("language", m.getLanguage(ctx))
		ctx.Next()
	}
}
func (m *middleware) UpdateUserSession() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware UpdateUserSession")
		if logined, uid := m.session.IsLogin(ctx); logined {
			m.session.SetUserID(ctx, uid)
		}
		ctx.Next()
	}
}
func (m *middleware) CheckHTTPReferer() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckHTTPReferer")
		targetURL, found := RefererURLs[ginurl.GetURLString(ctx)]
		if !found {
			return
		}
		if err := m.validateReferer(ctx, targetURL); err != nil {
			m.logger.Error("fail to call validateReferer()", zap.Error(err))
			m.session.DeleteToken(ctx)
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}
		ctx.Next()
	}
}
func (m *middleware) validateReferer(ctx *gin.Context, pageFrom string) error {
	webserverURL := getServerURL(m.serverConf.Scheme, m.serverConf.Host, m.serverConf.Port)
	referer := fmt.Sprintf("%s/%s", webserverURL, pageFrom)
	m.logger.Debug("validateReferer",
		zap.String("expected_referer", referer),
		zap.String("ctx_referer", ctx.Request.Header.Get("Referer")),
	)
	if referer != ctx.Request.Referer() {
		return errors.Errorf("Referer is invalid: actual: %s, want: %s", ctx.Request.Referer(), referer)
	}
	return nil
}
func (m *middleware) CheckCSRF() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckCSRF", zap.String("gintoken", ctx.PostForm("gintoken")))
		ginctx.DebugContext(ctx, m.logger)
		if err := m.session.ValidateToken(ctx, ctx.PostForm("gintoken")); err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}
		ctx.Next()
	}
}
func (m *middleware) RejectNonHTTPS() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware RejectNonHTTPS")
		if !strings.Contains(ctx.Request.URL.Scheme, "https://") {
			ctx.AbortWithStatus(http.StatusForbidden)
			return
		}
		ctx.Next()
	}
}
func (m *middleware) CheckHTTPHeader() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckHttpHeader",
			zap.Any("request_header", ctx.Request.Header),
			zap.Any("request_form", ctx.Request.Form),
			zap.Any("request_body", ctx.Request.Body),
			zap.String("client_ip", ctx.ClientIP()),
			zap.String("request_method", ctx.Request.Method),
			zap.String("http_header_X-Custom-Header-Gin", ctx.Request.Header.Get("X-Custom-Header-Gin")),
		)
		if m.apiConf.Header.Enabled {
			if ctx.Request.Header.Get(m.apiConf.Header.Header) != m.apiConf.Header.Key {
				m.logger.Error("header and key are invalid")
				ctx.AbortWithStatus(http.StatusBadRequest)
				return
			}
		}
		if ctx.Request.Method == "OPTIONS" {
			ctx.Next()
			return
		}
		m.logger.Debug("debug",
			zap.Bool("m.apiConf.Ajax", m.apiConf.Ajax),
			zap.Bool("!m.isXHR(ctx)", m.isXHR(ctx)),
		)
		if m.apiConf.Ajax && !m.isXHR(ctx) {
			m.logger.Error("Ajax request is required")
			ctx.AbortWithStatus(http.StatusBadRequest)
			return
		}
		ctx.Next()
	}
}
func (m *middleware) CheckJWT() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if !preCheck(ctx) {
			return
		}
		m.logger.Info("middleware CheckJWT")
		auth := ctx.Request.Header.Get("Authorization")
		if auth == "" {
			ctx.AbortWithError(http.StatusBadRequest, errors.New("authorization header is missing"))
			return
		}
		authParts := strings.Split(auth, " ")
		if len(authParts) != 2 {
			ctx.AbortWithError(http.StatusBadRequest, errors.New("Authorization header is invalid"))
			return
		}
		if authParts[0] != "Bearer" {
			ctx.AbortWithError(http.StatusBadRequest, errors.New("Authorization header is invalid"))
			return
		}
		if err := m.jwter.ValidateToken(authParts[1]); err != nil {
			ctx.AbortWithError(http.StatusBadRequest, err)
			return
		}
		ctx.Next()
	}
}