add reload for web api
This commit is contained in:
+13
-83
@@ -2,13 +2,11 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/auth"
|
"github.com/go-gost/core/auth"
|
||||||
"github.com/go-gost/core/service"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -21,55 +19,17 @@ type Response struct {
|
|||||||
Msg string `json:"msg,omitempty"`
|
Msg string `json:"msg,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type Options struct {
|
||||||
accessLog bool
|
AccessLog bool
|
||||||
pathPrefix string
|
PathPrefix string
|
||||||
auther auth.Authenticator
|
Auther auth.Authenticator
|
||||||
}
|
}
|
||||||
|
|
||||||
type Option func(*options)
|
func Register(r *gin.Engine, opts *Options) {
|
||||||
|
if opts == nil {
|
||||||
func PathPrefixOption(pathPrefix string) Option {
|
opts = &Options{}
|
||||||
return func(o *options) {
|
|
||||||
o.pathPrefix = pathPrefix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AccessLogOption(enable bool) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.accessLog = enable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AutherOption(auther auth.Authenticator) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.auther = auther
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type server struct {
|
|
||||||
s *http.Server
|
|
||||||
ln net.Listener
|
|
||||||
cclose chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(network, addr string, opts ...Option) (service.Service, error) {
|
|
||||||
if network == "" {
|
|
||||||
network = "tcp"
|
|
||||||
}
|
|
||||||
ln, err := net.Listen(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var options options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
|
||||||
|
|
||||||
r := gin.New()
|
|
||||||
r.Use(
|
r.Use(
|
||||||
cors.New((cors.Config{
|
cors.New((cors.Config{
|
||||||
AllowAllOrigins: true,
|
AllowAllOrigins: true,
|
||||||
@@ -79,55 +39,25 @@ func NewService(network, addr string, opts ...Option) (service.Service, error) {
|
|||||||
})),
|
})),
|
||||||
gin.Recovery(),
|
gin.Recovery(),
|
||||||
)
|
)
|
||||||
if options.accessLog {
|
if opts.AccessLog {
|
||||||
r.Use(mwLogger())
|
r.Use(mwLogger())
|
||||||
}
|
}
|
||||||
|
|
||||||
router := r.Group("")
|
router := r.Group("")
|
||||||
if options.pathPrefix != "" {
|
if opts.PathPrefix != "" {
|
||||||
router = router.Group(options.pathPrefix)
|
router = router.Group(opts.PathPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
router.StaticFS("/docs", http.FS(swaggerDoc))
|
router.StaticFS("/docs", http.FS(swaggerDoc))
|
||||||
|
|
||||||
config := router.Group("/config")
|
config := router.Group("/config")
|
||||||
config.Use(mwBasicAuth(options.auther))
|
config.Use(mwBasicAuth(opts.Auther))
|
||||||
registerConfig(config)
|
|
||||||
|
|
||||||
return &server{
|
|
||||||
s: &http.Server{
|
|
||||||
Handler: r,
|
|
||||||
},
|
|
||||||
ln: ln,
|
|
||||||
cclose: make(chan struct{}),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) Serve() error {
|
|
||||||
return s.s.Serve(s.ln)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) Addr() net.Addr {
|
|
||||||
return s.ln.Addr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) Close() error {
|
|
||||||
return s.s.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) IsClosed() bool {
|
|
||||||
select {
|
|
||||||
case <-s.cclose:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerConfig(config *gin.RouterGroup) {
|
|
||||||
config.GET("", getConfig)
|
config.GET("", getConfig)
|
||||||
config.POST("", saveConfig)
|
config.POST("", saveConfig)
|
||||||
|
|
||||||
|
config.POST("/reload", reloadConfig)
|
||||||
|
|
||||||
config.POST("/services", createService)
|
config.POST("/services", createService)
|
||||||
config.PUT("/services/:service", updateService)
|
config.PUT("/services/:service", updateService)
|
||||||
config.DELETE("/services/:service", deleteService)
|
config.DELETE("/services/:service", deleteService)
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-gost/x/config"
|
||||||
|
"github.com/go-gost/x/config/loader"
|
||||||
|
"github.com/go-gost/x/config/parsing/parser"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// swagger:parameters reloadConfigRequest
|
||||||
|
type reloadConfigRequest struct{}
|
||||||
|
|
||||||
|
// successful operation.
|
||||||
|
// swagger:response reloadConfigResponse
|
||||||
|
type reloadConfigResponse struct {
|
||||||
|
Data Response
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadConfig(ctx *gin.Context) {
|
||||||
|
// swagger:route POST /config/reload Reload reloadConfigRequest
|
||||||
|
//
|
||||||
|
// Hot reload config.
|
||||||
|
//
|
||||||
|
// Security:
|
||||||
|
// basicAuth: []
|
||||||
|
//
|
||||||
|
// Responses:
|
||||||
|
// 200: reloadConfigResponse
|
||||||
|
|
||||||
|
cfg, err := parser.Parse()
|
||||||
|
if err != nil {
|
||||||
|
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Set(cfg)
|
||||||
|
|
||||||
|
if err := loader.Load(cfg); err != nil {
|
||||||
|
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, svc := range registry.ServiceRegistry().GetAll() {
|
||||||
|
svc := svc
|
||||||
|
go func() {
|
||||||
|
svc.Serve()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, Response{
|
||||||
|
Msg: "OK",
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-gost/core/auth"
|
||||||
|
"github.com/go-gost/core/service"
|
||||||
|
"github.com/go-gost/x/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
accessLog bool
|
||||||
|
pathPrefix string
|
||||||
|
auther auth.Authenticator
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func PathPrefixOption(pathPrefix string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.pathPrefix = pathPrefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AccessLogOption(enable bool) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.accessLog = enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AutherOption(auther auth.Authenticator) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.auther = auther
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
s *http.Server
|
||||||
|
ln net.Listener
|
||||||
|
cclose chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(network, addr string, opts ...Option) (service.Service, error) {
|
||||||
|
if network == "" {
|
||||||
|
network = "tcp"
|
||||||
|
}
|
||||||
|
ln, err := net.Listen(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var options options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
|
r := gin.New()
|
||||||
|
api.Register(r, &api.Options{
|
||||||
|
AccessLog: options.accessLog,
|
||||||
|
PathPrefix: options.pathPrefix,
|
||||||
|
Auther: options.auther,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &server{
|
||||||
|
s: &http.Server{
|
||||||
|
Handler: r,
|
||||||
|
},
|
||||||
|
ln: ln,
|
||||||
|
cclose: make(chan struct{}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) Serve() error {
|
||||||
|
return s.s.Serve(s.ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) Addr() net.Addr {
|
||||||
|
return s.ln.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) Close() error {
|
||||||
|
return s.s.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) IsClosed() bool {
|
||||||
|
select {
|
||||||
|
case <-s.cclose:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
+103
-8
@@ -41,7 +41,7 @@ definitions:
|
|||||||
reload:
|
reload:
|
||||||
$ref: '#/definitions/Duration'
|
$ref: '#/definitions/Duration'
|
||||||
reverse:
|
reverse:
|
||||||
description: DEPRECATED by whitelist since beta.4
|
description: 'Deprecated: use whitelist instead'
|
||||||
type: boolean
|
type: boolean
|
||||||
x-go-name: Reverse
|
x-go-name: Reverse
|
||||||
whitelist:
|
whitelist:
|
||||||
@@ -102,7 +102,7 @@ definitions:
|
|||||||
reload:
|
reload:
|
||||||
$ref: '#/definitions/Duration'
|
$ref: '#/definitions/Duration'
|
||||||
reverse:
|
reverse:
|
||||||
description: DEPRECATED by whitelist since beta.4
|
description: 'Deprecated: use whitelist instead'
|
||||||
type: boolean
|
type: boolean
|
||||||
x-go-name: Reverse
|
x-go-name: Reverse
|
||||||
whitelist:
|
whitelist:
|
||||||
@@ -309,11 +309,13 @@ definitions:
|
|||||||
filter:
|
filter:
|
||||||
$ref: '#/definitions/NodeFilterConfig'
|
$ref: '#/definitions/NodeFilterConfig'
|
||||||
host:
|
host:
|
||||||
description: DEPRECATED by filter.host
|
description: 'Deprecated: use matcher instead'
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Host
|
x-go-name: Host
|
||||||
http:
|
http:
|
||||||
$ref: '#/definitions/HTTPNodeConfig'
|
$ref: '#/definitions/HTTPNodeConfig'
|
||||||
|
matcher:
|
||||||
|
$ref: '#/definitions/NodeMatcherConfig'
|
||||||
metadata:
|
metadata:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
type: object
|
type: object
|
||||||
@@ -325,11 +327,11 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
x-go-name: Network
|
x-go-name: Network
|
||||||
path:
|
path:
|
||||||
description: DEPRECATED by filter.path
|
description: 'Deprecated: use matcher instead'
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Path
|
x-go-name: Path
|
||||||
protocol:
|
protocol:
|
||||||
description: DEPRECATED by filter.protocol
|
description: 'Deprecated: use matcher instead'
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Protocol
|
x-go-name: Protocol
|
||||||
tls:
|
tls:
|
||||||
@@ -343,7 +345,7 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
x-go-name: Hop
|
x-go-name: Hop
|
||||||
name:
|
name:
|
||||||
description: DEPRECATED by hop field
|
description: 'Deprecated: use hop instead'
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
nodes:
|
nodes:
|
||||||
@@ -355,6 +357,17 @@ definitions:
|
|||||||
$ref: '#/definitions/SelectorConfig'
|
$ref: '#/definitions/SelectorConfig'
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
|
HTTPBodyRewriteConfig:
|
||||||
|
properties:
|
||||||
|
Match:
|
||||||
|
type: string
|
||||||
|
Replacement:
|
||||||
|
type: string
|
||||||
|
Type:
|
||||||
|
description: filter by MIME types
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-go-package: github.com/go-gost/x/config
|
||||||
HTTPLoader:
|
HTTPLoader:
|
||||||
properties:
|
properties:
|
||||||
timeout:
|
timeout:
|
||||||
@@ -371,20 +384,52 @@ definitions:
|
|||||||
header:
|
header:
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: string
|
type: string
|
||||||
|
description: 'Deprecated: use requestHeader instead'
|
||||||
type: object
|
type: object
|
||||||
x-go-name: Header
|
x-go-name: Header
|
||||||
host:
|
host:
|
||||||
|
description: rewrite host header
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Host
|
x-go-name: Host
|
||||||
|
requestHeader:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: additional request header
|
||||||
|
type: object
|
||||||
|
x-go-name: RequestHeader
|
||||||
|
responseHeader:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: additional response header
|
||||||
|
type: object
|
||||||
|
x-go-name: ResponseHeader
|
||||||
rewrite:
|
rewrite:
|
||||||
|
description: 'Deprecated: use rewriteURL instead'
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/HTTPURLRewriteConfig'
|
$ref: '#/definitions/HTTPURLRewriteConfig'
|
||||||
type: array
|
type: array
|
||||||
x-go-name: Rewrite
|
x-go-name: Rewrite
|
||||||
|
rewriteBody:
|
||||||
|
description: rewrite response body
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/HTTPBodyRewriteConfig'
|
||||||
|
type: array
|
||||||
|
x-go-name: RewriteBody
|
||||||
|
rewriteURL:
|
||||||
|
description: rewrite URL
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/HTTPURLRewriteConfig'
|
||||||
|
type: array
|
||||||
|
x-go-name: RewriteURL
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
HTTPRecorder:
|
HTTPRecorder:
|
||||||
properties:
|
properties:
|
||||||
|
header:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
x-go-name: Header
|
||||||
timeout:
|
timeout:
|
||||||
$ref: '#/definitions/Duration'
|
$ref: '#/definitions/Duration'
|
||||||
url:
|
url:
|
||||||
@@ -458,6 +503,10 @@ definitions:
|
|||||||
interface:
|
interface:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Interface
|
x-go-name: Interface
|
||||||
|
metadata:
|
||||||
|
additionalProperties: {}
|
||||||
|
type: object
|
||||||
|
x-go-name: Metadata
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
@@ -737,6 +786,8 @@ definitions:
|
|||||||
interface:
|
interface:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Interface
|
x-go-name: Interface
|
||||||
|
matcher:
|
||||||
|
$ref: '#/definitions/NodeMatcherConfig'
|
||||||
metadata:
|
metadata:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
type: object
|
type: object
|
||||||
@@ -744,6 +795,9 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
|
netns:
|
||||||
|
type: string
|
||||||
|
x-go-name: Netns
|
||||||
network:
|
network:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Network
|
x-go-name: Network
|
||||||
@@ -769,6 +823,17 @@ definitions:
|
|||||||
x-go-name: Protocol
|
x-go-name: Protocol
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
|
NodeMatcherConfig:
|
||||||
|
properties:
|
||||||
|
priority:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
x-go-name: Priority
|
||||||
|
rule:
|
||||||
|
type: string
|
||||||
|
x-go-name: Rule
|
||||||
|
type: object
|
||||||
|
x-go-package: github.com/go-gost/x/config
|
||||||
ObserverConfig:
|
ObserverConfig:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
@@ -821,9 +886,10 @@ definitions:
|
|||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
RecorderObject:
|
RecorderObject:
|
||||||
properties:
|
properties:
|
||||||
Metadata:
|
metadata:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
type: object
|
type: object
|
||||||
|
x-go-name: Metadata
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
@@ -850,6 +916,9 @@ definitions:
|
|||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Type
|
x-go-name: Type
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
x-go-name: Username
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
RedisRecorder:
|
RedisRecorder:
|
||||||
@@ -870,6 +939,9 @@ definitions:
|
|||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Type
|
x-go-name: Type
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
x-go-name: Username
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
ResolverConfig:
|
ResolverConfig:
|
||||||
@@ -983,7 +1055,7 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
x-go-name: Hosts
|
x-go-name: Hosts
|
||||||
interface:
|
interface:
|
||||||
description: DEPRECATED by metadata.interface since beta.5
|
description: 'Deprecated: use metadata.interface instead'
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Interface
|
x-go-name: Interface
|
||||||
limiter:
|
limiter:
|
||||||
@@ -1139,6 +1211,11 @@ definitions:
|
|||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
TLSOptions:
|
TLSOptions:
|
||||||
properties:
|
properties:
|
||||||
|
alpn:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
x-go-name: ALPN
|
||||||
cipherSuites:
|
cipherSuites:
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
@@ -1834,6 +1911,18 @@ paths:
|
|||||||
summary: Update recorder by name, the recorder must already exist.
|
summary: Update recorder by name, the recorder must already exist.
|
||||||
tags:
|
tags:
|
||||||
- Recorder
|
- Recorder
|
||||||
|
/config/reload:
|
||||||
|
post:
|
||||||
|
operationId: reloadConfigRequest
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
$ref: '#/responses/reloadConfigResponse'
|
||||||
|
security:
|
||||||
|
- basicAuth:
|
||||||
|
- '[]'
|
||||||
|
summary: Hot reload config.
|
||||||
|
tags:
|
||||||
|
- Reload
|
||||||
/config/resolvers:
|
/config/resolvers:
|
||||||
post:
|
post:
|
||||||
operationId: createResolverRequest
|
operationId: createResolverRequest
|
||||||
@@ -2325,6 +2414,12 @@ responses:
|
|||||||
Config: {}
|
Config: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Config'
|
$ref: '#/definitions/Config'
|
||||||
|
reloadConfigResponse:
|
||||||
|
description: successful operation.
|
||||||
|
headers:
|
||||||
|
Data: {}
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Response'
|
||||||
saveConfigResponse:
|
saveConfigResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
|
|||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/config"
|
||||||
|
"github.com/go-gost/x/config/parsing"
|
||||||
|
admission_parser "github.com/go-gost/x/config/parsing/admission"
|
||||||
|
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||||
|
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||||
|
chain_parser "github.com/go-gost/x/config/parsing/chain"
|
||||||
|
hop_parser "github.com/go-gost/x/config/parsing/hop"
|
||||||
|
hosts_parser "github.com/go-gost/x/config/parsing/hosts"
|
||||||
|
ingress_parser "github.com/go-gost/x/config/parsing/ingress"
|
||||||
|
limiter_parser "github.com/go-gost/x/config/parsing/limiter"
|
||||||
|
logger_parser "github.com/go-gost/x/config/parsing/logger"
|
||||||
|
observer_parser "github.com/go-gost/x/config/parsing/observer"
|
||||||
|
recorder_parser "github.com/go-gost/x/config/parsing/recorder"
|
||||||
|
resolver_parser "github.com/go-gost/x/config/parsing/resolver"
|
||||||
|
router_parser "github.com/go-gost/x/config/parsing/router"
|
||||||
|
sd_parser "github.com/go-gost/x/config/parsing/sd"
|
||||||
|
service_parser "github.com/go-gost/x/config/parsing/service"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultLoader *loader = &loader{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Load(cfg *config.Config) error {
|
||||||
|
return defaultLoader.Load(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loader struct{}
|
||||||
|
|
||||||
|
func (l *loader) Load(cfg *config.Config) error {
|
||||||
|
logCfg := cfg.Log
|
||||||
|
if logCfg == nil {
|
||||||
|
logCfg = &config.LogConfig{}
|
||||||
|
}
|
||||||
|
logger.SetDefault(logger_parser.ParseLogger(&config.LoggerConfig{Log: logCfg}))
|
||||||
|
|
||||||
|
tlsCfg, err := parsing.BuildDefaultTLSConfig(cfg.TLS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
parsing.SetDefaultTLSConfig(tlsCfg)
|
||||||
|
|
||||||
|
if err := register(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func register(cfg *config.Config) error {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.LoggerRegistry().GetAll() {
|
||||||
|
registry.LoggerRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, loggerCfg := range cfg.Loggers {
|
||||||
|
if err := registry.LoggerRegistry().Register(loggerCfg.Name, logger_parser.ParseLogger(loggerCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.AutherRegistry().GetAll() {
|
||||||
|
registry.AutherRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, autherCfg := range cfg.Authers {
|
||||||
|
if err := registry.AutherRegistry().Register(autherCfg.Name, auth_parser.ParseAuther(autherCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.AdmissionRegistry().GetAll() {
|
||||||
|
registry.AdmissionRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, admissionCfg := range cfg.Admissions {
|
||||||
|
if err := registry.AdmissionRegistry().Register(admissionCfg.Name, admission_parser.ParseAdmission(admissionCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.BypassRegistry().GetAll() {
|
||||||
|
registry.BypassRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, bypassCfg := range cfg.Bypasses {
|
||||||
|
if err := registry.BypassRegistry().Register(bypassCfg.Name, bypass_parser.ParseBypass(bypassCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.ResolverRegistry().GetAll() {
|
||||||
|
registry.ResolverRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, resolverCfg := range cfg.Resolvers {
|
||||||
|
r, err := resolver_parser.ParseResolver(resolverCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := registry.ResolverRegistry().Register(resolverCfg.Name, r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.HostsRegistry().GetAll() {
|
||||||
|
registry.HostsRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, hostsCfg := range cfg.Hosts {
|
||||||
|
if err := registry.HostsRegistry().Register(hostsCfg.Name, hosts_parser.ParseHostMapper(hostsCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.IngressRegistry().GetAll() {
|
||||||
|
registry.IngressRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, ingressCfg := range cfg.Ingresses {
|
||||||
|
if err := registry.IngressRegistry().Register(ingressCfg.Name, ingress_parser.ParseIngress(ingressCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.RouterRegistry().GetAll() {
|
||||||
|
registry.RouterRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, routerCfg := range cfg.Routers {
|
||||||
|
if err := registry.RouterRegistry().Register(routerCfg.Name, router_parser.ParseRouter(routerCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.SDRegistry().GetAll() {
|
||||||
|
registry.SDRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, sdCfg := range cfg.SDs {
|
||||||
|
if err := registry.SDRegistry().Register(sdCfg.Name, sd_parser.ParseSD(sdCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.ObserverRegistry().GetAll() {
|
||||||
|
registry.ObserverRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, observerCfg := range cfg.Observers {
|
||||||
|
if err := registry.ObserverRegistry().Register(observerCfg.Name, observer_parser.ParseObserver(observerCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.RecorderRegistry().GetAll() {
|
||||||
|
registry.RecorderRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, recorderCfg := range cfg.Recorders {
|
||||||
|
if err := registry.RecorderRegistry().Register(recorderCfg.Name, recorder_parser.ParseRecorder(recorderCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.TrafficLimiterRegistry().GetAll() {
|
||||||
|
registry.TrafficLimiterRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, limiterCfg := range cfg.Limiters {
|
||||||
|
if err := registry.TrafficLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseTrafficLimiter(limiterCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.ConnLimiterRegistry().GetAll() {
|
||||||
|
registry.ConnLimiterRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, limiterCfg := range cfg.CLimiters {
|
||||||
|
if err := registry.ConnLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseConnLimiter(limiterCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.RateLimiterRegistry().GetAll() {
|
||||||
|
registry.RateLimiterRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, limiterCfg := range cfg.RLimiters {
|
||||||
|
if err := registry.RateLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseRateLimiter(limiterCfg)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.HopRegistry().GetAll() {
|
||||||
|
registry.HopRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, hopCfg := range cfg.Hops {
|
||||||
|
hop, err := hop_parser.ParseHop(hopCfg, logger.Default())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := registry.HopRegistry().Register(hopCfg.Name, hop); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.ChainRegistry().GetAll() {
|
||||||
|
registry.ChainRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, chainCfg := range cfg.Chains {
|
||||||
|
c, err := chain_parser.ParseChain(chainCfg, logger.Default())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := registry.ChainRegistry().Register(chainCfg.Name, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range registry.ServiceRegistry().GetAll() {
|
||||||
|
registry.ServiceRegistry().Unregister(name)
|
||||||
|
}
|
||||||
|
for _, svcCfg := range cfg.Services {
|
||||||
|
svc, err := service_parser.ParseService(svcCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if svc != nil {
|
||||||
|
if err := registry.ServiceRegistry().Register(svcCfg.Name, svc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/x/config"
|
||||||
|
"github.com/go-gost/x/config/cmd"
|
||||||
|
xmd "github.com/go-gost/x/metadata"
|
||||||
|
mdutil "github.com/go-gost/x/metadata/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultParser = &parser{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init(args Args) {
|
||||||
|
defaultParser = &parser{
|
||||||
|
args: args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse() (*config.Config, error) {
|
||||||
|
return defaultParser.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args struct {
|
||||||
|
CfgFile string
|
||||||
|
Services []string
|
||||||
|
Nodes []string
|
||||||
|
Debug bool
|
||||||
|
Trace bool
|
||||||
|
ApiAddr string
|
||||||
|
MetricsAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
args Args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) Parse() (*config.Config, error) {
|
||||||
|
cfg := &config.Config{}
|
||||||
|
|
||||||
|
cfgFile := p.args.CfgFile
|
||||||
|
if cfgFile != "" {
|
||||||
|
cfgFile = strings.TrimSpace(cfgFile)
|
||||||
|
if strings.HasPrefix(cfgFile, "{") && strings.HasSuffix(cfgFile, "}") {
|
||||||
|
if err := json.Unmarshal([]byte(cfgFile), cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := cfg.ReadFile(cfgFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdCfg, err := cmd.BuildConfigFromCmd(p.args.Services, p.args.Nodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg = mergeConfig(cfg, cmdCfg)
|
||||||
|
|
||||||
|
if len(cfg.Services) == 0 && p.args.ApiAddr == "" && cfg.API == nil {
|
||||||
|
if err := cfg.Load(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
||||||
|
cfg.Log = &config.LogConfig{
|
||||||
|
Level: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := os.Getenv("GOST_API"); v != "" {
|
||||||
|
cfg.API = &config.APIConfig{
|
||||||
|
Addr: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := os.Getenv("GOST_METRICS"); v != "" {
|
||||||
|
cfg.Metrics = &config.MetricsConfig{
|
||||||
|
Addr: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
||||||
|
cfg.Profiling = &config.ProfilingConfig{
|
||||||
|
Addr: v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.args.Debug || p.args.Trace {
|
||||||
|
if cfg.Log == nil {
|
||||||
|
cfg.Log = &config.LogConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Log.Level = string(logger.DebugLevel)
|
||||||
|
if p.args.Trace {
|
||||||
|
cfg.Log.Level = string(logger.TraceLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.args.ApiAddr != "" {
|
||||||
|
cfg.API = &config.APIConfig{
|
||||||
|
Addr: p.args.ApiAddr,
|
||||||
|
}
|
||||||
|
if url, _ := cmd.Norm(p.args.ApiAddr); url != nil {
|
||||||
|
cfg.API.Addr = url.Host
|
||||||
|
if url.User != nil {
|
||||||
|
username := url.User.Username()
|
||||||
|
password, _ := url.User.Password()
|
||||||
|
cfg.API.Auth = &config.AuthConfig{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := map[string]any{}
|
||||||
|
for k, v := range url.Query() {
|
||||||
|
if len(v) > 0 {
|
||||||
|
m[k] = v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
md := xmd.NewMetadata(m)
|
||||||
|
cfg.API.PathPrefix = mdutil.GetString(md, "pathPrefix")
|
||||||
|
cfg.API.AccessLog = mdutil.GetBool(md, "accesslog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.args.MetricsAddr != "" {
|
||||||
|
cfg.Metrics = &config.MetricsConfig{
|
||||||
|
Addr: p.args.MetricsAddr,
|
||||||
|
}
|
||||||
|
if url, _ := cmd.Norm(p.args.MetricsAddr); url != nil {
|
||||||
|
cfg.Metrics.Addr = url.Host
|
||||||
|
if url.User != nil {
|
||||||
|
username := url.User.Username()
|
||||||
|
password, _ := url.User.Password()
|
||||||
|
cfg.Metrics.Auth = &config.AuthConfig{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := map[string]any{}
|
||||||
|
for k, v := range url.Query() {
|
||||||
|
if len(v) > 0 {
|
||||||
|
m[k] = v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
md := xmd.NewMetadata(m)
|
||||||
|
cfg.Metrics.Path = mdutil.GetString(md, "path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeConfig(cfg1, cfg2 *config.Config) *config.Config {
|
||||||
|
if cfg1 == nil {
|
||||||
|
return cfg2
|
||||||
|
}
|
||||||
|
if cfg2 == nil {
|
||||||
|
return cfg1
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &config.Config{
|
||||||
|
Services: append(cfg1.Services, cfg2.Services...),
|
||||||
|
Chains: append(cfg1.Chains, cfg2.Chains...),
|
||||||
|
Hops: append(cfg1.Hops, cfg2.Hops...),
|
||||||
|
Authers: append(cfg1.Authers, cfg2.Authers...),
|
||||||
|
Admissions: append(cfg1.Admissions, cfg2.Admissions...),
|
||||||
|
Bypasses: append(cfg1.Bypasses, cfg2.Bypasses...),
|
||||||
|
Resolvers: append(cfg1.Resolvers, cfg2.Resolvers...),
|
||||||
|
Hosts: append(cfg1.Hosts, cfg2.Hosts...),
|
||||||
|
Ingresses: append(cfg1.Ingresses, cfg2.Ingresses...),
|
||||||
|
SDs: append(cfg1.SDs, cfg2.SDs...),
|
||||||
|
Recorders: append(cfg1.Recorders, cfg2.Recorders...),
|
||||||
|
Limiters: append(cfg1.Limiters, cfg2.Limiters...),
|
||||||
|
CLimiters: append(cfg1.CLimiters, cfg2.CLimiters...),
|
||||||
|
RLimiters: append(cfg1.RLimiters, cfg2.RLimiters...),
|
||||||
|
Loggers: append(cfg1.Loggers, cfg2.Loggers...),
|
||||||
|
Routers: append(cfg1.Routers, cfg2.Routers...),
|
||||||
|
Observers: append(cfg1.Observers, cfg2.Observers...),
|
||||||
|
TLS: cfg1.TLS,
|
||||||
|
Log: cfg1.Log,
|
||||||
|
API: cfg1.API,
|
||||||
|
Metrics: cfg1.Metrics,
|
||||||
|
Profiling: cfg1.Profiling,
|
||||||
|
}
|
||||||
|
if cfg2.TLS != nil {
|
||||||
|
cfg.TLS = cfg2.TLS
|
||||||
|
}
|
||||||
|
if cfg2.Log != nil {
|
||||||
|
cfg.Log = cfg2.Log
|
||||||
|
}
|
||||||
|
if cfg2.API != nil {
|
||||||
|
cfg.API = cfg2.API
|
||||||
|
}
|
||||||
|
if cfg2.Metrics != nil {
|
||||||
|
cfg.Metrics = cfg2.Metrics
|
||||||
|
}
|
||||||
|
if cfg2.Profiling != nil {
|
||||||
|
cfg.Profiling = cfg2.Profiling
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-gost/core/handler"
|
||||||
|
md "github.com/go-gost/core/metadata"
|
||||||
|
"github.com/go-gost/x/api"
|
||||||
|
"github.com/go-gost/x/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.HandlerRegistry().Register("api", NewHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiHandler struct {
|
||||||
|
handler http.Handler
|
||||||
|
md metadata
|
||||||
|
options handler.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
|
options := handler.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiHandler{
|
||||||
|
options: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiHandler) Init(md md.Metadata) (err error) {
|
||||||
|
if err = h.parseMetadata(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
|
r := gin.New()
|
||||||
|
api.Register(r, &api.Options{
|
||||||
|
AccessLog: h.md.accesslog,
|
||||||
|
PathPrefix: h.md.pathPrefix,
|
||||||
|
Auther: h.options.Auther,
|
||||||
|
})
|
||||||
|
h.handler = r
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
|
l := &singleConnListener{
|
||||||
|
conn: make(chan net.Conn, 1),
|
||||||
|
}
|
||||||
|
l.send(conn)
|
||||||
|
|
||||||
|
s := http.Server{
|
||||||
|
Handler: h.handler,
|
||||||
|
}
|
||||||
|
s.Serve(l)
|
||||||
|
|
||||||
|
return s.Shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
type singleConnListener struct {
|
||||||
|
conn chan net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *singleConnListener) Accept() (net.Conn, error) {
|
||||||
|
if conn, ok := <-l.conn; ok {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
return nil, net.ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *singleConnListener) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *singleConnListener) Addr() net.Addr {
|
||||||
|
return &net.TCPAddr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *singleConnListener) send(conn net.Conn) {
|
||||||
|
l.conn <- conn
|
||||||
|
close(l.conn)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
mdutil "github.com/go-gost/x/metadata/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
accesslog bool
|
||||||
|
pathPrefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *apiHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
|
h.md.accesslog = mdutil.GetBool(md, "api.accessLog", "accessLog")
|
||||||
|
h.md.pathPrefix = mdutil.GetString(md, "api.pathPrefix", "pathPrefix")
|
||||||
|
return
|
||||||
|
}
|
||||||
+22
-41
@@ -1,10 +1,9 @@
|
|||||||
package file
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/handler"
|
"github.com/go-gost/core/handler"
|
||||||
@@ -20,8 +19,7 @@ func init() {
|
|||||||
|
|
||||||
type metricsHandler struct {
|
type metricsHandler struct {
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
server *http.Server
|
mux *http.ServeMux
|
||||||
ln *singleConnListener
|
|
||||||
md metadata
|
md metadata
|
||||||
options handler.Options
|
options handler.Options
|
||||||
}
|
}
|
||||||
@@ -42,32 +40,33 @@ func (h *metricsHandler) Init(md md.Metadata) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xmetrics.Init(xmetrics.NewMetrics())
|
xmetrics.Enable(true)
|
||||||
|
|
||||||
h.handler = promhttp.Handler()
|
h.handler = promhttp.Handler()
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle(h.md.path, http.HandlerFunc(h.handleFunc))
|
mux.Handle(h.md.path, http.HandlerFunc(h.handleFunc))
|
||||||
h.server = &http.Server{
|
h.mux = mux
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ln = &singleConnListener{
|
|
||||||
conn: make(chan net.Conn),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
|
||||||
go h.server.Serve(h.ln)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *metricsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
func (h *metricsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
|
||||||
h.ln.send(conn)
|
l := &singleConnListener{
|
||||||
|
conn: make(chan net.Conn, 1),
|
||||||
|
}
|
||||||
|
l.send(conn)
|
||||||
|
|
||||||
return nil
|
s := http.Server{
|
||||||
|
Handler: h.mux,
|
||||||
|
}
|
||||||
|
s.Serve(l)
|
||||||
|
|
||||||
|
return s.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *metricsHandler) Close() error {
|
func (h *metricsHandler) Close() error {
|
||||||
return h.server.Close()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *metricsHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
func (h *metricsHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -88,47 +87,29 @@ func (h *metricsHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
|||||||
"remote": r.RemoteAddr,
|
"remote": r.RemoteAddr,
|
||||||
"duration": time.Since(start),
|
"duration": time.Since(start),
|
||||||
})
|
})
|
||||||
log.Debugf("%s %s", r.Method, r.RequestURI)
|
log.Infof("%s %s", r.Method, r.RequestURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
type singleConnListener struct {
|
type singleConnListener struct {
|
||||||
conn chan net.Conn
|
conn chan net.Conn
|
||||||
addr net.Addr
|
|
||||||
done chan struct{}
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *singleConnListener) Accept() (net.Conn, error) {
|
func (l *singleConnListener) Accept() (net.Conn, error) {
|
||||||
select {
|
if conn, ok := <-l.conn; ok {
|
||||||
case conn := <-l.conn:
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
|
|
||||||
case <-l.done:
|
|
||||||
return nil, net.ErrClosed
|
|
||||||
}
|
}
|
||||||
|
return nil, net.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *singleConnListener) Close() error {
|
func (l *singleConnListener) Close() error {
|
||||||
l.mu.Lock()
|
|
||||||
defer l.mu.Unlock()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-l.done:
|
|
||||||
default:
|
|
||||||
close(l.done)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *singleConnListener) Addr() net.Addr {
|
func (l *singleConnListener) Addr() net.Addr {
|
||||||
return l.addr
|
return &net.TCPAddr{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *singleConnListener) send(conn net.Conn) {
|
func (l *singleConnListener) send(conn net.Conn) {
|
||||||
select {
|
l.conn <- conn
|
||||||
case l.conn <- conn:
|
close(l.conn)
|
||||||
case <-l.done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package file
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
|
|||||||
+22
-12
@@ -1,6 +1,10 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import "github.com/go-gost/core/metrics"
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of services. Labels: host.
|
// Number of services. Labels: host.
|
||||||
@@ -24,29 +28,35 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
global metrics.Metrics = Noop()
|
defaultMetrics metrics.Metrics = NewMetrics()
|
||||||
|
enabled atomic.Bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(m metrics.Metrics) {
|
func Enable(b bool) {
|
||||||
if m != nil {
|
enabled.Store(b)
|
||||||
global = m
|
|
||||||
} else {
|
|
||||||
global = Noop()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsEnabled() bool {
|
func IsEnabled() bool {
|
||||||
return global != Noop()
|
return enabled.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCounter(name metrics.MetricName, labels metrics.Labels) metrics.Counter {
|
func GetCounter(name metrics.MetricName, labels metrics.Labels) metrics.Counter {
|
||||||
return global.Counter(name, labels)
|
if IsEnabled() {
|
||||||
|
return defaultMetrics.Counter(name, labels)
|
||||||
|
}
|
||||||
|
return noop.Counter(name, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGauge(name metrics.MetricName, labels metrics.Labels) metrics.Gauge {
|
func GetGauge(name metrics.MetricName, labels metrics.Labels) metrics.Gauge {
|
||||||
return global.Gauge(name, labels)
|
if IsEnabled() {
|
||||||
|
return defaultMetrics.Gauge(name, labels)
|
||||||
|
}
|
||||||
|
return noop.Gauge(name, labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetObserver(name metrics.MetricName, labels metrics.Labels) metrics.Observer {
|
func GetObserver(name metrics.MetricName, labels metrics.Labels) metrics.Observer {
|
||||||
return global.Observer(name, labels)
|
if IsEnabled() {
|
||||||
|
return defaultMetrics.Observer(name, labels)
|
||||||
|
}
|
||||||
|
return noop.Observer(name, labels)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user