diff --git a/config/components/head/config.go b/config/components/head/config.go index c5346c8b..f8da99d9 100644 --- a/config/components/head/config.go +++ b/config/components/head/config.go @@ -27,7 +27,7 @@ package head import ( - librtr "github.com/nabbar/golib/router" + librtr "github.com/nabbar/golib/router/header" spfcbr "github.com/spf13/cobra" spfvpr "github.com/spf13/viper" ) diff --git a/config/components/head/interface.go b/config/components/head/interface.go index 25022f64..9a32fc3a 100644 --- a/config/components/head/interface.go +++ b/config/components/head/interface.go @@ -29,9 +29,10 @@ package head import ( "sync" + librtr "github.com/nabbar/golib/router/header" + libcfg "github.com/nabbar/golib/config" cfgtps "github.com/nabbar/golib/config/types" - librtr "github.com/nabbar/golib/router" ) type ComponentHead interface { diff --git a/config/components/head/model.go b/config/components/head/model.go index a752f0f5..024c4ae0 100644 --- a/config/components/head/model.go +++ b/config/components/head/model.go @@ -29,8 +29,9 @@ package head import ( "sync" + librtr "github.com/nabbar/golib/router/header" + libctx "github.com/nabbar/golib/context" - librtr "github.com/nabbar/golib/router" ) type componentHead struct { diff --git a/prometheus/interface.go b/prometheus/interface.go index 05cff102..96abee83 100644 --- a/prometheus/interface.go +++ b/prometheus/interface.go @@ -31,7 +31,6 @@ import ( "sync" ginsdk "github.com/gin-gonic/gin" - libctx "github.com/nabbar/golib/context" libmet "github.com/nabbar/golib/prometheus/metrics" prmpol "github.com/nabbar/golib/prometheus/pool" diff --git a/router/auth/interface.go b/router/auth/interface.go new file mode 100644 index 00000000..962657be --- /dev/null +++ b/router/auth/interface.go @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package auth + +import ( + ginsdk "github.com/gin-gonic/gin" + liberr "github.com/nabbar/golib/errors" + liblog "github.com/nabbar/golib/logger" + rtrhdr "github.com/nabbar/golib/router/authheader" +) + +type Authorization interface { + Handler(c *ginsdk.Context) + Register(router ...ginsdk.HandlerFunc) ginsdk.HandlerFunc + Append(router ...ginsdk.HandlerFunc) +} + +func NewAuthorization(log liblog.FuncLog, HeadAuthType string, authCheckFunc func(AuthHeader string) (rtrhdr.AuthCode, liberr.Error)) Authorization { + return &authorization{ + log: log, + check: authCheckFunc, + authType: HeadAuthType, + router: make([]ginsdk.HandlerFunc, 0), + } +} diff --git a/router/auth.go b/router/auth/model.go similarity index 57% rename from router/auth.go rename to router/auth/model.go index 95859d63..1debef6a 100644 --- a/router/auth.go +++ b/router/auth/model.go @@ -23,78 +23,28 @@ * */ -package router +package auth import ( "fmt" "net/http" "strings" - liblog "github.com/nabbar/golib/logger" - ginsdk "github.com/gin-gonic/gin" liberr "github.com/nabbar/golib/errors" + liblog "github.com/nabbar/golib/logger" loglvl "github.com/nabbar/golib/logger/level" + librtr "github.com/nabbar/golib/router" + rtrhdr "github.com/nabbar/golib/router/authheader" ) -type AuthCode uint8 - -const ( - AUTH_CODE_SUCCESS = iota - AUTH_CODE_REQUIRE - AUTH_CODE_FORBIDDEN -) - -const ( - HEAD_AUTH_REQR = "WWW-Authenticate" - HEAD_AUTH_SEND = "Authorization" - HEAD_AUTH_REAL = "Basic realm=LDAP Authorization Required" -) - -func AuthRequire(c *ginsdk.Context, err error) { - if err != nil { - c.Errors = append(c.Errors, &ginsdk.Error{ - Err: err, - Type: ginsdk.ErrorTypePrivate, - }) - } - // Credentials doesn't match, we return 401 and abort handlers chain. - c.Header(HEAD_AUTH_REQR, HEAD_AUTH_REAL) - c.AbortWithStatus(http.StatusUnauthorized) -} - -func AuthForbidden(c *ginsdk.Context, err error) { - if err != nil { - c.Errors = append(c.Errors, &ginsdk.Error{ - Err: err, - Type: ginsdk.ErrorTypePrivate, - }) - } - c.AbortWithStatus(http.StatusForbidden) -} - type authorization struct { log liblog.FuncLog - check func(AuthHeader string) (AuthCode, liberr.Error) + check func(AuthHeader string) (rtrhdr.AuthCode, liberr.Error) router []ginsdk.HandlerFunc authType string } -type Authorization interface { - Handler(c *ginsdk.Context) - Register(router ...ginsdk.HandlerFunc) ginsdk.HandlerFunc - Append(router ...ginsdk.HandlerFunc) -} - -func NewAuthorization(log liblog.FuncLog, HeadAuthType string, authCheckFunc func(AuthHeader string) (AuthCode, liberr.Error)) Authorization { - return &authorization{ - log: log, - check: authCheckFunc, - authType: HeadAuthType, - router: make([]ginsdk.HandlerFunc, 0), - } -} - func (a *authorization) Register(router ...ginsdk.HandlerFunc) ginsdk.HandlerFunc { a.router = router return a.Handler @@ -112,10 +62,10 @@ func (a *authorization) logDebug(msg string, args ...interface{}) { func (a *authorization) Handler(c *ginsdk.Context) { // Search user in the slice of allowed credentials - auth := c.Request.Header.Get(HEAD_AUTH_SEND) + auth := c.Request.Header.Get(rtrhdr.HeaderAuthSend) if auth == "" { - AuthRequire(c, fmt.Errorf("%v", ErrorHeaderAuthMissing.Error(nil).GetErrorSlice())) + rtrhdr.AuthRequire(c, fmt.Errorf("%v", librtr.ErrorHeaderAuthMissing.Error(nil).GetErrorSlice())) return } @@ -129,24 +79,24 @@ func (a *authorization) Handler(c *ginsdk.Context) { } if authValue == "" { - AuthRequire(c, fmt.Errorf("%v", ErrorHeaderAuthEmpty.Error(nil).GetErrorSlice())) + rtrhdr.AuthRequire(c, fmt.Errorf("%v", librtr.ErrorHeaderAuthEmpty.Error(nil).GetErrorSlice())) return } else { code, err := a.check(authValue) switch code { - case AUTH_CODE_SUCCESS: + case rtrhdr.AuthCodeSuccess: for _, r := range a.router { a.logDebug("Calling router '%s=%s'", c.Request.Method, c.Request.URL.RawPath) r(c) } - case AUTH_CODE_REQUIRE: - AuthRequire(c, fmt.Errorf("%v", ErrorHeaderAuthRequire.Error(err).GetErrorSlice())) - case AUTH_CODE_FORBIDDEN: - AuthForbidden(c, fmt.Errorf("%v", ErrorHeaderAuthForbidden.Error(err).GetErrorSlice())) + case rtrhdr.AuthCodeRequire: + rtrhdr.AuthRequire(c, fmt.Errorf("%v", librtr.ErrorHeaderAuthRequire.Error(err).GetErrorSlice())) + case rtrhdr.AuthCodeForbidden: + rtrhdr.AuthForbidden(c, fmt.Errorf("%v", librtr.ErrorHeaderAuthForbidden.Error(err).GetErrorSlice())) default: c.Errors = append(c.Errors, &ginsdk.Error{ - Err: fmt.Errorf("%v", ErrorHeaderAuth.Error(err).GetErrorSlice()), + Err: fmt.Errorf("%v", librtr.ErrorHeaderAuth.Error(err).GetErrorSlice()), Type: ginsdk.ErrorTypePrivate, }) c.AbortWithStatus(http.StatusInternalServerError) diff --git a/router/authheader/interface.go b/router/authheader/interface.go new file mode 100644 index 00000000..3221b040 --- /dev/null +++ b/router/authheader/interface.go @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package authheader + +import ( + "net/http" + + ginsdk "github.com/gin-gonic/gin" +) + +type AuthCode uint8 + +const ( + AuthCodeSuccess = iota + AuthCodeRequire + AuthCodeForbidden +) + +const ( + HeaderAuthRequire = "WWW-Authenticate" + HeaderAuthSend = "Authorization" + HeaderAuthReal = "Basic realm=LDAP Authorization Required" +) + +func AuthRequire(c *ginsdk.Context, err error) { + if err != nil { + c.Errors = append(c.Errors, &ginsdk.Error{ + Err: err, + Type: ginsdk.ErrorTypePrivate, + }) + } + // Credentials doesn't match, we return 401 and abort handlers chain. + c.Header(HeaderAuthRequire, HeaderAuthReal) + c.AbortWithStatus(http.StatusUnauthorized) +} + +func AuthForbidden(c *ginsdk.Context, err error) { + if err != nil { + c.Errors = append(c.Errors, &ginsdk.Error{ + Err: err, + Type: ginsdk.ErrorTypePrivate, + }) + } + c.AbortWithStatus(http.StatusForbidden) +} diff --git a/router/default.go b/router/default.go new file mode 100644 index 00000000..2e04f1dc --- /dev/null +++ b/router/default.go @@ -0,0 +1,109 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package router + +import ( + "net/http" + + ginsdk "github.com/gin-gonic/gin" +) + +func DefaultGinInit() *ginsdk.Engine { + engine := ginsdk.New() + engine.Use(ginsdk.Logger(), ginsdk.Recovery()) + + return engine +} + +func DefaultGinWithTrustyProxy(trustyProxy []string) *ginsdk.Engine { + engine := ginsdk.New() + engine.Use(ginsdk.Logger(), ginsdk.Recovery()) + + if len(trustyProxy) > 0 { + _ = engine.SetTrustedProxies(trustyProxy) + } + + return engine +} + +func DefaultGinWithTrustedPlatform(trustedPlatform string) *ginsdk.Engine { + engine := ginsdk.New() + engine.Use(ginsdk.Logger(), ginsdk.Recovery()) + + if len(trustedPlatform) > 0 { + engine.TrustedPlatform = trustedPlatform + } + + return engine +} + +func RoutersRegister(method string, relativePath string, router ...ginsdk.HandlerFunc) { + defaultRouters.Register(method, relativePath, router...) +} + +func RoutersRegisterInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) { + defaultRouters.RegisterInGroup(group, method, relativePath, router...) +} + +func RoutersHandler(engine *ginsdk.Engine) { + defaultRouters.Handler(engine) +} + +func GinEngine(trustedPlatform string, trustyProxy ...string) (*ginsdk.Engine, error) { + var err error + + engine := ginsdk.New() + if len(trustyProxy) > 0 { + err = engine.SetTrustedProxies(trustyProxy) + } + if len(trustedPlatform) > 0 { + engine.TrustedPlatform = trustedPlatform + } + + return engine, err +} + +func GinAddGlobalMiddleware(eng *ginsdk.Engine, middleware ...ginsdk.HandlerFunc) *ginsdk.Engine { + eng.Use(middleware...) + return eng +} + +// SetGinHandler func that return given func as ginTonic HandlerFunc interface type. +func SetGinHandler(fct func(c *ginsdk.Context)) ginsdk.HandlerFunc { + return fct +} + +func Handler(routerList RouterList) http.Handler { + e := routerList.Engine() + + if routerList == nil { + RoutersHandler(e) + } else { + routerList.Handler(e) + } + + return e +} diff --git a/router/header/config.go b/router/header/config.go new file mode 100644 index 00000000..abd1156c --- /dev/null +++ b/router/header/config.go @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package header + +import ( + liberr "github.com/nabbar/golib/errors" + "github.com/nabbar/golib/router" +) + +type HeadersConfig map[string]string + +func (h HeadersConfig) New() Headers { + var res = NewHeaders() + + for k, v := range h { + res.Add(k, v) + } + + return res +} + +func (h HeadersConfig) Validate() liberr.Error { + err := router.ErrorConfigValidator.Error(nil) + + if !err.HasParent() { + err = nil + } + + return err +} diff --git a/router/header/interface.go b/router/header/interface.go new file mode 100644 index 00000000..dc1ab3da --- /dev/null +++ b/router/header/interface.go @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package header + +import ( + "net/http" + + ginsdk "github.com/gin-gonic/gin" +) + +type Headers interface { + Add(key, value string) + Set(key, value string) + Get(key string) string + Del(key string) + + Header() map[string]string + Register(router ...ginsdk.HandlerFunc) []ginsdk.HandlerFunc + Handler(c *ginsdk.Context) + + Clone() Headers +} + +func NewHeaders() Headers { + return &headers{ + head: make(http.Header), + } +} diff --git a/router/headers.go b/router/header/model.go similarity index 80% rename from router/headers.go rename to router/header/model.go index d244a766..595eb5f2 100644 --- a/router/headers.go +++ b/router/header/model.go @@ -23,60 +23,18 @@ * */ -package router +package header import ( "net/http" ginsdk "github.com/gin-gonic/gin" - liberr "github.com/nabbar/golib/errors" ) -type HeadersConfig map[string]string - -func (h HeadersConfig) New() Headers { - var res = NewHeaders() - - for k, v := range h { - res.Add(k, v) - } - - return res -} - -func (h HeadersConfig) Validate() liberr.Error { - err := ErrorConfigValidator.Error(nil) - - if !err.HasParent() { - err = nil - } - - return err -} - type headers struct { head http.Header } -type Headers interface { - Add(key, value string) - Set(key, value string) - Get(key string) string - Del(key string) - - Header() map[string]string - Register(router ...ginsdk.HandlerFunc) []ginsdk.HandlerFunc - Handler(c *ginsdk.Context) - - Clone() Headers -} - -func NewHeaders() Headers { - return &headers{ - head: make(http.Header), - } -} - func (h headers) Clone() Headers { return &headers{ head: h.head, diff --git a/router/interface.go b/router/interface.go new file mode 100644 index 00000000..769dabbc --- /dev/null +++ b/router/interface.go @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package router + +import ( + "os" + + ginsdk "github.com/gin-gonic/gin" +) + +const ( + EmptyHandlerGroup = "" + GinContextStartUnixNanoTime = "gin-ctx-start-unix-nano-time" + GinContextRequestPath = "gin-ctx-request-path" + GinContextRequestUser = "gin-ctx-request-user" +) + +var ( + defaultRouters = NewRouterList(DefaultGinInit) +) + +func init() { + if os.Getenv("GIN_MODE") == "" { + ginsdk.SetMode(ginsdk.ReleaseMode) + } +} + +type RegisterRouter func(method string, relativePath string, router ...ginsdk.HandlerFunc) +type RegisterRouterInGroup func(group, method string, relativePath string, router ...ginsdk.HandlerFunc) + +type RouterList interface { + Register(method string, relativePath string, router ...ginsdk.HandlerFunc) + RegisterInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) + RegisterMergeInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) + Handler(engine *ginsdk.Engine) + Engine() *ginsdk.Engine +} + +func NewRouterList(initGin func() *ginsdk.Engine) RouterList { + return &rtr{ + init: initGin, + list: make(map[string][]itm), + } +} diff --git a/router/register.go b/router/middleware.go similarity index 55% rename from router/register.go rename to router/middleware.go index c4c38877..0f17135d 100644 --- a/router/register.go +++ b/router/middleware.go @@ -40,36 +40,6 @@ import ( loglvl "github.com/nabbar/golib/logger/level" ) -const ( - EmptyHandlerGroup = "" - GinContextStartUnixNanoTime = "gin-ctx-start-unix-nano-time" - GinContextRequestPath = "gin-ctx-request-path" - GinContextRequestUser = "gin-ctx-request-user" -) - -var ( - defaultRouters = NewRouterList(DefaultGinInit) -) - -func GinEngine(trustedPlatform string, trustyProxy ...string) (*ginsdk.Engine, error) { - var err error - - engine := ginsdk.New() - if len(trustyProxy) > 0 { - err = engine.SetTrustedProxies(trustyProxy) - } - if len(trustedPlatform) > 0 { - engine.TrustedPlatform = trustedPlatform - } - - return engine, err -} - -func GinAddGlobalMiddleware(eng *ginsdk.Engine, middleware ...ginsdk.HandlerFunc) *ginsdk.Engine { - eng.Use(middleware...) - return eng -} - func GinLatencyContext(c *ginsdk.Context) { // Start timer c.Set(GinContextStartUnixNanoTime, time.Now().UnixNano()) @@ -78,13 +48,6 @@ func GinLatencyContext(c *ginsdk.Context) { c.Next() } -func sanitizeString(s string) string { - s = strings.Replace(s, "\n", "", -1) - s = strings.Replace(s, "\r", "", -1) - s = strings.Replace(s, "\t", "", -1) - return s -} - func GinRequestContext(c *ginsdk.Context) { // Set Path if c != nil { @@ -204,115 +167,3 @@ func GinErrorLog(log liblog.FuncLog) ginsdk.HandlerFunc { c.Next() } } - -func DefaultGinInit() *ginsdk.Engine { - engine := ginsdk.New() - engine.Use(ginsdk.Logger(), ginsdk.Recovery()) - - return engine -} - -func DefaultGinWithTrustyProxy(trustyProxy []string) *ginsdk.Engine { - engine := ginsdk.New() - engine.Use(ginsdk.Logger(), ginsdk.Recovery()) - - if len(trustyProxy) > 0 { - _ = engine.SetTrustedProxies(trustyProxy) - } - - return engine -} - -func DefaultGinWithTrustedPlatform(trustedPlatform string) *ginsdk.Engine { - engine := ginsdk.New() - engine.Use(ginsdk.Logger(), ginsdk.Recovery()) - - if len(trustedPlatform) > 0 { - engine.TrustedPlatform = trustedPlatform - } - - return engine -} - -type routerItem struct { - method string - relative string - router []ginsdk.HandlerFunc -} - -type routerList struct { - init func() *ginsdk.Engine - list map[string][]routerItem -} - -type RegisterRouter func(method string, relativePath string, router ...ginsdk.HandlerFunc) -type RegisterRouterInGroup func(group, method string, relativePath string, router ...ginsdk.HandlerFunc) - -type RouterList interface { - Register(method string, relativePath string, router ...ginsdk.HandlerFunc) - RegisterInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) - Handler(engine *ginsdk.Engine) - Engine() *ginsdk.Engine -} - -func RoutersRegister(method string, relativePath string, router ...ginsdk.HandlerFunc) { - defaultRouters.Register(method, relativePath, router...) -} - -func RoutersRegisterInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) { - defaultRouters.RegisterInGroup(group, method, relativePath, router...) -} - -func RoutersHandler(engine *ginsdk.Engine) { - defaultRouters.Handler(engine) -} - -func NewRouterList(initGin func() *ginsdk.Engine) RouterList { - return &routerList{ - init: initGin, - list: make(map[string][]routerItem), - } -} - -func (l routerList) Handler(engine *ginsdk.Engine) { - for grpRoute, grpList := range l.list { - if grpRoute == EmptyHandlerGroup { - for _, r := range grpList { - engine.Handle(r.method, r.relative, r.router...) - } - } else { - var grp = engine.Group(grpRoute) - for _, r := range grpList { - grp.Handle(r.method, r.relative, r.router...) - } - } - } -} - -func (l *routerList) RegisterInGroup(group, method string, relativePath string, router ...ginsdk.HandlerFunc) { - if group == "" { - group = EmptyHandlerGroup - } - - if _, ok := l.list[group]; !ok { - l.list[group] = make([]routerItem, 0) - } - - l.list[group] = append(l.list[group], routerItem{ - method: method, - relative: relativePath, - router: router, - }) -} - -func (l *routerList) Register(method string, relativePath string, router ...ginsdk.HandlerFunc) { - l.RegisterInGroup("", method, relativePath, router...) -} - -func (l routerList) Engine() *ginsdk.Engine { - if l.init != nil { - return l.init() - } else { - return DefaultGinInit() - } -} diff --git a/router/model.go b/router/model.go new file mode 100644 index 00000000..79a3d061 --- /dev/null +++ b/router/model.go @@ -0,0 +1,104 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package router + +import ( + ginsdk "github.com/gin-gonic/gin" +) + +type rtr struct { + init func() *ginsdk.Engine + list map[string][]itm +} + +func (l *rtr) Handler(engine *ginsdk.Engine) { + for grpRoute, grpList := range l.list { + if grpRoute == EmptyHandlerGroup { + for _, r := range grpList { + engine.Handle(r.method, r.relative, r.router...) + } + } else { + var grp = engine.Group(grpRoute) + for _, r := range grpList { + grp.Handle(r.method, r.relative, r.router...) + } + } + } +} + +func (l *rtr) RegisterInGroup(group, method, relativePath string, router ...ginsdk.HandlerFunc) { + if group == "" { + group = EmptyHandlerGroup + } + + if _, ok := l.list[group]; !ok { + l.list[group] = make([]itm, 0) + } + + l.list[group] = append(l.list[group], itm{ + method: method, + relative: relativePath, + router: router, + }) +} + +func (l *rtr) RegisterMergeInGroup(group, method, relativePath string, router ...ginsdk.HandlerFunc) { + if group == "" { + group = EmptyHandlerGroup + } + + if _, ok := l.list[group]; !ok { + l.list[group] = make([]itm, 0) + } + + // if same route exists for same relative path and same method, so replace router list + for i, r := range l.list[group] { + if !r.Same(method, relativePath) { + continue + } else { + l.list[group][i].Merge(router...) + return + } + } + + l.list[group] = append(l.list[group], itm{ + method: method, + relative: relativePath, + router: router, + }) +} + +func (l *rtr) Register(method, relativePath string, router ...ginsdk.HandlerFunc) { + l.RegisterInGroup("", method, relativePath, router...) +} + +func (l *rtr) Engine() *ginsdk.Engine { + if l.init != nil { + return l.init() + } else { + return DefaultGinInit() + } +} diff --git a/router/router.go b/router/router.go index a961bbef..e749f75f 100644 --- a/router/router.go +++ b/router/router.go @@ -25,32 +25,24 @@ package router -import ( - "net/http" - "os" +import ginsdk "github.com/gin-gonic/gin" - ginsdk "github.com/gin-gonic/gin" -) - -func init() { - if os.Getenv("GIN_MODE") == "" { - ginsdk.SetMode(ginsdk.ReleaseMode) - } -} - -// SetGinHandler func that return given func as ginTonic HandlerFunc interface type. -func SetGinHandler(fct func(c *ginsdk.Context)) ginsdk.HandlerFunc { - return fct +type itm struct { + method string + relative string + router []ginsdk.HandlerFunc } -func Handler(routerList RouterList) http.Handler { - e := routerList.Engine() - - if routerList == nil { - RoutersHandler(e) - } else { - routerList.Handler(e) +func (o *itm) Same(method, relative string) bool { + if o.method != method { + return false + } + if o.relative != relative { + return false } + return true +} - return e +func (o *itm) Merge(rtr ...ginsdk.HandlerFunc) { + o.router = rtr } diff --git a/router/tools.go b/router/tools.go new file mode 100644 index 00000000..39a5a5fd --- /dev/null +++ b/router/tools.go @@ -0,0 +1,35 @@ +/* + * MIT License + * + * Copyright (c) 2019 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package router + +import "strings" + +func sanitizeString(s string) string { + s = strings.Replace(s, "\n", "", -1) + s = strings.Replace(s, "\r", "", -1) + s = strings.Replace(s, "\t", "", -1) + return s +} diff --git a/static/interface.go b/static/interface.go index 65cb3dc1..67824b70 100644 --- a/static/interface.go +++ b/static/interface.go @@ -30,7 +30,6 @@ import ( "embed" "io" "os" - "sync" "sync/atomic" libfpg "github.com/nabbar/golib/file/progress" @@ -48,7 +47,7 @@ type Static interface { RegisterRouter(route string, register librtr.RegisterRouter, router ...ginsdk.HandlerFunc) RegisterRouterInGroup(route, group string, register librtr.RegisterRouterInGroup, router ...ginsdk.HandlerFunc) - RegisterLogger(log func() liblog.Logger) + RegisterLogger(log liblog.FuncLog) SetDownload(pathFile string, flag bool) SetIndex(group, route, pathFile string) @@ -79,15 +78,19 @@ type Static interface { } func New(ctx libctx.FuncContext, content embed.FS, embedRootDir ...string) Static { - return &staticHandler{ - m: sync.RWMutex{}, - l: nil, + s := &staticHandler{ + l: new(atomic.Value), c: content, - b: embedRootDir, - z: 0, + b: new(atomic.Value), + z: new(atomic.Int64), i: libctx.NewConfig[string](ctx), d: libctx.NewConfig[string](ctx), f: libctx.NewConfig[string](ctx), + s: libctx.NewConfig[string](ctx), r: new(atomic.Value), + h: new(atomic.Value), } + s._setBase(embedRootDir...) + s._setLogger(nil) + return s } diff --git a/static/model.go b/static/model.go index c8c0a039..e030742e 100644 --- a/static/model.go +++ b/static/model.go @@ -28,7 +28,6 @@ package static import ( "embed" - "sync" "sync/atomic" libctx "github.com/nabbar/golib/context" @@ -40,62 +39,46 @@ const ( ) type staticHandler struct { - m sync.RWMutex - l func() liblog.Logger + l *atomic.Value // logger + r *atomic.Value // router + h *atomic.Value // monitor c embed.FS - b []string // base - z int64 // size + b *atomic.Value // base []string + z *atomic.Int64 // size i libctx.Config[string] // index d libctx.Config[string] // download f libctx.Config[string] // follow s libctx.Config[string] // specific - r *atomic.Value // router - h *atomic.Value // monitor } -func (s *staticHandler) _setLogger(fct func() liblog.Logger) { - s.m.Lock() - defer s.m.Unlock() +func (s *staticHandler) _setLogger(fct liblog.FuncLog) { + if fct == nil { + fct = s._getDefaultLogger + } - s.l = fct + s.l.Store(fct()) } func (s *staticHandler) _getLogger() liblog.Logger { - s.m.RLock() - defer s.m.RUnlock() - - var log liblog.Logger - - if s.l == nil { - s.m.RUnlock() - log = s._getDefaultLogger() - s.m.RLock() - return log - } else if log = s.l(); log == nil { - s.m.RUnlock() - log = s._getDefaultLogger() - s.m.RLock() - return log + i := s.l.Load() + + if i == nil { + return s._getDefaultLogger() + } else if l, k := i.(liblog.FuncLog); !k { + return s._getDefaultLogger() + } else if log := l(); log == nil { + return s._getDefaultLogger() } else { return log } } func (s *staticHandler) _getDefaultLogger() liblog.Logger { - s.m.Lock() - defer s.m.Unlock() - - var log = liblog.New(s.d.GetContext) - - s.l = func() liblog.Logger { - return log - } - - return log + return liblog.New(s.d.GetContext) } -func (s *staticHandler) RegisterLogger(log func() liblog.Logger) { +func (s *staticHandler) RegisterLogger(log liblog.FuncLog) { s._setLogger(log) } diff --git a/static/pathfile.go b/static/pathfile.go index acf209a9..85a2edc0 100644 --- a/static/pathfile.go +++ b/static/pathfile.go @@ -41,31 +41,32 @@ import ( ) func (s *staticHandler) _getSize() int64 { - s.m.RLock() - defer s.m.RUnlock() - - return s.z + return s.z.Load() } func (s *staticHandler) _setSize(size int64) { - s.m.Lock() - defer s.m.Unlock() - - s.z = size + s.z.Store(size) } func (s *staticHandler) _getBase() []string { - s.m.RLock() - defer s.m.RUnlock() - - return s.b + i := s.b.Load() + if i == nil { + return make([]string, 0) + } else if b, k := i.([]string); !k { + return make([]string, 0) + } else { + return b + } } func (s *staticHandler) _setBase(base ...string) { - s.m.Lock() - defer s.m.Unlock() + var b = make([]string, 0) - s.b = base + if len(base) > 0 { + b = append(b, base...) + } + + s.b.Store(b) } func (s *staticHandler) _listEmbed(root string) ([]fs.DirEntry, liberr.Error) { @@ -73,9 +74,6 @@ func (s *staticHandler) _listEmbed(root string) ([]fs.DirEntry, liberr.Error) { return nil, ErrorParamEmpty.Error(fmt.Errorf("pathfile is empty")) } - s.m.RLock() - defer s.m.RUnlock() - val, err := s.c.ReadDir(root) if err != nil && errors.Is(err, fs.ErrNotExist) { @@ -108,9 +106,6 @@ func (s *staticHandler) _fileInfo(pathFile string) (fs.FileInfo, liberr.Error) { return nil, ErrorParamEmpty.Error(fmt.Errorf("pathfile is empty")) } - s.m.RLock() - defer s.m.RUnlock() - var inf fs.FileInfo obj, err := s.c.Open(pathFile) @@ -140,9 +135,6 @@ func (s *staticHandler) _fileBuff(pathFile string) (io.ReadCloser, liberr.Error) return nil, ErrorParamEmpty.Error(fmt.Errorf("pathfile is empty")) } - s.m.RLock() - defer s.m.RUnlock() - obj, err := s.c.ReadFile(pathFile) if err != nil && errors.Is(err, fs.ErrNotExist) { @@ -159,9 +151,6 @@ func (s *staticHandler) _fileTemp(pathFile string) (libfpg.Progress, liberr.Erro return nil, ErrorParamEmpty.Error(fmt.Errorf("pathfile is empty")) } - s.m.RLock() - defer s.m.RUnlock() - var tmp libfpg.Progress obj, err := s.c.Open(pathFile) diff --git a/static/route.go b/static/route.go index 638b22b0..20dd1146 100644 --- a/static/route.go +++ b/static/route.go @@ -35,6 +35,8 @@ import ( "path" "strings" + "github.com/nabbar/golib/router/header" + ginsdk "github.com/gin-gonic/gin" ginrdr "github.com/gin-gonic/gin/render" liberr "github.com/nabbar/golib/errors" @@ -50,18 +52,53 @@ func (s *staticHandler) _makeRoute(group, route string) string { return path.Join(group, route) } -func (s *staticHandler) RegisterRouter(route string, register librtr.RegisterRouter, router ...ginsdk.HandlerFunc) { - s._setRouter(append(s._getRouter(), s._makeRoute(urlPathSeparator, route))) +func (s *staticHandler) genRegisterRouter(route, group string, register any, router ...ginsdk.HandlerFunc) { + var ( + ok bool + rte string + reg librtr.RegisterRouter + grp librtr.RegisterRouterInGroup + ) + + if register == nil { + return + } else if reg, ok = register.(librtr.RegisterRouter); ok { + rte = s._makeRoute(urlPathSeparator, route) + grp = nil + } else if grp, ok = register.(librtr.RegisterRouterInGroup); ok { + rte = s._makeRoute(group, route) + reg = nil + } else { + return + } + + if len(router) > 0 { + router = append(router, s.Get) + } else { + router = append(make([]ginsdk.HandlerFunc, 0), s.Get) + } + + if rtr := s._getRouter(); len(rtr) > 0 { + s._setRouter(append(rtr, rte)) + } else { + s._setRouter(append(make([]string, 0), rte)) + } + + if reg != nil { + reg(http.MethodGet, path.Join(route, urlPathSeparator+"*file"), router...) + } - router = append(router, s.Get) - register(http.MethodGet, path.Join(route, urlPathSeparator+"*file"), router...) + if grp != nil { + grp(group, http.MethodGet, path.Join(route, urlPathSeparator+"*file"), router...) + } } -func (s *staticHandler) RegisterRouterInGroup(route, group string, register librtr.RegisterRouterInGroup, router ...ginsdk.HandlerFunc) { - s._setRouter(append(s._getRouter(), s._makeRoute(group, route))) +func (s *staticHandler) RegisterRouter(route string, register librtr.RegisterRouter, router ...ginsdk.HandlerFunc) { + s.genRegisterRouter(route, "", register, router...) +} - router = append(router, s.Get) - register(group, http.MethodGet, path.Join(route, urlPathSeparator+"*file"), router...) +func (s *staticHandler) RegisterRouterInGroup(route, group string, register librtr.RegisterRouterInGroup, router ...ginsdk.HandlerFunc) { + s.genRegisterRouter(route, group, register, router...) } func (s *staticHandler) Get(c *ginsdk.Context) { @@ -139,7 +176,7 @@ func (s *staticHandler) Get(c *ginsdk.Context) { } func (s *staticHandler) SendFile(c *ginsdk.Context, filename string, size int64, isDownload bool, buf io.ReadCloser) { - head := librtr.NewHeaders() + head := header.NewHeaders() if isDownload { head.Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", path.Base(filename))) diff --git a/static/router.go b/static/router.go index a4fd19e7..04935bdf 100644 --- a/static/router.go +++ b/static/router.go @@ -26,36 +26,22 @@ package static -import "sync/atomic" - func (s *staticHandler) _getRouter() []string { - s.m.Lock() - defer s.m.Unlock() - - var def = make([]string, 0) - if s.r == nil { - return def - } if i := s.r.Load(); i == nil { - return def + return make([]string, 0) } else if o, ok := i.([]string); !ok { - return def + return make([]string, 0) } else { return o } } func (s *staticHandler) _setRouter(val []string) { - s.m.Lock() - defer s.m.Unlock() - - if val == nil { - val = make([]string, 0) - } + var rtr = make([]string, 0) - if s.r == nil { - s.r = new(atomic.Value) + if len(val) > 0 { + copy(rtr, val) } - s.r.Store(val) + s.r.Store(rtr) }