Compare commits
No commits in common. "dev" and "master" have entirely different histories.
@ -4,13 +4,71 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-gost/core/admission"
|
"github.com/go-gost/core/admission"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/plugin/admission/proto"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type grpcPlugin struct {
|
||||||
|
conn grpc.ClientConnInterface
|
||||||
|
client proto.AdmissionClient
|
||||||
|
log logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGRPCPlugin creates an Admission plugin based on gRPC.
|
||||||
|
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) admission.Admission {
|
||||||
|
var options plugin.Options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{
|
||||||
|
"kind": "admission",
|
||||||
|
"admission": name,
|
||||||
|
})
|
||||||
|
conn, err := plugin.NewGRPCConn(addr, &options)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &grpcPlugin{
|
||||||
|
conn: conn,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
if conn != nil {
|
||||||
|
p.client = proto.NewAdmissionClient(conn)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
|
||||||
|
if p.client == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := p.client.Admit(ctx,
|
||||||
|
&proto.AdmissionRequest{
|
||||||
|
Addr: addr,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
p.log.Error(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return r.Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *grpcPlugin) Close() error {
|
||||||
|
if closer, ok := p.conn.(io.Closer); ok {
|
||||||
|
return closer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type httpPluginRequest struct {
|
type httpPluginRequest struct {
|
||||||
Addr string `json:"addr"`
|
Addr string `json:"addr"`
|
||||||
}
|
}
|
@ -1,67 +0,0 @@
|
|||||||
package admission
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/admission"
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/plugin/admission/proto"
|
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grpcPlugin struct {
|
|
||||||
conn grpc.ClientConnInterface
|
|
||||||
client proto.AdmissionClient
|
|
||||||
log logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGRPCPlugin creates an Admission plugin based on gRPC.
|
|
||||||
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) admission.Admission {
|
|
||||||
var options plugin.Options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
log := logger.Default().WithFields(map[string]any{
|
|
||||||
"kind": "admission",
|
|
||||||
"admission": name,
|
|
||||||
})
|
|
||||||
conn, err := plugin.NewGRPCConn(addr, &options)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &grpcPlugin{
|
|
||||||
conn: conn,
|
|
||||||
log: log,
|
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
p.client = proto.NewAdmissionClient(conn)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
|
|
||||||
if p.client == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := p.client.Admit(ctx,
|
|
||||||
&proto.AdmissionRequest{
|
|
||||||
Addr: addr,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
p.log.Error(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return r.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) Close() error {
|
|
||||||
if closer, ok := p.conn.(io.Closer); ok {
|
|
||||||
return closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
176
api/api.go
176
api/api.go
@ -2,13 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-gost/core/auth"
|
|
||||||
"github.com/go-gost/core/service"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -20,172 +13,3 @@ type Response struct {
|
|||||||
Code int `json:"code,omitempty"`
|
Code int `json:"code,omitempty"`
|
||||||
Msg string `json:"msg,omitempty"`
|
Msg string `json:"msg,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
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(addr string, opts ...Option) (service.Service, error) {
|
|
||||||
ln, err := net.Listen("tcp", 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(
|
|
||||||
cors.New((cors.Config{
|
|
||||||
AllowAllOrigins: true,
|
|
||||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
||||||
AllowHeaders: []string{"*"},
|
|
||||||
AllowPrivateNetwork: true,
|
|
||||||
})),
|
|
||||||
gin.Recovery(),
|
|
||||||
)
|
|
||||||
if options.accessLog {
|
|
||||||
r.Use(mwLogger())
|
|
||||||
}
|
|
||||||
|
|
||||||
router := r.Group("")
|
|
||||||
if options.pathPrefix != "" {
|
|
||||||
router = router.Group(options.pathPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
router.StaticFS("/docs", http.FS(swaggerDoc))
|
|
||||||
|
|
||||||
config := router.Group("/config")
|
|
||||||
config.Use(mwBasicAuth(options.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.POST("", saveConfig)
|
|
||||||
|
|
||||||
config.POST("/services", createService)
|
|
||||||
config.PUT("/services/:service", updateService)
|
|
||||||
config.DELETE("/services/:service", deleteService)
|
|
||||||
|
|
||||||
config.POST("/chains", createChain)
|
|
||||||
config.PUT("/chains/:chain", updateChain)
|
|
||||||
config.DELETE("/chains/:chain", deleteChain)
|
|
||||||
|
|
||||||
config.POST("/hops", createHop)
|
|
||||||
config.PUT("/hops/:hop", updateHop)
|
|
||||||
config.DELETE("/hops/:hop", deleteHop)
|
|
||||||
|
|
||||||
config.POST("/authers", createAuther)
|
|
||||||
config.PUT("/authers/:auther", updateAuther)
|
|
||||||
config.DELETE("/authers/:auther", deleteAuther)
|
|
||||||
|
|
||||||
config.POST("/admissions", createAdmission)
|
|
||||||
config.PUT("/admissions/:admission", updateAdmission)
|
|
||||||
config.DELETE("/admissions/:admission", deleteAdmission)
|
|
||||||
|
|
||||||
config.POST("/bypasses", createBypass)
|
|
||||||
config.PUT("/bypasses/:bypass", updateBypass)
|
|
||||||
config.DELETE("/bypasses/:bypass", deleteBypass)
|
|
||||||
|
|
||||||
config.POST("/resolvers", createResolver)
|
|
||||||
config.PUT("/resolvers/:resolver", updateResolver)
|
|
||||||
config.DELETE("/resolvers/:resolver", deleteResolver)
|
|
||||||
|
|
||||||
config.POST("/hosts", createHosts)
|
|
||||||
config.PUT("/hosts/:hosts", updateHosts)
|
|
||||||
config.DELETE("/hosts/:hosts", deleteHosts)
|
|
||||||
|
|
||||||
config.POST("/ingresses", createIngress)
|
|
||||||
config.PUT("/ingresses/:ingress", updateIngress)
|
|
||||||
config.DELETE("/ingresses/:ingress", deleteIngress)
|
|
||||||
|
|
||||||
config.POST("/routers", createRouter)
|
|
||||||
config.PUT("/routers/:router", updateRouter)
|
|
||||||
config.DELETE("/routers/:router", deleteRouter)
|
|
||||||
|
|
||||||
config.POST("/observers", createObserver)
|
|
||||||
config.PUT("/observers/:observer", updateObserver)
|
|
||||||
config.DELETE("/observers/:observer", deleteObserver)
|
|
||||||
|
|
||||||
config.POST("/recorders", createRecorder)
|
|
||||||
config.PUT("/recorders/:recorder", updateRecorder)
|
|
||||||
config.DELETE("/recorders/:recorder", deleteRecorder)
|
|
||||||
|
|
||||||
config.POST("/sds", createSD)
|
|
||||||
config.PUT("/sds/:sd", updateSD)
|
|
||||||
config.DELETE("/sds/:sd", deleteSD)
|
|
||||||
|
|
||||||
config.POST("/limiters", createLimiter)
|
|
||||||
config.PUT("/limiters/:limiter", updateLimiter)
|
|
||||||
config.DELETE("/limiters/:limiter", deleteLimiter)
|
|
||||||
|
|
||||||
config.POST("/climiters", createConnLimiter)
|
|
||||||
config.PUT("/climiters/:limiter", updateConnLimiter)
|
|
||||||
config.DELETE("/climiters/:limiter", deleteConnLimiter)
|
|
||||||
|
|
||||||
config.POST("/rlimiters", createRateLimiter)
|
|
||||||
config.PUT("/rlimiters/:limiter", updateRateLimiter)
|
|
||||||
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
|
|
||||||
}
|
|
||||||
|
@ -7,16 +7,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/observer/stats"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
"github.com/go-gost/x/service"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type serviceStatus interface {
|
|
||||||
Status() *service.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters getConfigRequest
|
// swagger:parameters getConfigRequest
|
||||||
type getConfigRequest struct {
|
type getConfigRequest struct {
|
||||||
// output format, one of yaml|json, default is json.
|
// output format, one of yaml|json, default is json.
|
||||||
@ -44,40 +37,6 @@ func getConfig(ctx *gin.Context) {
|
|||||||
var req getConfigRequest
|
var req getConfigRequest
|
||||||
ctx.ShouldBindQuery(&req)
|
ctx.ShouldBindQuery(&req)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
for _, svc := range c.Services {
|
|
||||||
if svc == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s := registry.ServiceRegistry().Get(svc.Name)
|
|
||||||
ss, ok := s.(serviceStatus)
|
|
||||||
if ok && ss != nil {
|
|
||||||
status := ss.Status()
|
|
||||||
svc.Status = &config.ServiceStatus{
|
|
||||||
CreateTime: status.CreateTime().Unix(),
|
|
||||||
State: string(status.State()),
|
|
||||||
}
|
|
||||||
if st := status.Stats(); st != nil {
|
|
||||||
svc.Status.Stats = &config.ServiceStats{
|
|
||||||
TotalConns: st.Get(stats.KindTotalConns),
|
|
||||||
CurrentConns: st.Get(stats.KindCurrentConns),
|
|
||||||
TotalErrs: st.Get(stats.KindTotalErrs),
|
|
||||||
InputBytes: st.Get(stats.KindInputBytes),
|
|
||||||
OutputBytes: st.Get(stats.KindOutputBytes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, ev := range status.Events() {
|
|
||||||
if !ev.Time.IsZero() {
|
|
||||||
svc.Status.Events = append(svc.Status.Events, config.ServiceEvent{
|
|
||||||
Time: ev.Time.Unix(),
|
|
||||||
Msg: ev.Message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
var resp getConfigResponse
|
var resp getConfigResponse
|
||||||
resp.Config = config.Global()
|
resp.Config = config.Global()
|
||||||
|
|
||||||
@ -103,9 +62,6 @@ type saveConfigRequest struct {
|
|||||||
// output format, one of yaml|json, default is yaml.
|
// output format, one of yaml|json, default is yaml.
|
||||||
// in: query
|
// in: query
|
||||||
Format string `form:"format" json:"format"`
|
Format string `form:"format" json:"format"`
|
||||||
// file path, default is gost.yaml|gost.json in current working directory.
|
|
||||||
// in: query
|
|
||||||
Path string `form:"path" json:"path"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// successful operation.
|
// successful operation.
|
||||||
@ -136,15 +92,11 @@ func saveConfig(ctx *gin.Context) {
|
|||||||
req.Format = "yaml"
|
req.Format = "yaml"
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Path != "" {
|
|
||||||
file = req.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create(file)
|
f, err := os.Create(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, &Error{
|
writeError(ctx, &Error{
|
||||||
statusCode: http.StatusInternalServerError,
|
statusCode: http.StatusInternalServerError,
|
||||||
Code: ErrCodeSaveConfigFailed,
|
Code: 40005,
|
||||||
Msg: fmt.Sprintf("create file: %s", err.Error()),
|
Msg: fmt.Sprintf("create file: %s", err.Error()),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -154,8 +106,8 @@ func saveConfig(ctx *gin.Context) {
|
|||||||
if err := config.Global().Write(f, req.Format); err != nil {
|
if err := config.Global().Write(f, req.Format); err != nil {
|
||||||
writeError(ctx, &Error{
|
writeError(ctx, &Error{
|
||||||
statusCode: http.StatusInternalServerError,
|
statusCode: http.StatusInternalServerError,
|
||||||
Code: ErrCodeSaveConfigFailed,
|
Code: 40006,
|
||||||
Msg: fmt.Sprintf("save config: %s", err.Error()),
|
Msg: fmt.Sprintf("write: %s", err.Error()),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createAdmission(ctx *gin.Context) {
|
|||||||
var req createAdmissionRequest
|
var req createAdmissionRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "admission name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.AdmissionRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseAdmission(&req.Data)
|
v := parser.ParseAdmission(&req.Data)
|
||||||
|
|
||||||
if err := registry.AdmissionRegistry().Register(name, v); err != nil {
|
if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateAdmission(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Admission)
|
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.AdmissionRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Admission
|
||||||
|
|
||||||
v := parser.ParseAdmission(&req.Data)
|
v := parser.ParseAdmission(&req.Data)
|
||||||
|
|
||||||
registry.AdmissionRegistry().Unregister(name)
|
registry.AdmissionRegistry().Unregister(req.Admission)
|
||||||
|
|
||||||
if err := registry.AdmissionRegistry().Register(name, v); err != nil {
|
if err := registry.AdmissionRegistry().Register(req.Admission, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Admissions {
|
for i := range c.Admissions {
|
||||||
if c.Admissions[i].Name == name {
|
if c.Admissions[i].Name == req.Admission {
|
||||||
c.Admissions[i] = &req.Data
|
c.Admissions[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteAdmission(ctx *gin.Context) {
|
|||||||
var req deleteAdmissionRequest
|
var req deleteAdmissionRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Admission)
|
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.AdmissionRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.AdmissionRegistry().Unregister(name)
|
registry.AdmissionRegistry().Unregister(req.Admission)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
admissiones := c.Admissions
|
admissiones := c.Admissions
|
||||||
c.Admissions = nil
|
c.Admissions = nil
|
||||||
for _, s := range admissiones {
|
for _, s := range admissiones {
|
||||||
if s.Name == name {
|
if s.Name == req.Admission {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Admissions = append(c.Admissions, s)
|
c.Admissions = append(c.Admissions, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,21 +35,14 @@ func createAuther(ctx *gin.Context) {
|
|||||||
var req createAutherRequest
|
var req createAutherRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "auther name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.AutherRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseAuther(&req.Data)
|
v := parser.ParseAuther(&req.Data)
|
||||||
if err := registry.AutherRegistry().Register(name, v); err != nil {
|
if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,26 +86,24 @@ func updateAuther(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Auther)
|
if !registry.AutherRegistry().IsRegistered(req.Auther) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.AutherRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Auther
|
||||||
|
|
||||||
v := parser.ParseAuther(&req.Data)
|
v := parser.ParseAuther(&req.Data)
|
||||||
registry.AutherRegistry().Unregister(name)
|
registry.AutherRegistry().Unregister(req.Auther)
|
||||||
|
|
||||||
if err := registry.AutherRegistry().Register(name, v); err != nil {
|
if err := registry.AutherRegistry().Register(req.Auther, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Authers {
|
for i := range c.Authers {
|
||||||
if c.Authers[i].Name == name {
|
if c.Authers[i].Name == req.Auther {
|
||||||
c.Authers[i] = &req.Data
|
c.Authers[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -154,19 +143,17 @@ func deleteAuther(ctx *gin.Context) {
|
|||||||
var req deleteAutherRequest
|
var req deleteAutherRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Auther)
|
if !registry.AutherRegistry().IsRegistered(req.Auther) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.AutherRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.AutherRegistry().Unregister(name)
|
registry.AutherRegistry().Unregister(req.Auther)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
authers := c.Authers
|
authers := c.Authers
|
||||||
c.Authers = nil
|
c.Authers = nil
|
||||||
for _, s := range authers {
|
for _, s := range authers {
|
||||||
if s.Name == name {
|
if s.Name == req.Auther {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Authers = append(c.Authers, s)
|
c.Authers = append(c.Authers, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createBypass(ctx *gin.Context) {
|
|||||||
var req createBypassRequest
|
var req createBypassRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "bypass name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.BypassRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseBypass(&req.Data)
|
v := parser.ParseBypass(&req.Data)
|
||||||
|
|
||||||
if err := registry.BypassRegistry().Register(name, v); err != nil {
|
if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateBypass(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Bypass)
|
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.BypassRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Bypass
|
||||||
|
|
||||||
v := parser.ParseBypass(&req.Data)
|
v := parser.ParseBypass(&req.Data)
|
||||||
|
|
||||||
registry.BypassRegistry().Unregister(name)
|
registry.BypassRegistry().Unregister(req.Bypass)
|
||||||
|
|
||||||
if err := registry.BypassRegistry().Register(name, v); err != nil {
|
if err := registry.BypassRegistry().Register(req.Bypass, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Bypasses {
|
for i := range c.Bypasses {
|
||||||
if c.Bypasses[i].Name == name {
|
if c.Bypasses[i].Name == req.Bypass {
|
||||||
c.Bypasses[i] = &req.Data
|
c.Bypasses[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteBypass(ctx *gin.Context) {
|
|||||||
var req deleteBypassRequest
|
var req deleteBypassRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Bypass)
|
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.BypassRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.BypassRegistry().Unregister(name)
|
registry.BypassRegistry().Unregister(req.Bypass)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
bypasses := c.Bypasses
|
bypasses := c.Bypasses
|
||||||
c.Bypasses = nil
|
c.Bypasses = nil
|
||||||
for _, s := range bypasses {
|
for _, s := range bypasses {
|
||||||
if s.Name == name {
|
if s.Name == req.Bypass {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Bypasses = append(c.Bypasses, s)
|
c.Bypasses = append(c.Bypasses, s)
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
parser "github.com/go-gost/x/config/parsing/chain"
|
parser "github.com/go-gost/x/config/parsing/chain"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
@ -38,26 +35,19 @@ func createChain(ctx *gin.Context) {
|
|||||||
var req createChainRequest
|
var req createChainRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "chain name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.ChainRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := parser.ParseChain(&req.Data, logger.Default())
|
v, err := parser.ParseChain(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := registry.ChainRegistry().Register(name, v); err != nil {
|
if err := registry.ChainRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,31 +92,29 @@ func updateChain(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Chain)
|
if !registry.ChainRegistry().IsRegistered(req.Chain) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ChainRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Chain
|
||||||
|
|
||||||
v, err := parser.ParseChain(&req.Data, logger.Default())
|
v, err := parser.ParseChain(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.ChainRegistry().Unregister(name)
|
registry.ChainRegistry().Unregister(req.Chain)
|
||||||
|
|
||||||
if err := registry.ChainRegistry().Register(name, v); err != nil {
|
if err := registry.ChainRegistry().Register(req.Chain, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Chains {
|
for i := range c.Chains {
|
||||||
if c.Chains[i].Name == name {
|
if c.Chains[i].Name == req.Chain {
|
||||||
c.Chains[i] = &req.Data
|
c.Chains[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -166,19 +154,17 @@ func deleteChain(ctx *gin.Context) {
|
|||||||
var req deleteChainRequest
|
var req deleteChainRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Chain)
|
if !registry.ChainRegistry().IsRegistered(req.Chain) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ChainRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.ChainRegistry().Unregister(name)
|
registry.ChainRegistry().Unregister(req.Chain)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
chains := c.Chains
|
chains := c.Chains
|
||||||
c.Chains = nil
|
c.Chains = nil
|
||||||
for _, s := range chains {
|
for _, s := range chains {
|
||||||
if s.Name == name {
|
if s.Name == req.Chain {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Chains = append(c.Chains, s)
|
c.Chains = append(c.Chains, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,21 +35,15 @@ func createConnLimiter(ctx *gin.Context) {
|
|||||||
var req createConnLimiterRequest
|
var req createConnLimiterRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.ConnLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseConnLimiter(&req.Data)
|
v := parser.ParseConnLimiter(&req.Data)
|
||||||
if err := registry.ConnLimiterRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create limmiter %s failed: %s", name, err.Error())))
|
if err := registry.ConnLimiterRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,27 +87,25 @@ func updateConnLimiter(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ConnLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Limiter
|
||||||
|
|
||||||
v := parser.ParseConnLimiter(&req.Data)
|
v := parser.ParseConnLimiter(&req.Data)
|
||||||
|
|
||||||
registry.ConnLimiterRegistry().Unregister(name)
|
registry.ConnLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
if err := registry.ConnLimiterRegistry().Register(name, v); err != nil {
|
if err := registry.ConnLimiterRegistry().Register(req.Limiter, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.CLimiters {
|
for i := range c.CLimiters {
|
||||||
if c.CLimiters[i].Name == name {
|
if c.CLimiters[i].Name == req.Limiter {
|
||||||
c.CLimiters[i] = &req.Data
|
c.CLimiters[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -155,19 +145,17 @@ func deleteConnLimiter(ctx *gin.Context) {
|
|||||||
var req deleteConnLimiterRequest
|
var req deleteConnLimiterRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ConnLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.ConnLimiterRegistry().Unregister(name)
|
registry.ConnLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
limiteres := c.CLimiters
|
limiteres := c.CLimiters
|
||||||
c.CLimiters = nil
|
c.CLimiters = nil
|
||||||
for _, s := range limiteres {
|
for _, s := range limiteres {
|
||||||
if s.Name == name {
|
if s.Name == req.Limiter {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.CLimiters = append(c.CLimiters, s)
|
c.CLimiters = append(c.CLimiters, s)
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
parser "github.com/go-gost/x/config/parsing/hop"
|
parser "github.com/go-gost/x/config/parsing/hop"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
@ -38,26 +35,19 @@ func createHop(ctx *gin.Context) {
|
|||||||
var req createHopRequest
|
var req createHopRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hop name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.HopRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := parser.ParseHop(&req.Data, logger.Default())
|
v, err := parser.ParseHop(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := registry.HopRegistry().Register(name, v); err != nil {
|
if err := registry.HopRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,30 +92,29 @@ func updateHop(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Hop)
|
if !registry.HopRegistry().IsRegistered(req.Hop) {
|
||||||
if !registry.HopRegistry().IsRegistered(name) {
|
writeError(ctx, ErrNotFound)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Hop
|
||||||
|
|
||||||
v, err := parser.ParseHop(&req.Data, logger.Default())
|
v, err := parser.ParseHop(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.HopRegistry().Unregister(name)
|
registry.HopRegistry().Unregister(req.Hop)
|
||||||
|
|
||||||
if err := registry.HopRegistry().Register(name, v); err != nil {
|
if err := registry.HopRegistry().Register(req.Hop, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Hops {
|
for i := range c.Hops {
|
||||||
if c.Hops[i].Name == name {
|
if c.Hops[i].Name == req.Hop {
|
||||||
c.Hops[i] = &req.Data
|
c.Hops[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -165,19 +154,17 @@ func deleteHop(ctx *gin.Context) {
|
|||||||
var req deleteHopRequest
|
var req deleteHopRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Hop)
|
if !registry.HopRegistry().IsRegistered(req.Hop) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.HopRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.HopRegistry().Unregister(name)
|
registry.HopRegistry().Unregister(req.Hop)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
hops := c.Hops
|
hops := c.Hops
|
||||||
c.Hops = nil
|
c.Hops = nil
|
||||||
for _, s := range hops {
|
for _, s := range hops {
|
||||||
if s.Name == name {
|
if s.Name == req.Hop {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Hops = append(c.Hops, s)
|
c.Hops = append(c.Hops, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createHosts(ctx *gin.Context) {
|
|||||||
var req createHostsRequest
|
var req createHostsRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hosts name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.HostsRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseHostMapper(&req.Data)
|
v := parser.ParseHostMapper(&req.Data)
|
||||||
|
|
||||||
if err := registry.HostsRegistry().Register(name, v); err != nil {
|
if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateHosts(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Hosts)
|
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.HostsRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Hosts
|
||||||
|
|
||||||
v := parser.ParseHostMapper(&req.Data)
|
v := parser.ParseHostMapper(&req.Data)
|
||||||
|
|
||||||
registry.HostsRegistry().Unregister(name)
|
registry.HostsRegistry().Unregister(req.Hosts)
|
||||||
|
|
||||||
if err := registry.HostsRegistry().Register(name, v); err != nil {
|
if err := registry.HostsRegistry().Register(req.Hosts, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Hosts {
|
for i := range c.Hosts {
|
||||||
if c.Hosts[i].Name == name {
|
if c.Hosts[i].Name == req.Hosts {
|
||||||
c.Hosts[i] = &req.Data
|
c.Hosts[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteHosts(ctx *gin.Context) {
|
|||||||
var req deleteHostsRequest
|
var req deleteHostsRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Hosts)
|
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.HostsRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.HostsRegistry().Unregister(name)
|
registry.HostsRegistry().Unregister(req.Hosts)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
hosts := c.Hosts
|
hosts := c.Hosts
|
||||||
c.Hosts = nil
|
c.Hosts = nil
|
||||||
for _, s := range hosts {
|
for _, s := range hosts {
|
||||||
if s.Name == name {
|
if s.Name == req.Hosts {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Hosts = append(c.Hosts, s)
|
c.Hosts = append(c.Hosts, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createIngress(ctx *gin.Context) {
|
|||||||
var req createIngressRequest
|
var req createIngressRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "ingress name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.IngressRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseIngress(&req.Data)
|
v := parser.ParseIngress(&req.Data)
|
||||||
|
|
||||||
if err := registry.IngressRegistry().Register(name, v); err != nil {
|
if err := registry.IngressRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateIngress(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Ingress)
|
if !registry.IngressRegistry().IsRegistered(req.Ingress) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.IngressRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Ingress
|
||||||
|
|
||||||
v := parser.ParseIngress(&req.Data)
|
v := parser.ParseIngress(&req.Data)
|
||||||
|
|
||||||
registry.IngressRegistry().Unregister(name)
|
registry.IngressRegistry().Unregister(req.Ingress)
|
||||||
|
|
||||||
if err := registry.IngressRegistry().Register(name, v); err != nil {
|
if err := registry.IngressRegistry().Register(req.Ingress, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Ingresses {
|
for i := range c.Ingresses {
|
||||||
if c.Ingresses[i].Name == name {
|
if c.Ingresses[i].Name == req.Ingress {
|
||||||
c.Ingresses[i] = &req.Data
|
c.Ingresses[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteIngress(ctx *gin.Context) {
|
|||||||
var req deleteIngressRequest
|
var req deleteIngressRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Ingress)
|
if !registry.IngressRegistry().IsRegistered(req.Ingress) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.IngressRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.IngressRegistry().Unregister(name)
|
registry.IngressRegistry().Unregister(req.Ingress)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
ingresses := c.Ingresses
|
ingresses := c.Ingresses
|
||||||
c.Ingresses = nil
|
c.Ingresses = nil
|
||||||
for _, s := range ingresses {
|
for _, s := range ingresses {
|
||||||
if s.Name == name {
|
if s.Name == req.Ingress {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Ingresses = append(c.Ingresses, s)
|
c.Ingresses = append(c.Ingresses, s)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createLimiter(ctx *gin.Context) {
|
|||||||
var req createLimiterRequest
|
var req createLimiterRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.TrafficLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseTrafficLimiter(&req.Data)
|
v := parser.ParseTrafficLimiter(&req.Data)
|
||||||
|
|
||||||
if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
|
if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateLimiter(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Limiter
|
||||||
|
|
||||||
v := parser.ParseTrafficLimiter(&req.Data)
|
v := parser.ParseTrafficLimiter(&req.Data)
|
||||||
|
|
||||||
registry.TrafficLimiterRegistry().Unregister(name)
|
registry.TrafficLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
|
if err := registry.TrafficLimiterRegistry().Register(req.Limiter, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Limiters {
|
for i := range c.Limiters {
|
||||||
if c.Limiters[i].Name == name {
|
if c.Limiters[i].Name == req.Limiter {
|
||||||
c.Limiters[i] = &req.Data
|
c.Limiters[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteLimiter(ctx *gin.Context) {
|
|||||||
var req deleteLimiterRequest
|
var req deleteLimiterRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.TrafficLimiterRegistry().Unregister(name)
|
registry.TrafficLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
limiteres := c.Limiters
|
limiteres := c.Limiters
|
||||||
c.Limiters = nil
|
c.Limiters = nil
|
||||||
for _, s := range limiteres {
|
for _, s := range limiteres {
|
||||||
if s.Name == name {
|
if s.Name == req.Limiter {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Limiters = append(c.Limiters, s)
|
c.Limiters = append(c.Limiters, s)
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
parser "github.com/go-gost/x/config/parsing/observer"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swagger:parameters createObserverRequest
|
|
||||||
type createObserverRequest struct {
|
|
||||||
// in: body
|
|
||||||
Data config.ObserverConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response createObserverResponse
|
|
||||||
type createObserverResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func createObserver(ctx *gin.Context) {
|
|
||||||
// swagger:route POST /config/observers Observer createObserverRequest
|
|
||||||
//
|
|
||||||
// Create a new observer, the name of the observer must be unique in observer list.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: createObserverResponse
|
|
||||||
|
|
||||||
var req createObserverRequest
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
|
||||||
if name == "" {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "observer name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.ObserverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v := parser.ParseObserver(&req.Data)
|
|
||||||
|
|
||||||
if err := registry.ObserverRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
c.Observers = append(c.Observers, &req.Data)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters updateObserverRequest
|
|
||||||
type updateObserverRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Observer string `uri:"observer" json:"observer"`
|
|
||||||
// in: body
|
|
||||||
Data config.ObserverConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response updateObserverResponse
|
|
||||||
type updateObserverResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateObserver(ctx *gin.Context) {
|
|
||||||
// swagger:route PUT /config/observers/{observer} Observer updateObserverRequest
|
|
||||||
//
|
|
||||||
// Update observer by name, the observer must already exist.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: updateObserverResponse
|
|
||||||
|
|
||||||
var req updateObserverRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Observer)
|
|
||||||
|
|
||||||
if !registry.ObserverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
v := parser.ParseObserver(&req.Data)
|
|
||||||
|
|
||||||
registry.ObserverRegistry().Unregister(name)
|
|
||||||
|
|
||||||
if err := registry.ObserverRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
for i := range c.Observers {
|
|
||||||
if c.Observers[i].Name == name {
|
|
||||||
c.Observers[i] = &req.Data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters deleteObserverRequest
|
|
||||||
type deleteObserverRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Observer string `uri:"observer" json:"observer"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response deleteObserverResponse
|
|
||||||
type deleteObserverResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteObserver(ctx *gin.Context) {
|
|
||||||
// swagger:route DELETE /config/observers/{observer} Observer deleteObserverRequest
|
|
||||||
//
|
|
||||||
// Delete observer by name.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: deleteObserverResponse
|
|
||||||
|
|
||||||
var req deleteObserverRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Observer)
|
|
||||||
|
|
||||||
if !registry.ObserverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
registry.ObserverRegistry().Unregister(name)
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
observers := c.Observers
|
|
||||||
c.Observers = nil
|
|
||||||
for _, s := range observers {
|
|
||||||
if s.Name == name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.Observers = append(c.Observers, s)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,22 +35,15 @@ func createRateLimiter(ctx *gin.Context) {
|
|||||||
var req createRateLimiterRequest
|
var req createRateLimiterRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.RateLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v := parser.ParseRateLimiter(&req.Data)
|
v := parser.ParseRateLimiter(&req.Data)
|
||||||
|
|
||||||
if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
|
if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,27 +87,25 @@ func updateRateLimiter(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.RateLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Limiter
|
||||||
|
|
||||||
v := parser.ParseRateLimiter(&req.Data)
|
v := parser.ParseRateLimiter(&req.Data)
|
||||||
|
|
||||||
registry.RateLimiterRegistry().Unregister(name)
|
registry.RateLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
|
if err := registry.RateLimiterRegistry().Register(req.Limiter, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.RLimiters {
|
for i := range c.RLimiters {
|
||||||
if c.RLimiters[i].Name == name {
|
if c.RLimiters[i].Name == req.Limiter {
|
||||||
c.RLimiters[i] = &req.Data
|
c.RLimiters[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -156,19 +145,17 @@ func deleteRateLimiter(ctx *gin.Context) {
|
|||||||
var req deleteRateLimiterRequest
|
var req deleteRateLimiterRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Limiter)
|
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.RateLimiterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.RateLimiterRegistry().Unregister(name)
|
registry.RateLimiterRegistry().Unregister(req.Limiter)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
limiteres := c.RLimiters
|
limiteres := c.RLimiters
|
||||||
c.RLimiters = nil
|
c.RLimiters = nil
|
||||||
for _, s := range limiteres {
|
for _, s := range limiteres {
|
||||||
if s.Name == name {
|
if s.Name == req.Limiter {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.RLimiters = append(c.RLimiters, s)
|
c.RLimiters = append(c.RLimiters, s)
|
||||||
|
@ -1,181 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
parser "github.com/go-gost/x/config/parsing/recorder"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swagger:parameters createRecorderRequest
|
|
||||||
type createRecorderRequest struct {
|
|
||||||
// in: body
|
|
||||||
Data config.RecorderConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response createRecorderResponse
|
|
||||||
type createRecorderResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRecorder(ctx *gin.Context) {
|
|
||||||
// swagger:route POST /config/recorders Recorder createRecorderRequest
|
|
||||||
//
|
|
||||||
// Create a new recorder, the name of the recorder must be unique in recorder list.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: createRecorderResponse
|
|
||||||
|
|
||||||
var req createRecorderRequest
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
|
||||||
if name == "" {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "recorder name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.RecorderRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v := parser.ParseRecorder(&req.Data)
|
|
||||||
|
|
||||||
if err := registry.RecorderRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
c.Recorders = append(c.Recorders, &req.Data)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters updateRecorderRequest
|
|
||||||
type updateRecorderRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Recorder string `uri:"recorder" json:"recorder"`
|
|
||||||
// in: body
|
|
||||||
Data config.RecorderConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response updateRecorderResponse
|
|
||||||
type updateRecorderResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateRecorder(ctx *gin.Context) {
|
|
||||||
// swagger:route PUT /config/recorders/{recorder} Recorder updateRecorderRequest
|
|
||||||
//
|
|
||||||
// Update recorder by name, the recorder must already exist.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: updateRecorderResponse
|
|
||||||
|
|
||||||
var req updateRecorderRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Recorder)
|
|
||||||
|
|
||||||
if !registry.RecorderRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
v := parser.ParseRecorder(&req.Data)
|
|
||||||
|
|
||||||
registry.RecorderRegistry().Unregister(name)
|
|
||||||
|
|
||||||
if err := registry.RecorderRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
for i := range c.Recorders {
|
|
||||||
if c.Recorders[i].Name == name {
|
|
||||||
c.Recorders[i] = &req.Data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters deleteRecorderRequest
|
|
||||||
type deleteRecorderRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Recorder string `uri:"recorder" json:"recorder"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response deleteRecorderResponse
|
|
||||||
type deleteRecorderResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteRecorder(ctx *gin.Context) {
|
|
||||||
// swagger:route DELETE /config/recorders/{recorder} Recorder deleteRecorderRequest
|
|
||||||
//
|
|
||||||
// Delete recorder by name.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: deleteRecorderResponse
|
|
||||||
|
|
||||||
var req deleteRecorderRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Recorder)
|
|
||||||
|
|
||||||
if !registry.RecorderRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
registry.RecorderRegistry().Unregister(name)
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
recorders := c.Recorders
|
|
||||||
c.Recorders = nil
|
|
||||||
for _, s := range recorders {
|
|
||||||
if s.Name == name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.Recorders = append(c.Recorders, s)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,26 +35,19 @@ func createResolver(ctx *gin.Context) {
|
|||||||
var req createResolverRequest
|
var req createResolverRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "resolver name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.ResolverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := parser.ParseResolver(&req.Data)
|
v, err := parser.ParseResolver(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := registry.ResolverRegistry().Register(name, v); err != nil {
|
if err := registry.ResolverRegistry().Register(req.Data.Name, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,31 +91,29 @@ func updateResolver(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Resolver)
|
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ResolverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Resolver
|
||||||
|
|
||||||
v, err := parser.ParseResolver(&req.Data)
|
v, err := parser.ParseResolver(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.ResolverRegistry().Unregister(name)
|
registry.ResolverRegistry().Unregister(req.Resolver)
|
||||||
|
|
||||||
if err := registry.ResolverRegistry().Register(name, v); err != nil {
|
if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Resolvers {
|
for i := range c.Resolvers {
|
||||||
if c.Resolvers[i].Name == name {
|
if c.Resolvers[i].Name == req.Resolver {
|
||||||
c.Resolvers[i] = &req.Data
|
c.Resolvers[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -164,19 +153,17 @@ func deleteResolver(ctx *gin.Context) {
|
|||||||
var req deleteResolverRequest
|
var req deleteResolverRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Resolver)
|
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
|
||||||
|
writeError(ctx, ErrNotFound)
|
||||||
if !registry.ResolverRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
registry.ResolverRegistry().Unregister(name)
|
registry.ResolverRegistry().Unregister(req.Resolver)
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
resolvers := c.Resolvers
|
resolvers := c.Resolvers
|
||||||
c.Resolvers = nil
|
c.Resolvers = nil
|
||||||
for _, s := range resolvers {
|
for _, s := range resolvers {
|
||||||
if s.Name == name {
|
if s.Name == req.Resolver {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Resolvers = append(c.Resolvers, s)
|
c.Resolvers = append(c.Resolvers, s)
|
||||||
|
@ -1,182 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
parser "github.com/go-gost/x/config/parsing/router"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swagger:parameters createRouterRequest
|
|
||||||
type createRouterRequest struct {
|
|
||||||
// in: body
|
|
||||||
Data config.RouterConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response createRouterResponse
|
|
||||||
type createRouterResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRouter(ctx *gin.Context) {
|
|
||||||
// swagger:route POST /config/routers Router createRouterRequest
|
|
||||||
//
|
|
||||||
// Create a new router, the name of the router must be unique in router list.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: createRouterResponse
|
|
||||||
|
|
||||||
var req createRouterRequest
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
|
||||||
if name == "" {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "router name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.RouterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v := parser.ParseRouter(&req.Data)
|
|
||||||
|
|
||||||
if err := registry.RouterRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
c.Routers = append(c.Routers, &req.Data)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters updateRouterRequest
|
|
||||||
type updateRouterRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Router string `uri:"router" json:"router"`
|
|
||||||
// in: body
|
|
||||||
Data config.RouterConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response updateRouterResponse
|
|
||||||
type updateRouterResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateRouter(ctx *gin.Context) {
|
|
||||||
// swagger:route PUT /config/routers/{router} Router updateRouterRequest
|
|
||||||
//
|
|
||||||
// Update router by name, the router must already exist.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: updateRouterResponse
|
|
||||||
|
|
||||||
var req updateRouterRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Router)
|
|
||||||
|
|
||||||
if !registry.RouterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
v := parser.ParseRouter(&req.Data)
|
|
||||||
|
|
||||||
registry.RouterRegistry().Unregister(name)
|
|
||||||
|
|
||||||
if err := registry.RouterRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
for i := range c.Routers {
|
|
||||||
if c.Routers[i].Name == name {
|
|
||||||
c.Routers[i] = &req.Data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters deleteRouterRequest
|
|
||||||
type deleteRouterRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
Router string `uri:"router" json:"router"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response deleteRouterResponse
|
|
||||||
type deleteRouterResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteRouter(ctx *gin.Context) {
|
|
||||||
// swagger:route DELETE /config/routers/{router} Router deleteRouterRequest
|
|
||||||
//
|
|
||||||
// Delete router by name.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: deleteRouterResponse
|
|
||||||
|
|
||||||
var req deleteRouterRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Router)
|
|
||||||
|
|
||||||
if !registry.RouterRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
registry.RouterRegistry().Unregister(name)
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
routeres := c.Routers
|
|
||||||
c.Routers = nil
|
|
||||||
for _, s := range routeres {
|
|
||||||
if s.Name == name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.Routers = append(c.Routers, s)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
182
api/config_sd.go
182
api/config_sd.go
@ -1,182 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
parser "github.com/go-gost/x/config/parsing/sd"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// swagger:parameters createSDRequest
|
|
||||||
type createSDRequest struct {
|
|
||||||
// in: body
|
|
||||||
Data config.SDConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response createSDResponse
|
|
||||||
type createSDResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSD(ctx *gin.Context) {
|
|
||||||
// swagger:route POST /config/sds SD createSDRequest
|
|
||||||
//
|
|
||||||
// Create a new SD, the name of the SD must be unique in SD list.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: createSDResponse
|
|
||||||
|
|
||||||
var req createSDRequest
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
|
||||||
if name == "" {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "sd name is required"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.SDRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v := parser.ParseSD(&req.Data)
|
|
||||||
|
|
||||||
if err := registry.SDRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
c.SDs = append(c.SDs, &req.Data)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters updateSDRequest
|
|
||||||
type updateSDRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
SD string `uri:"sd" json:"sd"`
|
|
||||||
// in: body
|
|
||||||
Data config.SDConfig `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response updateSDResponse
|
|
||||||
type updateSDResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateSD(ctx *gin.Context) {
|
|
||||||
// swagger:route PUT /config/sds/{sd} SD updateSDRequest
|
|
||||||
//
|
|
||||||
// Update SD by name, the SD must already exist.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: updateSDResponse
|
|
||||||
|
|
||||||
var req updateSDRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.SD)
|
|
||||||
|
|
||||||
if !registry.SDRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
v := parser.ParseSD(&req.Data)
|
|
||||||
|
|
||||||
registry.SDRegistry().Unregister(name)
|
|
||||||
|
|
||||||
if err := registry.SDRegistry().Register(name, v); err != nil {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
for i := range c.SDs {
|
|
||||||
if c.SDs[i].Name == name {
|
|
||||||
c.SDs[i] = &req.Data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:parameters deleteSDRequest
|
|
||||||
type deleteSDRequest struct {
|
|
||||||
// in: path
|
|
||||||
// required: true
|
|
||||||
SD string `uri:"sd" json:"sd"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// successful operation.
|
|
||||||
// swagger:response deleteSDResponse
|
|
||||||
type deleteSDResponse struct {
|
|
||||||
Data Response
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteSD(ctx *gin.Context) {
|
|
||||||
// swagger:route DELETE /config/sds/{sd} SD deleteSDRequest
|
|
||||||
//
|
|
||||||
// Delete SD by name.
|
|
||||||
//
|
|
||||||
// Security:
|
|
||||||
// basicAuth: []
|
|
||||||
//
|
|
||||||
// Responses:
|
|
||||||
// 200: deleteSDResponse
|
|
||||||
|
|
||||||
var req deleteSDRequest
|
|
||||||
ctx.ShouldBindUri(&req)
|
|
||||||
|
|
||||||
name := strings.TrimSpace(req.SD)
|
|
||||||
|
|
||||||
if !registry.SDRegistry().IsRegistered(name) {
|
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
registry.SDRegistry().Unregister(name)
|
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
|
||||||
sds := c.SDs
|
|
||||||
c.SDs = nil
|
|
||||||
for _, s := range sds {
|
|
||||||
if s.Name == name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.SDs = append(c.SDs, s)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, Response{
|
|
||||||
Msg: "OK",
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
@ -37,27 +35,25 @@ func createService(ctx *gin.Context) {
|
|||||||
var req createServiceRequest
|
var req createServiceRequest
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Data.Name)
|
if req.Data.Name == "" {
|
||||||
if name == "" {
|
writeError(ctx, ErrInvalid)
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "service name is required"))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Data.Name = name
|
|
||||||
|
|
||||||
if registry.ServiceRegistry().IsRegistered(name) {
|
if registry.ServiceRegistry().IsRegistered(req.Data.Name) {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
svc, err := parser.ParseService(&req.Data)
|
svc, err := parser.ParseService(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := registry.ServiceRegistry().Register(name, svc); err != nil {
|
if err := registry.ServiceRegistry().Register(req.Data.Name, svc); err != nil {
|
||||||
svc.Close()
|
svc.Close()
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,28 +99,26 @@ func updateService(ctx *gin.Context) {
|
|||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
ctx.ShouldBindJSON(&req.Data)
|
ctx.ShouldBindJSON(&req.Data)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Service)
|
old := registry.ServiceRegistry().Get(req.Service)
|
||||||
|
|
||||||
old := registry.ServiceRegistry().Get(name)
|
|
||||||
if old == nil {
|
if old == nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name)))
|
writeError(ctx, ErrNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
old.Close()
|
old.Close()
|
||||||
|
|
||||||
req.Data.Name = name
|
req.Data.Name = req.Service
|
||||||
|
|
||||||
svc, err := parser.ParseService(&req.Data)
|
svc, err := parser.ParseService(&req.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", name, err.Error())))
|
writeError(ctx, ErrCreate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.ServiceRegistry().Unregister(name)
|
registry.ServiceRegistry().Unregister(req.Service)
|
||||||
|
|
||||||
if err := registry.ServiceRegistry().Register(name, svc); err != nil {
|
if err := registry.ServiceRegistry().Register(req.Service, svc); err != nil {
|
||||||
svc.Close()
|
svc.Close()
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
|
writeError(ctx, ErrDup)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +126,7 @@ func updateService(ctx *gin.Context) {
|
|||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
for i := range c.Services {
|
for i := range c.Services {
|
||||||
if c.Services[i].Name == name {
|
if c.Services[i].Name == req.Service {
|
||||||
c.Services[i] = &req.Data
|
c.Services[i] = &req.Data
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -172,22 +166,20 @@ func deleteService(ctx *gin.Context) {
|
|||||||
var req deleteServiceRequest
|
var req deleteServiceRequest
|
||||||
ctx.ShouldBindUri(&req)
|
ctx.ShouldBindUri(&req)
|
||||||
|
|
||||||
name := strings.TrimSpace(req.Service)
|
svc := registry.ServiceRegistry().Get(req.Service)
|
||||||
|
|
||||||
svc := registry.ServiceRegistry().Get(name)
|
|
||||||
if svc == nil {
|
if svc == nil {
|
||||||
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name)))
|
writeError(ctx, ErrNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
registry.ServiceRegistry().Unregister(name)
|
registry.ServiceRegistry().Unregister(req.Service)
|
||||||
svc.Close()
|
svc.Close()
|
||||||
|
|
||||||
config.OnUpdate(func(c *config.Config) error {
|
config.OnUpdate(func(c *config.Config) error {
|
||||||
services := c.Services
|
services := c.Services
|
||||||
c.Services = nil
|
c.Services = nil
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
if s.Name == name {
|
if s.Name == req.Service {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Services = append(c.Services, s)
|
c.Services = append(c.Services, s)
|
||||||
|
18
api/error.go
18
api/error.go
@ -15,16 +15,6 @@ var (
|
|||||||
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
|
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrCode int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ErrCodeInvalid = 40001
|
|
||||||
ErrCodeDup = 40002
|
|
||||||
ErrCodeFailed = 40003
|
|
||||||
ErrCodeNotFound = 40004
|
|
||||||
ErrCodeSaveConfigFailed = 40005
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error is an api error.
|
// Error is an api error.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
@ -32,14 +22,6 @@ type Error struct {
|
|||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewError(status, code int, msg string) error {
|
|
||||||
return &Error{
|
|
||||||
statusCode: status,
|
|
||||||
Code: code,
|
|
||||||
Msg: msg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
b, _ := json.Marshal(e)
|
b, _ := json.Marshal(e)
|
||||||
return string(b)
|
return string(b)
|
||||||
|
@ -36,12 +36,7 @@ func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
u, p, _ := c.Request.BasicAuth()
|
u, p, _ := c.Request.BasicAuth()
|
||||||
if _, ok := auther.Authenticate(c, u, p); !ok {
|
if _, ok := auther.Authenticate(c, u, p); !ok {
|
||||||
c.Writer.Header().Set("WWW-Authenticate", "Basic")
|
c.AbortWithStatus(http.StatusUnauthorized)
|
||||||
c.JSON(http.StatusUnauthorized, Response{
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
Msg: "Unauthorized",
|
|
||||||
})
|
|
||||||
c.Abort()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
152
api/service.go
Normal file
152
api/service.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-gost/core/auth"
|
||||||
|
"github.com/go-gost/core/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(addr string, opts ...Option) (service.Service, error) {
|
||||||
|
ln, err := net.Listen("tcp", 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(
|
||||||
|
cors.New((cors.Config{
|
||||||
|
AllowAllOrigins: true,
|
||||||
|
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||||
|
AllowHeaders: []string{"*"},
|
||||||
|
})),
|
||||||
|
gin.Recovery(),
|
||||||
|
)
|
||||||
|
if options.accessLog {
|
||||||
|
r.Use(mwLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
router := r.Group("")
|
||||||
|
if options.pathPrefix != "" {
|
||||||
|
router = router.Group(options.pathPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.StaticFS("/docs", http.FS(swaggerDoc))
|
||||||
|
|
||||||
|
config := router.Group("/config")
|
||||||
|
config.Use(mwBasicAuth(options.auther))
|
||||||
|
registerConfig(config)
|
||||||
|
|
||||||
|
return &server{
|
||||||
|
s: &http.Server{
|
||||||
|
Handler: r,
|
||||||
|
},
|
||||||
|
ln: ln,
|
||||||
|
}, 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 registerConfig(config *gin.RouterGroup) {
|
||||||
|
config.GET("", getConfig)
|
||||||
|
config.POST("", saveConfig)
|
||||||
|
|
||||||
|
config.POST("/services", createService)
|
||||||
|
config.PUT("/services/:service", updateService)
|
||||||
|
config.DELETE("/services/:service", deleteService)
|
||||||
|
|
||||||
|
config.POST("/chains", createChain)
|
||||||
|
config.PUT("/chains/:chain", updateChain)
|
||||||
|
config.DELETE("/chains/:chain", deleteChain)
|
||||||
|
|
||||||
|
config.POST("/hops", createHop)
|
||||||
|
config.PUT("/hops/:hop", updateHop)
|
||||||
|
config.DELETE("/hops/:hop", deleteHop)
|
||||||
|
|
||||||
|
config.POST("/authers", createAuther)
|
||||||
|
config.PUT("/authers/:auther", updateAuther)
|
||||||
|
config.DELETE("/authers/:auther", deleteAuther)
|
||||||
|
|
||||||
|
config.POST("/admissions", createAdmission)
|
||||||
|
config.PUT("/admissions/:admission", updateAdmission)
|
||||||
|
config.DELETE("/admissions/:admission", deleteAdmission)
|
||||||
|
|
||||||
|
config.POST("/bypasses", createBypass)
|
||||||
|
config.PUT("/bypasses/:bypass", updateBypass)
|
||||||
|
config.DELETE("/bypasses/:bypass", deleteBypass)
|
||||||
|
|
||||||
|
config.POST("/resolvers", createResolver)
|
||||||
|
config.PUT("/resolvers/:resolver", updateResolver)
|
||||||
|
config.DELETE("/resolvers/:resolver", deleteResolver)
|
||||||
|
|
||||||
|
config.POST("/hosts", createHosts)
|
||||||
|
config.PUT("/hosts/:hosts", updateHosts)
|
||||||
|
config.DELETE("/hosts/:hosts", deleteHosts)
|
||||||
|
|
||||||
|
config.POST("/ingresses", createIngress)
|
||||||
|
config.PUT("/ingresses/:ingress", updateIngress)
|
||||||
|
config.DELETE("/ingresses/:ingress", deleteIngress)
|
||||||
|
|
||||||
|
config.POST("/limiters", createLimiter)
|
||||||
|
config.PUT("/limiters/:limiter", updateLimiter)
|
||||||
|
config.DELETE("/limiters/:limiter", deleteLimiter)
|
||||||
|
|
||||||
|
config.POST("/climiters", createConnLimiter)
|
||||||
|
config.PUT("/climiters/:limiter", updateConnLimiter)
|
||||||
|
config.DELETE("/climiters/:limiter", deleteConnLimiter)
|
||||||
|
|
||||||
|
config.POST("/rlimiters", createRateLimiter)
|
||||||
|
config.PUT("/rlimiters/:limiter", updateRateLimiter)
|
||||||
|
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
|
||||||
|
}
|
657
api/swagger.yaml
657
api/swagger.yaml
@ -34,8 +34,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -73,8 +71,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -95,8 +91,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -113,6 +107,9 @@ definitions:
|
|||||||
ChainConfig:
|
ChainConfig:
|
||||||
properties:
|
properties:
|
||||||
hops:
|
hops:
|
||||||
|
description: |-
|
||||||
|
REMOVED since beta.6
|
||||||
|
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/HopConfig'
|
$ref: '#/definitions/HopConfig'
|
||||||
type: array
|
type: array
|
||||||
@ -188,18 +185,8 @@ definitions:
|
|||||||
x-go-name: Limiters
|
x-go-name: Limiters
|
||||||
log:
|
log:
|
||||||
$ref: '#/definitions/LogConfig'
|
$ref: '#/definitions/LogConfig'
|
||||||
loggers:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/LoggerConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: Loggers
|
|
||||||
metrics:
|
metrics:
|
||||||
$ref: '#/definitions/MetricsConfig'
|
$ref: '#/definitions/MetricsConfig'
|
||||||
observers:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/ObserverConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: Observers
|
|
||||||
profiling:
|
profiling:
|
||||||
$ref: '#/definitions/ProfilingConfig'
|
$ref: '#/definitions/ProfilingConfig'
|
||||||
recorders:
|
recorders:
|
||||||
@ -217,16 +204,6 @@ definitions:
|
|||||||
$ref: '#/definitions/LimiterConfig'
|
$ref: '#/definitions/LimiterConfig'
|
||||||
type: array
|
type: array
|
||||||
x-go-name: RLimiters
|
x-go-name: RLimiters
|
||||||
routers:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RouterConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: Routers
|
|
||||||
sds:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/SDConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: SDs
|
|
||||||
services:
|
services:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/ServiceConfig'
|
$ref: '#/definitions/ServiceConfig'
|
||||||
@ -296,8 +273,6 @@ definitions:
|
|||||||
addr:
|
addr:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Addr
|
x-go-name: Addr
|
||||||
auth:
|
|
||||||
$ref: '#/definitions/AuthConfig'
|
|
||||||
bypass:
|
bypass:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Bypass
|
x-go-name: Bypass
|
||||||
@ -306,44 +281,20 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
x-go-name: Bypasses
|
x-go-name: Bypasses
|
||||||
filter:
|
|
||||||
$ref: '#/definitions/NodeFilterConfig'
|
|
||||||
host:
|
host:
|
||||||
description: DEPRECATED by filter.host
|
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Host
|
x-go-name: Host
|
||||||
http:
|
|
||||||
$ref: '#/definitions/HTTPNodeConfig'
|
|
||||||
metadata:
|
|
||||||
additionalProperties: {}
|
|
||||||
type: object
|
|
||||||
x-go-name: Metadata
|
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
network:
|
|
||||||
type: string
|
|
||||||
x-go-name: Network
|
|
||||||
path:
|
|
||||||
description: DEPRECATED by filter.path
|
|
||||||
type: string
|
|
||||||
x-go-name: Path
|
|
||||||
protocol:
|
protocol:
|
||||||
description: DEPRECATED by filter.protocol
|
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Protocol
|
x-go-name: Protocol
|
||||||
tls:
|
|
||||||
$ref: '#/definitions/TLSNodeConfig'
|
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
ForwarderConfig:
|
ForwarderConfig:
|
||||||
properties:
|
properties:
|
||||||
hop:
|
|
||||||
description: the referenced hop name
|
|
||||||
type: string
|
|
||||||
x-go-name: Hop
|
|
||||||
name:
|
name:
|
||||||
description: DEPRECATED by hop field
|
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
nodes:
|
nodes:
|
||||||
@ -353,6 +304,12 @@ definitions:
|
|||||||
x-go-name: Nodes
|
x-go-name: Nodes
|
||||||
selector:
|
selector:
|
||||||
$ref: '#/definitions/SelectorConfig'
|
$ref: '#/definitions/SelectorConfig'
|
||||||
|
targets:
|
||||||
|
description: DEPRECATED by nodes since beta.4
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
x-go-name: Targets
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
HTTPLoader:
|
HTTPLoader:
|
||||||
@ -364,42 +321,6 @@ definitions:
|
|||||||
x-go-name: URL
|
x-go-name: URL
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
HTTPNodeConfig:
|
|
||||||
properties:
|
|
||||||
auth:
|
|
||||||
$ref: '#/definitions/AuthConfig'
|
|
||||||
header:
|
|
||||||
additionalProperties:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
x-go-name: Header
|
|
||||||
host:
|
|
||||||
type: string
|
|
||||||
x-go-name: Host
|
|
||||||
rewrite:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/HTTPURLRewriteConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: Rewrite
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
HTTPRecorder:
|
|
||||||
properties:
|
|
||||||
timeout:
|
|
||||||
$ref: '#/definitions/Duration'
|
|
||||||
url:
|
|
||||||
type: string
|
|
||||||
x-go-name: URL
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
HTTPURLRewriteConfig:
|
|
||||||
properties:
|
|
||||||
Match:
|
|
||||||
type: string
|
|
||||||
Replacement:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
HandlerConfig:
|
HandlerConfig:
|
||||||
properties:
|
properties:
|
||||||
auth:
|
auth:
|
||||||
@ -417,16 +338,13 @@ definitions:
|
|||||||
x-go-name: Chain
|
x-go-name: Chain
|
||||||
chainGroup:
|
chainGroup:
|
||||||
$ref: '#/definitions/ChainGroupConfig'
|
$ref: '#/definitions/ChainGroupConfig'
|
||||||
limiter:
|
ingress:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Limiter
|
x-go-name: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
type: object
|
type: object
|
||||||
x-go-name: Metadata
|
x-go-name: Metadata
|
||||||
observer:
|
|
||||||
type: string
|
|
||||||
x-go-name: Observer
|
|
||||||
retries:
|
retries:
|
||||||
format: int64
|
format: int64
|
||||||
type: integer
|
type: integer
|
||||||
@ -448,13 +366,9 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
type: array
|
type: array
|
||||||
x-go-name: Bypasses
|
x-go-name: Bypasses
|
||||||
file:
|
|
||||||
$ref: '#/definitions/FileLoader'
|
|
||||||
hosts:
|
hosts:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Hosts
|
x-go-name: Hosts
|
||||||
http:
|
|
||||||
$ref: '#/definitions/HTTPLoader'
|
|
||||||
interface:
|
interface:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Interface
|
x-go-name: Interface
|
||||||
@ -466,12 +380,6 @@ definitions:
|
|||||||
$ref: '#/definitions/NodeConfig'
|
$ref: '#/definitions/NodeConfig'
|
||||||
type: array
|
type: array
|
||||||
x-go-name: Nodes
|
x-go-name: Nodes
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
|
||||||
$ref: '#/definitions/RedisLoader'
|
|
||||||
reload:
|
|
||||||
$ref: '#/definitions/Duration'
|
|
||||||
resolver:
|
resolver:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Resolver
|
x-go-name: Resolver
|
||||||
@ -510,8 +418,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -527,8 +433,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -564,8 +468,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisLoader'
|
$ref: '#/definitions/RedisLoader'
|
||||||
reload:
|
reload:
|
||||||
@ -657,25 +559,11 @@ definitions:
|
|||||||
x-go-name: MaxSize
|
x-go-name: MaxSize
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
LoggerConfig:
|
|
||||||
properties:
|
|
||||||
log:
|
|
||||||
$ref: '#/definitions/LogConfig'
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
x-go-name: Name
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
MetricsConfig:
|
MetricsConfig:
|
||||||
properties:
|
properties:
|
||||||
addr:
|
addr:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Addr
|
x-go-name: Addr
|
||||||
auth:
|
|
||||||
$ref: '#/definitions/AuthConfig'
|
|
||||||
auther:
|
|
||||||
type: string
|
|
||||||
x-go-name: Auther
|
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Path
|
x-go-name: Path
|
||||||
@ -686,9 +574,6 @@ definitions:
|
|||||||
addr:
|
addr:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Addr
|
x-go-name: Addr
|
||||||
async:
|
|
||||||
type: boolean
|
|
||||||
x-go-name: Async
|
|
||||||
chain:
|
chain:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Chain
|
x-go-name: Chain
|
||||||
@ -698,9 +583,6 @@ definitions:
|
|||||||
hostname:
|
hostname:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Hostname
|
x-go-name: Hostname
|
||||||
only:
|
|
||||||
type: string
|
|
||||||
x-go-name: Only
|
|
||||||
prefer:
|
prefer:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Prefer
|
x-go-name: Prefer
|
||||||
@ -727,13 +609,12 @@ definitions:
|
|||||||
$ref: '#/definitions/ConnectorConfig'
|
$ref: '#/definitions/ConnectorConfig'
|
||||||
dialer:
|
dialer:
|
||||||
$ref: '#/definitions/DialerConfig'
|
$ref: '#/definitions/DialerConfig'
|
||||||
filter:
|
host:
|
||||||
$ref: '#/definitions/NodeFilterConfig'
|
type: string
|
||||||
|
x-go-name: Host
|
||||||
hosts:
|
hosts:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Hosts
|
x-go-name: Hosts
|
||||||
http:
|
|
||||||
$ref: '#/definitions/HTTPNodeConfig'
|
|
||||||
interface:
|
interface:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Interface
|
x-go-name: Interface
|
||||||
@ -744,55 +625,14 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
network:
|
protocol:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Network
|
x-go-name: Protocol
|
||||||
resolver:
|
resolver:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Resolver
|
x-go-name: Resolver
|
||||||
sockopts:
|
sockopts:
|
||||||
$ref: '#/definitions/SockOptsConfig'
|
$ref: '#/definitions/SockOptsConfig'
|
||||||
tls:
|
|
||||||
$ref: '#/definitions/TLSNodeConfig'
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
NodeFilterConfig:
|
|
||||||
properties:
|
|
||||||
host:
|
|
||||||
type: string
|
|
||||||
x-go-name: Host
|
|
||||||
path:
|
|
||||||
type: string
|
|
||||||
x-go-name: Path
|
|
||||||
protocol:
|
|
||||||
type: string
|
|
||||||
x-go-name: Protocol
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
ObserverConfig:
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
x-go-name: Name
|
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
PluginConfig:
|
|
||||||
properties:
|
|
||||||
addr:
|
|
||||||
type: string
|
|
||||||
x-go-name: Addr
|
|
||||||
timeout:
|
|
||||||
$ref: '#/definitions/Duration'
|
|
||||||
tls:
|
|
||||||
$ref: '#/definitions/TLSConfig'
|
|
||||||
token:
|
|
||||||
type: string
|
|
||||||
x-go-name: Token
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
x-go-name: Type
|
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
ProfilingConfig:
|
ProfilingConfig:
|
||||||
@ -806,24 +646,15 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
file:
|
file:
|
||||||
$ref: '#/definitions/FileRecorder'
|
$ref: '#/definitions/FileRecorder'
|
||||||
http:
|
|
||||||
$ref: '#/definitions/HTTPRecorder'
|
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
redis:
|
||||||
$ref: '#/definitions/RedisRecorder'
|
$ref: '#/definitions/RedisRecorder'
|
||||||
tcp:
|
|
||||||
$ref: '#/definitions/TCPRecorder'
|
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
RecorderObject:
|
RecorderObject:
|
||||||
properties:
|
properties:
|
||||||
Metadata:
|
|
||||||
additionalProperties: {}
|
|
||||||
type: object
|
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
@ -882,8 +713,6 @@ definitions:
|
|||||||
$ref: '#/definitions/NameserverConfig'
|
$ref: '#/definitions/NameserverConfig'
|
||||||
type: array
|
type: array
|
||||||
x-go-name: Nameservers
|
x-go-name: Nameservers
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
Response:
|
Response:
|
||||||
@ -897,47 +726,6 @@ definitions:
|
|||||||
x-go-name: Msg
|
x-go-name: Msg
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/api
|
x-go-package: github.com/go-gost/x/api
|
||||||
RouterConfig:
|
|
||||||
properties:
|
|
||||||
file:
|
|
||||||
$ref: '#/definitions/FileLoader'
|
|
||||||
http:
|
|
||||||
$ref: '#/definitions/HTTPLoader'
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
x-go-name: Name
|
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
redis:
|
|
||||||
$ref: '#/definitions/RedisLoader'
|
|
||||||
reload:
|
|
||||||
$ref: '#/definitions/Duration'
|
|
||||||
routes:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/RouterRouteConfig'
|
|
||||||
type: array
|
|
||||||
x-go-name: Routes
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
RouterRouteConfig:
|
|
||||||
properties:
|
|
||||||
gateway:
|
|
||||||
type: string
|
|
||||||
x-go-name: Gateway
|
|
||||||
net:
|
|
||||||
type: string
|
|
||||||
x-go-name: Net
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
SDConfig:
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
x-go-name: Name
|
|
||||||
plugin:
|
|
||||||
$ref: '#/definitions/PluginConfig'
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
SelectorConfig:
|
SelectorConfig:
|
||||||
properties:
|
properties:
|
||||||
failTimeout:
|
failTimeout:
|
||||||
@ -991,14 +779,6 @@ definitions:
|
|||||||
x-go-name: Limiter
|
x-go-name: Limiter
|
||||||
listener:
|
listener:
|
||||||
$ref: '#/definitions/ListenerConfig'
|
$ref: '#/definitions/ListenerConfig'
|
||||||
logger:
|
|
||||||
type: string
|
|
||||||
x-go-name: Logger
|
|
||||||
loggers:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
x-go-name: Loggers
|
|
||||||
metadata:
|
metadata:
|
||||||
additionalProperties: {}
|
additionalProperties: {}
|
||||||
type: object
|
type: object
|
||||||
@ -1006,9 +786,6 @@ definitions:
|
|||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Name
|
x-go-name: Name
|
||||||
observer:
|
|
||||||
type: string
|
|
||||||
x-go-name: Observer
|
|
||||||
recorders:
|
recorders:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/RecorderObject'
|
$ref: '#/definitions/RecorderObject'
|
||||||
@ -1022,61 +799,6 @@ definitions:
|
|||||||
x-go-name: RLimiter
|
x-go-name: RLimiter
|
||||||
sockopts:
|
sockopts:
|
||||||
$ref: '#/definitions/SockOptsConfig'
|
$ref: '#/definitions/SockOptsConfig'
|
||||||
status:
|
|
||||||
$ref: '#/definitions/ServiceStatus'
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
ServiceEvent:
|
|
||||||
properties:
|
|
||||||
msg:
|
|
||||||
type: string
|
|
||||||
x-go-name: Msg
|
|
||||||
time:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
x-go-name: Time
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
ServiceStats:
|
|
||||||
properties:
|
|
||||||
currentConns:
|
|
||||||
format: uint64
|
|
||||||
type: integer
|
|
||||||
x-go-name: CurrentConns
|
|
||||||
inputBytes:
|
|
||||||
format: uint64
|
|
||||||
type: integer
|
|
||||||
x-go-name: InputBytes
|
|
||||||
outputBytes:
|
|
||||||
format: uint64
|
|
||||||
type: integer
|
|
||||||
x-go-name: OutputBytes
|
|
||||||
totalConns:
|
|
||||||
format: uint64
|
|
||||||
type: integer
|
|
||||||
x-go-name: TotalConns
|
|
||||||
totalErrs:
|
|
||||||
format: uint64
|
|
||||||
type: integer
|
|
||||||
x-go-name: TotalErrs
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
ServiceStatus:
|
|
||||||
properties:
|
|
||||||
createTime:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
x-go-name: CreateTime
|
|
||||||
events:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/ServiceEvent'
|
|
||||||
type: array
|
|
||||||
x-go-name: Events
|
|
||||||
state:
|
|
||||||
type: string
|
|
||||||
x-go-name: State
|
|
||||||
stats:
|
|
||||||
$ref: '#/definitions/ServiceStats'
|
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
SockOptsConfig:
|
SockOptsConfig:
|
||||||
@ -1087,15 +809,6 @@ definitions:
|
|||||||
x-go-name: Mark
|
x-go-name: Mark
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
TCPRecorder:
|
|
||||||
properties:
|
|
||||||
addr:
|
|
||||||
type: string
|
|
||||||
x-go-name: Addr
|
|
||||||
timeout:
|
|
||||||
$ref: '#/definitions/Duration'
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
TLSConfig:
|
TLSConfig:
|
||||||
properties:
|
properties:
|
||||||
caFile:
|
caFile:
|
||||||
@ -1110,8 +823,6 @@ definitions:
|
|||||||
keyFile:
|
keyFile:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: KeyFile
|
x-go-name: KeyFile
|
||||||
options:
|
|
||||||
$ref: '#/definitions/TLSOptions'
|
|
||||||
organization:
|
organization:
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Organization
|
x-go-name: Organization
|
||||||
@ -1125,33 +836,6 @@ definitions:
|
|||||||
$ref: '#/definitions/Duration'
|
$ref: '#/definitions/Duration'
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/go-gost/x/config
|
x-go-package: github.com/go-gost/x/config
|
||||||
TLSNodeConfig:
|
|
||||||
properties:
|
|
||||||
options:
|
|
||||||
$ref: '#/definitions/TLSOptions'
|
|
||||||
secure:
|
|
||||||
type: boolean
|
|
||||||
x-go-name: Secure
|
|
||||||
serverName:
|
|
||||||
type: string
|
|
||||||
x-go-name: ServerName
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
TLSOptions:
|
|
||||||
properties:
|
|
||||||
cipherSuites:
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
type: array
|
|
||||||
x-go-name: CipherSuites
|
|
||||||
maxVersion:
|
|
||||||
type: string
|
|
||||||
x-go-name: MaxVersion
|
|
||||||
minVersion:
|
|
||||||
type: string
|
|
||||||
x-go-name: MinVersion
|
|
||||||
type: object
|
|
||||||
x-go-package: github.com/go-gost/x/config
|
|
||||||
info:
|
info:
|
||||||
title: Documentation of Web API.
|
title: Documentation of Web API.
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@ -1182,11 +866,6 @@ paths:
|
|||||||
name: format
|
name: format
|
||||||
type: string
|
type: string
|
||||||
x-go-name: Format
|
x-go-name: Format
|
||||||
- description: file path, default is gost.yaml|gost.json in current working directory.
|
|
||||||
in: query
|
|
||||||
name: path
|
|
||||||
type: string
|
|
||||||
x-go-name: Path
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
$ref: '#/responses/saveConfigResponse'
|
$ref: '#/responses/saveConfigResponse'
|
||||||
@ -1718,122 +1397,6 @@ paths:
|
|||||||
summary: Update limiter by name, the limiter must already exist.
|
summary: Update limiter by name, the limiter must already exist.
|
||||||
tags:
|
tags:
|
||||||
- Limiter
|
- Limiter
|
||||||
/config/observers:
|
|
||||||
post:
|
|
||||||
operationId: createObserverRequest
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/ObserverConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/createObserverResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Create a new observer, the name of the observer must be unique in observer list.
|
|
||||||
tags:
|
|
||||||
- Observer
|
|
||||||
/config/observers/{observer}:
|
|
||||||
delete:
|
|
||||||
operationId: deleteObserverRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: observer
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Observer
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/deleteObserverResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Delete observer by name.
|
|
||||||
tags:
|
|
||||||
- Observer
|
|
||||||
put:
|
|
||||||
operationId: updateObserverRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: observer
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Observer
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/ObserverConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/updateObserverResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Update observer by name, the observer must already exist.
|
|
||||||
tags:
|
|
||||||
- Observer
|
|
||||||
/config/recorders:
|
|
||||||
post:
|
|
||||||
operationId: createRecorderRequest
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RecorderConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/createRecorderResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Create a new recorder, the name of the recorder must be unique in recorder list.
|
|
||||||
tags:
|
|
||||||
- Recorder
|
|
||||||
/config/recorders/{recorder}:
|
|
||||||
delete:
|
|
||||||
operationId: deleteRecorderRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: recorder
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Recorder
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/deleteRecorderResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Delete recorder by name.
|
|
||||||
tags:
|
|
||||||
- Recorder
|
|
||||||
put:
|
|
||||||
operationId: updateRecorderRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: recorder
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Recorder
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RecorderConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/updateRecorderResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Update recorder by name, the recorder must already exist.
|
|
||||||
tags:
|
|
||||||
- Recorder
|
|
||||||
/config/resolvers:
|
/config/resolvers:
|
||||||
post:
|
post:
|
||||||
operationId: createResolverRequest
|
operationId: createResolverRequest
|
||||||
@ -1950,122 +1513,6 @@ paths:
|
|||||||
summary: Update rate limiter by name, the limiter must already exist.
|
summary: Update rate limiter by name, the limiter must already exist.
|
||||||
tags:
|
tags:
|
||||||
- Limiter
|
- Limiter
|
||||||
/config/routers:
|
|
||||||
post:
|
|
||||||
operationId: createRouterRequest
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RouterConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/createRouterResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Create a new router, the name of the router must be unique in router list.
|
|
||||||
tags:
|
|
||||||
- Router
|
|
||||||
/config/routers/{router}:
|
|
||||||
delete:
|
|
||||||
operationId: deleteRouterRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: router
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Router
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/deleteRouterResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Delete router by name.
|
|
||||||
tags:
|
|
||||||
- Router
|
|
||||||
put:
|
|
||||||
operationId: updateRouterRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: router
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: Router
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/RouterConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/updateRouterResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Update router by name, the router must already exist.
|
|
||||||
tags:
|
|
||||||
- Router
|
|
||||||
/config/sds:
|
|
||||||
post:
|
|
||||||
operationId: createSDRequest
|
|
||||||
parameters:
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/SDConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/createSDResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Create a new SD, the name of the SD must be unique in SD list.
|
|
||||||
tags:
|
|
||||||
- SD
|
|
||||||
/config/sds/{sd}:
|
|
||||||
delete:
|
|
||||||
operationId: deleteSDRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: sd
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: SD
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/deleteSDResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Delete SD by name.
|
|
||||||
tags:
|
|
||||||
- SD
|
|
||||||
put:
|
|
||||||
operationId: updateSDRequest
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: sd
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
x-go-name: SD
|
|
||||||
- in: body
|
|
||||||
name: data
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/SDConfig'
|
|
||||||
x-go-name: Data
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
$ref: '#/responses/updateSDResponse'
|
|
||||||
security:
|
|
||||||
- basicAuth:
|
|
||||||
- '[]'
|
|
||||||
summary: Update SD by name, the SD must already exist.
|
|
||||||
tags:
|
|
||||||
- SD
|
|
||||||
/config/services:
|
/config/services:
|
||||||
post:
|
post:
|
||||||
operationId: createServiceRequest
|
operationId: createServiceRequest
|
||||||
@ -2181,42 +1628,18 @@ responses:
|
|||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
createObserverResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
createRateLimiterResponse:
|
createRateLimiterResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
createRecorderResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
createResolverResponse:
|
createResolverResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
createRouterResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
createSDResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
createServiceResponse:
|
createServiceResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
@ -2277,42 +1700,18 @@ responses:
|
|||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
deleteObserverResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
deleteRateLimiterResponse:
|
deleteRateLimiterResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
deleteRecorderResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
deleteResolverResponse:
|
deleteResolverResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
deleteRouterResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
deleteSDResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
deleteServiceResponse:
|
deleteServiceResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
@ -2385,42 +1784,18 @@ responses:
|
|||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
updateObserverResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
updateRateLimiterResponse:
|
updateRateLimiterResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
updateRecorderResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
updateResolverResponse:
|
updateResolverResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
Data: {}
|
Data: {}
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
updateRouterResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
updateSDResponse:
|
|
||||||
description: successful operation.
|
|
||||||
headers:
|
|
||||||
Data: {}
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/Response'
|
|
||||||
updateServiceResponse:
|
updateServiceResponse:
|
||||||
description: successful operation.
|
description: successful operation.
|
||||||
headers:
|
headers:
|
||||||
|
@ -110,7 +110,7 @@ func (p *authenticator) Authenticate(ctx context.Context, user, password string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
v, ok := p.kvs[user]
|
v, ok := p.kvs[user]
|
||||||
return user, ok && (v == "" || password == v)
|
return "", ok && (v == "" || password == v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *authenticator) periodReload(ctx context.Context) error {
|
func (p *authenticator) periodReload(ctx context.Context) error {
|
||||||
@ -145,8 +145,6 @@ func (p *authenticator) reload(ctx context.Context) (err error) {
|
|||||||
kvs[k] = v
|
kvs[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
p.options.logger.Debugf("load items %d", len(m))
|
|
||||||
|
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
@ -208,6 +206,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.options.logger.Debugf("load items %d", len(m))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,76 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-gost/core/auth"
|
"github.com/go-gost/core/auth"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
"github.com/go-gost/plugin/auth/proto"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
|
auth_util "github.com/go-gost/x/internal/util/auth"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type grpcPlugin struct {
|
||||||
|
conn grpc.ClientConnInterface
|
||||||
|
client proto.AuthenticatorClient
|
||||||
|
log logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGRPCPlugin creates an Authenticator plugin based on gRPC.
|
||||||
|
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) auth.Authenticator {
|
||||||
|
var options plugin.Options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{
|
||||||
|
"kind": "auther",
|
||||||
|
"auther": name,
|
||||||
|
})
|
||||||
|
conn, err := plugin.NewGRPCConn(addr, &options)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &grpcPlugin{
|
||||||
|
conn: conn,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn != nil {
|
||||||
|
p.client = proto.NewAuthenticatorClient(conn)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate checks the validity of the provided user-password pair.
|
||||||
|
func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
|
||||||
|
if p.client == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := p.client.Authenticate(ctx,
|
||||||
|
&proto.AuthenticateRequest{
|
||||||
|
Username: user,
|
||||||
|
Password: password,
|
||||||
|
Client: string(auth_util.ClientAddrFromContext(ctx)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
p.log.Error(err)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return r.Id, r.Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *grpcPlugin) Close() error {
|
||||||
|
if closer, ok := p.conn.(io.Closer); ok {
|
||||||
|
return closer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type httpPluginRequest struct {
|
type httpPluginRequest struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
@ -56,7 +118,7 @@ func (p *httpPlugin) Authenticate(ctx context.Context, user, password string, op
|
|||||||
rb := httpPluginRequest{
|
rb := httpPluginRequest{
|
||||||
Username: user,
|
Username: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
|
Client: string(auth_util.ClientAddrFromContext(ctx)),
|
||||||
}
|
}
|
||||||
v, err := json.Marshal(&rb)
|
v, err := json.Marshal(&rb)
|
||||||
if err != nil {
|
if err != nil {
|
@ -1,72 +0,0 @@
|
|||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/auth"
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/plugin/auth/proto"
|
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grpcPlugin struct {
|
|
||||||
conn grpc.ClientConnInterface
|
|
||||||
client proto.AuthenticatorClient
|
|
||||||
log logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGRPCPlugin creates an Authenticator plugin based on gRPC.
|
|
||||||
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) auth.Authenticator {
|
|
||||||
var options plugin.Options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
log := logger.Default().WithFields(map[string]any{
|
|
||||||
"kind": "auther",
|
|
||||||
"auther": name,
|
|
||||||
})
|
|
||||||
conn, err := plugin.NewGRPCConn(addr, &options)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &grpcPlugin{
|
|
||||||
conn: conn,
|
|
||||||
log: log,
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn != nil {
|
|
||||||
p.client = proto.NewAuthenticatorClient(conn)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate checks the validity of the provided user-password pair.
|
|
||||||
func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
|
|
||||||
if p.client == nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := p.client.Authenticate(ctx,
|
|
||||||
&proto.AuthenticateRequest{
|
|
||||||
Username: user,
|
|
||||||
Password: password,
|
|
||||||
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
p.log.Error(err)
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return r.Id, r.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) Close() error {
|
|
||||||
if closer, ok := p.conn.(io.Closer); ok {
|
|
||||||
return closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -130,7 +130,6 @@ func (bp *localBypass) reload(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
patterns := append(bp.options.matchers, v...)
|
patterns := append(bp.options.matchers, v...)
|
||||||
bp.options.logger.Debugf("load items %d", len(patterns))
|
|
||||||
|
|
||||||
var addrs []string
|
var addrs []string
|
||||||
var inets []*net.IPNet
|
var inets []*net.IPNet
|
||||||
@ -206,6 +205,7 @@ func (bp *localBypass) load(ctx context.Context) (patterns []string, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bp.options.logger.Debugf("load items %d", len(patterns))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,17 +235,11 @@ func (bp *localBypass) Contains(ctx context.Context, network, addr string, opts
|
|||||||
b := !bp.options.whitelist && matched ||
|
b := !bp.options.whitelist && matched ||
|
||||||
bp.options.whitelist && !matched
|
bp.options.whitelist && !matched
|
||||||
if b {
|
if b {
|
||||||
bp.options.logger.Debugf("bypass: %s, whitelist: %t", addr, bp.options.whitelist)
|
bp.options.logger.Debugf("bypass: %s", addr)
|
||||||
} else {
|
|
||||||
bp.options.logger.Debugf("pass: %s, whitelist: %t", addr, bp.options.whitelist)
|
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *localBypass) IsWhitelist() bool {
|
|
||||||
return p.options.whitelist
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bp *localBypass) parseLine(s string) string {
|
func (bp *localBypass) parseLine(s string) string {
|
||||||
if n := strings.IndexByte(s, '#'); n >= 0 {
|
if n := strings.IndexByte(s, '#'); n >= 0 {
|
||||||
s = s[:n]
|
s = s[:n]
|
||||||
|
@ -4,14 +4,81 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-gost/core/bypass"
|
"github.com/go-gost/core/bypass"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
"github.com/go-gost/plugin/bypass/proto"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
|
auth_util "github.com/go-gost/x/internal/util/auth"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type grpcPlugin struct {
|
||||||
|
conn grpc.ClientConnInterface
|
||||||
|
client proto.BypassClient
|
||||||
|
log logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGRPCPlugin creates a Bypass plugin based on gRPC.
|
||||||
|
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) bypass.Bypass {
|
||||||
|
var options plugin.Options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.Default().WithFields(map[string]any{
|
||||||
|
"kind": "bypass",
|
||||||
|
"bypass": name,
|
||||||
|
})
|
||||||
|
conn, err := plugin.NewGRPCConn(addr, &options)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &grpcPlugin{
|
||||||
|
conn: conn,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
if conn != nil {
|
||||||
|
p.client = proto.NewBypassClient(conn)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
||||||
|
if p.client == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var options bypass.Options
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := p.client.Bypass(ctx,
|
||||||
|
&proto.BypassRequest{
|
||||||
|
Network: network,
|
||||||
|
Addr: addr,
|
||||||
|
Client: string(auth_util.IDFromContext(ctx)),
|
||||||
|
Host: options.Host,
|
||||||
|
Path: options.Path,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
p.log.Error(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return r.Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *grpcPlugin) Close() error {
|
||||||
|
if closer, ok := p.conn.(io.Closer); ok {
|
||||||
|
return closer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type httpPluginRequest struct {
|
type httpPluginRequest struct {
|
||||||
Network string `json:"network"`
|
Network string `json:"network"`
|
||||||
Addr string `json:"addr"`
|
Addr string `json:"addr"`
|
||||||
@ -62,7 +129,7 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
|
|||||||
rb := httpPluginRequest{
|
rb := httpPluginRequest{
|
||||||
Network: network,
|
Network: network,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Client: string(ctxvalue.ClientIDFromContext(ctx)),
|
Client: string(auth_util.IDFromContext(ctx)),
|
||||||
Host: options.Host,
|
Host: options.Host,
|
||||||
Path: options.Path,
|
Path: options.Path,
|
||||||
}
|
}
|
||||||
@ -96,7 +163,3 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
|
|||||||
}
|
}
|
||||||
return res.OK
|
return res.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *httpPlugin) IsWhitelist() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
package bypass
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/bypass"
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/plugin/bypass/proto"
|
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grpcPlugin struct {
|
|
||||||
conn grpc.ClientConnInterface
|
|
||||||
client proto.BypassClient
|
|
||||||
log logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGRPCPlugin creates a Bypass plugin based on gRPC.
|
|
||||||
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) bypass.Bypass {
|
|
||||||
var options plugin.Options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
log := logger.Default().WithFields(map[string]any{
|
|
||||||
"kind": "bypass",
|
|
||||||
"bypass": name,
|
|
||||||
})
|
|
||||||
conn, err := plugin.NewGRPCConn(addr, &options)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := &grpcPlugin{
|
|
||||||
conn: conn,
|
|
||||||
log: log,
|
|
||||||
}
|
|
||||||
if conn != nil {
|
|
||||||
p.client = proto.NewBypassClient(conn)
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
|
|
||||||
if p.client == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var options bypass.Options
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := p.client.Bypass(ctx,
|
|
||||||
&proto.BypassRequest{
|
|
||||||
Network: network,
|
|
||||||
Addr: addr,
|
|
||||||
Client: string(ctxvalue.ClientIDFromContext(ctx)),
|
|
||||||
Host: options.Host,
|
|
||||||
Path: options.Path,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
p.log.Error(err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return r.Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) Close() error {
|
|
||||||
if closer, ok := p.conn.(io.Closer); ok {
|
|
||||||
return closer.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *grpcPlugin) IsWhitelist() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
@ -104,7 +104,7 @@ func (c *Chain) Route(ctx context.Context, network, address string, opts ...chai
|
|||||||
tr.Options().Route = rt
|
tr.Options().Route = rt
|
||||||
node = node.Copy()
|
node = node.Copy()
|
||||||
node.Options().Transport = tr
|
node.Options().Transport = tr
|
||||||
rt = NewRoute(ChainRouteOption(c))
|
rt = NewRoute()
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.addNode(node)
|
rt.addNode(node)
|
||||||
|
114
chain/route.go
114
chain/route.go
@ -2,8 +2,6 @@ package chain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -12,86 +10,9 @@ import (
|
|||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/core/metrics"
|
"github.com/go-gost/core/metrics"
|
||||||
"github.com/go-gost/core/selector"
|
"github.com/go-gost/core/selector"
|
||||||
xnet "github.com/go-gost/x/internal/net"
|
|
||||||
"github.com/go-gost/x/internal/net/dialer"
|
|
||||||
"github.com/go-gost/x/internal/net/udp"
|
|
||||||
xmetrics "github.com/go-gost/x/metrics"
|
xmetrics "github.com/go-gost/x/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrEmptyRoute = errors.New("empty route")
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
DefaultRoute chain.Route = &defaultRoute{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultRoute is a Route without nodes.
|
|
||||||
type defaultRoute struct{}
|
|
||||||
|
|
||||||
func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
|
|
||||||
var options chain.DialOptions
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
netd := dialer.Dialer{
|
|
||||||
Interface: options.Interface,
|
|
||||||
Netns: options.Netns,
|
|
||||||
Logger: options.Logger,
|
|
||||||
}
|
|
||||||
if options.SockOpts != nil {
|
|
||||||
netd.Mark = options.SockOpts.Mark
|
|
||||||
}
|
|
||||||
|
|
||||||
return netd.Dial(ctx, network, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*defaultRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) {
|
|
||||||
var options chain.BindOptions
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch network {
|
|
||||||
case "tcp", "tcp4", "tcp6":
|
|
||||||
addr, err := net.ResolveTCPAddr(network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return net.ListenTCP(network, addr)
|
|
||||||
case "udp", "udp4", "udp6":
|
|
||||||
addr, err := net.ResolveUDPAddr(network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := net.ListenUDP(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
logger := logger.Default().WithFields(map[string]any{
|
|
||||||
"network": network,
|
|
||||||
"address": address,
|
|
||||||
})
|
|
||||||
ln := udp.NewListener(conn, &udp.ListenConfig{
|
|
||||||
Backlog: options.Backlog,
|
|
||||||
ReadQueueSize: options.UDPDataQueueSize,
|
|
||||||
ReadBufferSize: options.UDPDataBufferSize,
|
|
||||||
TTL: options.UDPConnTTL,
|
|
||||||
Keepalive: true,
|
|
||||||
Logger: logger,
|
|
||||||
})
|
|
||||||
return ln, err
|
|
||||||
default:
|
|
||||||
err := fmt.Errorf("network %s unsupported", network)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *defaultRoute) Nodes() []*chain.Node {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouteOptions struct {
|
type RouteOptions struct {
|
||||||
Chain chain.Chainer
|
Chain chain.Chainer
|
||||||
}
|
}
|
||||||
@ -104,12 +25,12 @@ func ChainRouteOption(c chain.Chainer) RouteOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type chainRoute struct {
|
type route struct {
|
||||||
nodes []*chain.Node
|
nodes []*chain.Node
|
||||||
options RouteOptions
|
options RouteOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRoute(opts ...RouteOption) *chainRoute {
|
func NewRoute(opts ...RouteOption) *route {
|
||||||
var options RouteOptions
|
var options RouteOptions
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if opt != nil {
|
if opt != nil {
|
||||||
@ -117,18 +38,18 @@ func NewRoute(opts ...RouteOption) *chainRoute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &chainRoute{
|
return &route{
|
||||||
options: options,
|
options: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) addNode(nodes ...*chain.Node) {
|
func (r *route) addNode(nodes ...*chain.Node) {
|
||||||
r.nodes = append(r.nodes, nodes...)
|
r.nodes = append(r.nodes, nodes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
|
func (r *route) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
|
||||||
if len(r.Nodes()) == 0 {
|
if len(r.Nodes()) == 0 {
|
||||||
return DefaultRoute.Dial(ctx, network, address, opts...)
|
return chain.DefaultRoute.Dial(ctx, network, address, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var options chain.DialOptions
|
var options chain.DialOptions
|
||||||
@ -152,9 +73,9 @@ func (r *chainRoute) Dial(ctx context.Context, network, address string, opts ...
|
|||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) {
|
func (r *route) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) {
|
||||||
if len(r.Nodes()) == 0 {
|
if len(r.Nodes()) == 0 {
|
||||||
return DefaultRoute.Bind(ctx, network, address, opts...)
|
return chain.DefaultRoute.Bind(ctx, network, address, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var options chain.BindOptions
|
var options chain.BindOptions
|
||||||
@ -185,7 +106,7 @@ func (r *chainRoute) Bind(ctx context.Context, network, address string, opts ...
|
|||||||
return ln, nil
|
return ln, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn net.Conn, err error) {
|
func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Conn, err error) {
|
||||||
network := "ip"
|
network := "ip"
|
||||||
node := r.nodes[0]
|
node := r.nodes[0]
|
||||||
|
|
||||||
@ -208,16 +129,15 @@ func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn ne
|
|||||||
metrics.Labels{"chain": name, "node": node.Name}); v != nil {
|
metrics.Labels{"chain": name, "node": node.Name}); v != nil {
|
||||||
v.Inc()
|
v.Inc()
|
||||||
}
|
}
|
||||||
return
|
} else {
|
||||||
}
|
if marker != nil {
|
||||||
|
marker.Reset()
|
||||||
if marker != nil {
|
}
|
||||||
marker.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
addr, err := xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
|
addr, err := chain.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
|
||||||
marker := node.Marker()
|
marker := node.Marker()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if marker != nil {
|
if marker != nil {
|
||||||
@ -261,7 +181,7 @@ func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn ne
|
|||||||
preNode := node
|
preNode := node
|
||||||
for _, node := range r.nodes[1:] {
|
for _, node := range r.nodes[1:] {
|
||||||
marker := node.Marker()
|
marker := node.Marker()
|
||||||
addr, err = xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
|
addr, err = chain.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cn.Close()
|
cn.Close()
|
||||||
if marker != nil {
|
if marker != nil {
|
||||||
@ -297,14 +217,14 @@ func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn ne
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) getNode(index int) *chain.Node {
|
func (r *route) getNode(index int) *chain.Node {
|
||||||
if r == nil || len(r.Nodes()) == 0 || index < 0 || index >= len(r.Nodes()) {
|
if r == nil || len(r.Nodes()) == 0 || index < 0 || index >= len(r.Nodes()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return r.nodes[index]
|
return r.nodes[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *chainRoute) Nodes() []*chain.Node {
|
func (r *route) Nodes() []*chain.Node {
|
||||||
if r != nil {
|
if r != nil {
|
||||||
return r.nodes
|
return r.nodes
|
||||||
}
|
}
|
||||||
|
207
chain/router.go
207
chain/router.go
@ -1,207 +0,0 @@
|
|||||||
package chain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/chain"
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/core/recorder"
|
|
||||||
xnet "github.com/go-gost/x/internal/net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Router struct {
|
|
||||||
options chain.RouterOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRouter(opts ...chain.RouterOption) *Router {
|
|
||||||
r := &Router{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
if opt != nil {
|
|
||||||
opt(&r.options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.options.Timeout == 0 {
|
|
||||||
r.options.Timeout = 15 * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.options.Logger == nil {
|
|
||||||
r.options.Logger = logger.Default().WithFields(map[string]any{"kind": "router"})
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) Options() *chain.RouterOptions {
|
|
||||||
if r == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &r.options
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
|
||||||
host := address
|
|
||||||
if h, _, _ := net.SplitHostPort(address); h != "" {
|
|
||||||
host = h
|
|
||||||
}
|
|
||||||
r.record(ctx, recorder.RecorderServiceRouterDialAddress, []byte(host))
|
|
||||||
|
|
||||||
conn, err = r.dial(ctx, network, address)
|
|
||||||
if err != nil {
|
|
||||||
r.record(ctx, recorder.RecorderServiceRouterDialAddressError, []byte(host))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if network == "udp" || network == "udp4" || network == "udp6" {
|
|
||||||
if _, ok := conn.(net.PacketConn); !ok {
|
|
||||||
return &packetConn{conn}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) record(ctx context.Context, name string, data []byte) error {
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rec := range r.options.Recorders {
|
|
||||||
if rec.Record == name {
|
|
||||||
err := rec.Recorder.Record(ctx, data)
|
|
||||||
if err != nil {
|
|
||||||
r.options.Logger.Errorf("record %s: %v", name, err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
|
|
||||||
count := r.options.Retries + 1
|
|
||||||
if count <= 0 {
|
|
||||||
count = 1
|
|
||||||
}
|
|
||||||
r.options.Logger.Debugf("dial %s/%s", address, network)
|
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
ctx := ctx
|
|
||||||
if r.options.Timeout > 0 {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
var ipAddr string
|
|
||||||
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, r.options.Logger)
|
|
||||||
if err != nil {
|
|
||||||
r.options.Logger.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
var route chain.Route
|
|
||||||
if r.options.Chain != nil {
|
|
||||||
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.options.Logger.IsLevelEnabled(logger.DebugLevel) {
|
|
||||||
buf := bytes.Buffer{}
|
|
||||||
for _, node := range routePath(route) {
|
|
||||||
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&buf, "%s", ipAddr)
|
|
||||||
r.options.Logger.Debugf("route(retry=%d) %s", i, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if route == nil {
|
|
||||||
route = DefaultRoute
|
|
||||||
}
|
|
||||||
conn, err = route.Dial(ctx, network, ipAddr,
|
|
||||||
chain.InterfaceDialOption(r.options.IfceName),
|
|
||||||
chain.NetnsDialOption(r.options.Netns),
|
|
||||||
chain.SockOptsDialOption(r.options.SockOpts),
|
|
||||||
chain.LoggerDialOption(r.options.Logger),
|
|
||||||
)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.options.Logger.Errorf("route(retry=%d) %s", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (ln net.Listener, err error) {
|
|
||||||
count := r.options.Retries + 1
|
|
||||||
if count <= 0 {
|
|
||||||
count = 1
|
|
||||||
}
|
|
||||||
r.options.Logger.Debugf("bind on %s/%s", address, network)
|
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
ctx := ctx
|
|
||||||
if r.options.Timeout > 0 {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
var route chain.Route
|
|
||||||
if r.options.Chain != nil {
|
|
||||||
route = r.options.Chain.Route(ctx, network, address)
|
|
||||||
if route == nil || len(route.Nodes()) == 0 {
|
|
||||||
err = ErrEmptyRoute
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.options.Logger.IsLevelEnabled(logger.DebugLevel) {
|
|
||||||
buf := bytes.Buffer{}
|
|
||||||
for _, node := range routePath(route) {
|
|
||||||
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&buf, "%s", address)
|
|
||||||
r.options.Logger.Debugf("route(retry=%d) %s", i, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if route == nil {
|
|
||||||
route = DefaultRoute
|
|
||||||
}
|
|
||||||
ln, err = route.Bind(ctx, network, address, opts...)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
r.options.Logger.Errorf("route(retry=%d) %s", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func routePath(route chain.Route) (path []*chain.Node) {
|
|
||||||
if route == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, node := range route.Nodes() {
|
|
||||||
if tr := node.Options().Transport; tr != nil {
|
|
||||||
path = append(path, routePath(tr.Options().Route)...)
|
|
||||||
}
|
|
||||||
path = append(path, node)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type packetConn struct {
|
|
||||||
net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
|
|
||||||
n, err = c.Read(b)
|
|
||||||
addr = c.Conn.RemoteAddr()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *packetConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
|
||||||
return c.Write(b)
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package chain
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/chain"
|
|
||||||
"github.com/go-gost/core/connector"
|
|
||||||
"github.com/go-gost/core/dialer"
|
|
||||||
net_dialer "github.com/go-gost/x/internal/net/dialer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Transport struct {
|
|
||||||
dialer dialer.Dialer
|
|
||||||
connector connector.Connector
|
|
||||||
options chain.TransportOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTransport(d dialer.Dialer, c connector.Connector, opts ...chain.TransportOption) *Transport {
|
|
||||||
tr := &Transport{
|
|
||||||
dialer: d,
|
|
||||||
connector: c,
|
|
||||||
}
|
|
||||||
for _, opt := range opts {
|
|
||||||
if opt != nil {
|
|
||||||
opt(&tr.options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) {
|
|
||||||
netd := &net_dialer.Dialer{
|
|
||||||
Interface: tr.options.IfceName,
|
|
||||||
Netns: tr.options.Netns,
|
|
||||||
}
|
|
||||||
if tr.options.SockOpts != nil {
|
|
||||||
netd.Mark = tr.options.SockOpts.Mark
|
|
||||||
}
|
|
||||||
if tr.options.Route != nil && len(tr.options.Route.Nodes()) > 0 {
|
|
||||||
netd.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
return tr.options.Route.Dial(ctx, network, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts := []dialer.DialOption{
|
|
||||||
dialer.HostDialOption(tr.options.Addr),
|
|
||||||
dialer.NetDialerDialOption(netd),
|
|
||||||
}
|
|
||||||
return tr.dialer.Dial(ctx, addr, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
|
||||||
var err error
|
|
||||||
if hs, ok := tr.dialer.(dialer.Handshaker); ok {
|
|
||||||
conn, err = hs.Handshake(ctx, conn,
|
|
||||||
dialer.AddrHandshakeOption(tr.options.Addr))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hs, ok := tr.connector.(connector.Handshaker); ok {
|
|
||||||
return hs.Handshake(ctx, conn)
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Connect(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
|
|
||||||
netd := &net_dialer.Dialer{
|
|
||||||
Interface: tr.options.IfceName,
|
|
||||||
Netns: tr.options.Netns,
|
|
||||||
}
|
|
||||||
if tr.options.SockOpts != nil {
|
|
||||||
netd.Mark = tr.options.SockOpts.Mark
|
|
||||||
}
|
|
||||||
return tr.connector.Connect(ctx, conn, network, address,
|
|
||||||
connector.DialerConnectOption(netd),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
|
||||||
if binder, ok := tr.connector.(connector.Binder); ok {
|
|
||||||
return binder.Bind(ctx, conn, network, address, opts...)
|
|
||||||
}
|
|
||||||
return nil, connector.ErrBindUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Multiplex() bool {
|
|
||||||
if mux, ok := tr.dialer.(dialer.Multiplexer); ok {
|
|
||||||
return mux.Multiplex()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Options() *chain.TransportOptions {
|
|
||||||
if tr != nil {
|
|
||||||
return &tr.options
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *Transport) Copy() chain.Transporter {
|
|
||||||
tr2 := &Transport{}
|
|
||||||
*tr2 = *tr
|
|
||||||
return tr
|
|
||||||
}
|
|
@ -1,682 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
xnet "github.com/go-gost/x/internal/net"
|
|
||||||
"github.com/go-gost/x/limiter/conn"
|
|
||||||
"github.com/go-gost/x/limiter/traffic"
|
|
||||||
mdx "github.com/go-gost/x/metadata"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidCmd = errors.New("invalid cmd")
|
|
||||||
ErrInvalidNode = errors.New("invalid node")
|
|
||||||
)
|
|
||||||
|
|
||||||
func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error) {
|
|
||||||
namePrefix := ""
|
|
||||||
cfg := &config.Config{}
|
|
||||||
|
|
||||||
var chain *config.ChainConfig
|
|
||||||
if len(nodeList) > 0 {
|
|
||||||
chain = &config.ChainConfig{
|
|
||||||
Name: fmt.Sprintf("%schain-0", namePrefix),
|
|
||||||
}
|
|
||||||
cfg.Chains = append(cfg.Chains, chain)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, node := range nodeList {
|
|
||||||
url, err := Norm(node)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeConfig, err := buildNodeConfig(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
|
|
||||||
|
|
||||||
var nodes []*config.NodeConfig
|
|
||||||
for _, host := range strings.Split(nodeConfig.Addr, ",") {
|
|
||||||
if host == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
nodeCfg := &config.NodeConfig{}
|
|
||||||
*nodeCfg = *nodeConfig
|
|
||||||
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
|
|
||||||
nodeCfg.Addr = host
|
|
||||||
nodes = append(nodes, nodeCfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]any{}
|
|
||||||
for k, v := range url.Query() {
|
|
||||||
if len(v) > 0 {
|
|
||||||
m[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
|
|
||||||
hopConfig := &config.HopConfig{
|
|
||||||
Name: fmt.Sprintf("%shop-%d", namePrefix, i),
|
|
||||||
Selector: parseSelector(m),
|
|
||||||
Nodes: nodes,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "bypass"); v != "" {
|
|
||||||
bypassCfg := &config.BypassConfig{
|
|
||||||
Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
bypassCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
hopConfig.Bypass = bypassCfg.Name
|
|
||||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
|
||||||
delete(m, "bypass")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "resolver"); v != "" {
|
|
||||||
resolverCfg := &config.ResolverConfig{
|
|
||||||
Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)),
|
|
||||||
}
|
|
||||||
for _, rs := range strings.Split(v, ",") {
|
|
||||||
if rs == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resolverCfg.Nameservers = append(
|
|
||||||
resolverCfg.Nameservers,
|
|
||||||
&config.NameserverConfig{
|
|
||||||
Addr: rs,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hopConfig.Resolver = resolverCfg.Name
|
|
||||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
|
||||||
delete(m, "resolver")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "hosts"); v != "" {
|
|
||||||
hostsCfg := &config.HostsConfig{
|
|
||||||
Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)),
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
ss := strings.SplitN(s, ":", 2)
|
|
||||||
if len(ss) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hostsCfg.Mappings = append(
|
|
||||||
hostsCfg.Mappings,
|
|
||||||
&config.HostMappingConfig{
|
|
||||||
Hostname: ss[0],
|
|
||||||
IP: ss[1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hopConfig.Hosts = hostsCfg.Name
|
|
||||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
|
||||||
delete(m, "hosts")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "interface"); v != "" {
|
|
||||||
hopConfig.Interface = v
|
|
||||||
delete(m, "interface")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
|
|
||||||
hopConfig.SockOpts = &config.SockOptsConfig{
|
|
||||||
Mark: v,
|
|
||||||
}
|
|
||||||
delete(m, "so_mark")
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.Hops = append(chain.Hops, hopConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
var services []*config.ServiceConfig
|
|
||||||
for _, svc := range serviceList {
|
|
||||||
svc = strings.TrimSpace(svc)
|
|
||||||
if svc == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if svc[0] == ':' || !strings.Contains(svc, "://") {
|
|
||||||
svc = "auto://" + svc
|
|
||||||
}
|
|
||||||
|
|
||||||
host, svc := cutHost(svc)
|
|
||||||
|
|
||||||
url, err := Norm(svc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
url.Host = host
|
|
||||||
|
|
||||||
svcs, err := buildServiceConfig(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
services = append(services, svcs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, service := range services {
|
|
||||||
service.Name = fmt.Sprintf("%sservice-%d", namePrefix, i)
|
|
||||||
if chain != nil {
|
|
||||||
if service.Listener.Type == "rtcp" || service.Listener.Type == "rudp" {
|
|
||||||
service.Listener.Chain = chain.Name
|
|
||||||
} else {
|
|
||||||
service.Handler.Chain = chain.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.Services = append(cfg.Services, service)
|
|
||||||
|
|
||||||
mh := service.Handler.Metadata
|
|
||||||
md := mdx.NewMetadata(mh)
|
|
||||||
if v := mdutil.GetInt(md, "retries"); v > 0 {
|
|
||||||
service.Handler.Retries = v
|
|
||||||
delete(mh, "retries")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "admission"); v != "" {
|
|
||||||
admCfg := &config.AdmissionConfig{
|
|
||||||
Name: fmt.Sprintf("%sadmission-%d", namePrefix, len(cfg.Admissions)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
admCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
admCfg.Matchers = append(admCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
service.Admission = admCfg.Name
|
|
||||||
cfg.Admissions = append(cfg.Admissions, admCfg)
|
|
||||||
delete(mh, "admission")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "bypass"); v != "" {
|
|
||||||
bypassCfg := &config.BypassConfig{
|
|
||||||
Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)),
|
|
||||||
}
|
|
||||||
if v[0] == '~' {
|
|
||||||
bypassCfg.Whitelist = true
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
if s == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
|
||||||
}
|
|
||||||
service.Bypass = bypassCfg.Name
|
|
||||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
|
||||||
delete(mh, "bypass")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "resolver"); v != "" {
|
|
||||||
resolverCfg := &config.ResolverConfig{
|
|
||||||
Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)),
|
|
||||||
}
|
|
||||||
for _, rs := range strings.Split(v, ",") {
|
|
||||||
if rs == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resolverCfg.Nameservers = append(
|
|
||||||
resolverCfg.Nameservers,
|
|
||||||
&config.NameserverConfig{
|
|
||||||
Addr: rs,
|
|
||||||
Prefer: mdutil.GetString(md, "prefer"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
service.Resolver = resolverCfg.Name
|
|
||||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
|
||||||
delete(mh, "resolver")
|
|
||||||
}
|
|
||||||
if v := mdutil.GetString(md, "hosts"); v != "" {
|
|
||||||
hostsCfg := &config.HostsConfig{
|
|
||||||
Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)),
|
|
||||||
}
|
|
||||||
for _, s := range strings.Split(v, ",") {
|
|
||||||
ss := strings.SplitN(s, ":", 2)
|
|
||||||
if len(ss) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hostsCfg.Mappings = append(
|
|
||||||
hostsCfg.Mappings,
|
|
||||||
&config.HostMappingConfig{
|
|
||||||
Hostname: ss[0],
|
|
||||||
IP: ss[1],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
service.Hosts = hostsCfg.Name
|
|
||||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
|
||||||
delete(mh, "hosts")
|
|
||||||
}
|
|
||||||
|
|
||||||
in := mdutil.GetString(md, "limiter.in")
|
|
||||||
out := mdutil.GetString(md, "limiter.out")
|
|
||||||
cin := mdutil.GetString(md, "limiter.conn.in")
|
|
||||||
cout := mdutil.GetString(md, "limiter.conn.out")
|
|
||||||
if in != "" || cin != "" || out != "" || cout != "" {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%slimiter-%d", namePrefix, len(cfg.Limiters)),
|
|
||||||
}
|
|
||||||
if in != "" || out != "" {
|
|
||||||
limiter.Limits = append(limiter.Limits,
|
|
||||||
fmt.Sprintf("%s %s %s", traffic.GlobalLimitKey, in, out))
|
|
||||||
}
|
|
||||||
if cin != "" || cout != "" {
|
|
||||||
limiter.Limits = append(limiter.Limits,
|
|
||||||
fmt.Sprintf("%s %s %s", traffic.ConnLimitKey, cin, cout))
|
|
||||||
}
|
|
||||||
service.Limiter = limiter.Name
|
|
||||||
cfg.Limiters = append(cfg.Limiters, limiter)
|
|
||||||
delete(mh, "limiter.in")
|
|
||||||
delete(mh, "limiter.out")
|
|
||||||
delete(mh, "limiter.conn.in")
|
|
||||||
delete(mh, "limiter.conn.out")
|
|
||||||
}
|
|
||||||
|
|
||||||
if climit := mdutil.GetInt(md, "climiter"); climit > 0 {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%sclimiter-%d", namePrefix, len(cfg.CLimiters)),
|
|
||||||
Limits: []string{fmt.Sprintf("%s %d", conn.GlobalLimitKey, climit)},
|
|
||||||
}
|
|
||||||
service.CLimiter = limiter.Name
|
|
||||||
cfg.CLimiters = append(cfg.CLimiters, limiter)
|
|
||||||
delete(mh, "climiter")
|
|
||||||
}
|
|
||||||
|
|
||||||
if rlimit := mdutil.GetFloat(md, "rlimiter"); rlimit > 0 {
|
|
||||||
limiter := &config.LimiterConfig{
|
|
||||||
Name: fmt.Sprintf("%srlimiter-%d", namePrefix, len(cfg.RLimiters)),
|
|
||||||
Limits: []string{fmt.Sprintf("%s %s", conn.GlobalLimitKey, strconv.FormatFloat(rlimit, 'f', -1, 64))},
|
|
||||||
}
|
|
||||||
service.RLimiter = limiter.Name
|
|
||||||
cfg.RLimiters = append(cfg.RLimiters, limiter)
|
|
||||||
delete(mh, "rlimiter")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cutHost(s string) (host, remain string) {
|
|
||||||
if s == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n := strings.IndexByte(s, ':')
|
|
||||||
start := n + 3
|
|
||||||
end := strings.IndexAny(s[start:], "/?")
|
|
||||||
if end < 0 {
|
|
||||||
end = len(s)
|
|
||||||
} else {
|
|
||||||
end += start
|
|
||||||
}
|
|
||||||
// auth info
|
|
||||||
if n = strings.LastIndexByte(s[start:end], '@'); n >= 0 {
|
|
||||||
start += (n + 1)
|
|
||||||
}
|
|
||||||
host = s[start:end]
|
|
||||||
|
|
||||||
remain = s[:start] + s[end:]
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
|
|
||||||
namePrefix := ""
|
|
||||||
if v := os.Getenv("_GOST_ID"); v != "" {
|
|
||||||
namePrefix = fmt.Sprintf("go-%s@", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler, listener string
|
|
||||||
schemes := strings.Split(url.Scheme, "+")
|
|
||||||
if len(schemes) == 1 {
|
|
||||||
handler = schemes[0]
|
|
||||||
listener = schemes[0]
|
|
||||||
}
|
|
||||||
if len(schemes) == 2 {
|
|
||||||
handler = schemes[0]
|
|
||||||
listener = schemes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
addrs := xnet.AddrPortRange(url.Host).Addrs()
|
|
||||||
if len(addrs) == 0 {
|
|
||||||
addrs = append(addrs, url.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
var services []*config.ServiceConfig
|
|
||||||
for _, addr := range addrs {
|
|
||||||
services = append(services, &config.ServiceConfig{
|
|
||||||
Addr: addr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if h := registry.HandlerRegistry().Get(handler); h == nil {
|
|
||||||
handler = "auto"
|
|
||||||
}
|
|
||||||
if ln := registry.ListenerRegistry().Get(listener); ln == nil {
|
|
||||||
listener = "tcp"
|
|
||||||
if handler == "ssu" {
|
|
||||||
listener = "udp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodes []*config.ForwardNodeConfig
|
|
||||||
// forward mode
|
|
||||||
if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" {
|
|
||||||
i := 0
|
|
||||||
for _, addr := range strings.Split(remotes, ",") {
|
|
||||||
addrs := xnet.AddrPortRange(addr).Addrs()
|
|
||||||
if len(addrs) == 0 {
|
|
||||||
addrs = append(addrs, addr)
|
|
||||||
}
|
|
||||||
for _, adr := range addrs {
|
|
||||||
nodes = append(nodes, &config.ForwardNodeConfig{
|
|
||||||
Name: fmt.Sprintf("%starget-%d", namePrefix, i),
|
|
||||||
Addr: adr,
|
|
||||||
})
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if handler != "relay" {
|
|
||||||
if listener == "tcp" || listener == "udp" ||
|
|
||||||
listener == "rtcp" || listener == "rudp" ||
|
|
||||||
listener == "tun" || listener == "tap" ||
|
|
||||||
listener == "dns" || listener == "unix" ||
|
|
||||||
listener == "serial" {
|
|
||||||
handler = listener
|
|
||||||
} else {
|
|
||||||
handler = "forward"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nodes) > 0 {
|
|
||||||
if len(services) == 1 {
|
|
||||||
services[0].Forwarder = &config.ForwarderConfig{
|
|
||||||
Nodes: nodes,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i, svc := range services {
|
|
||||||
if len(nodes) == 1 {
|
|
||||||
svc.Forwarder = &config.ForwarderConfig{
|
|
||||||
Nodes: nodes,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if i < len(nodes) {
|
|
||||||
svc.Forwarder = &config.ForwarderConfig{
|
|
||||||
Nodes: []*config.ForwardNodeConfig{nodes[i]},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth *config.AuthConfig
|
|
||||||
if url.User != nil {
|
|
||||||
auth = &config.AuthConfig{
|
|
||||||
Username: url.User.Username(),
|
|
||||||
}
|
|
||||||
auth.Password, _ = url.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]any{}
|
|
||||||
for k, v := range url.Query() {
|
|
||||||
if len(v) > 0 {
|
|
||||||
m[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
|
|
||||||
if sa := mdutil.GetString(md, "auth"); sa != "" {
|
|
||||||
au, err := parseAuthFromCmd(sa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
auth = au
|
|
||||||
}
|
|
||||||
delete(m, "auth")
|
|
||||||
|
|
||||||
tlsConfig := &config.TLSConfig{
|
|
||||||
CertFile: mdutil.GetString(md, "tls.certFile", "certFile", "cert"),
|
|
||||||
KeyFile: mdutil.GetString(md, "tls.keyFile", "keyFile", "key"),
|
|
||||||
CAFile: mdutil.GetString(md, "tls.caFile", "caFile", "ca"),
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "tls.certFile")
|
|
||||||
delete(m, "certFile")
|
|
||||||
delete(m, "cert")
|
|
||||||
delete(m, "tls.keyFile")
|
|
||||||
delete(m, "keyFile")
|
|
||||||
delete(m, "key")
|
|
||||||
delete(m, "tls.caFile")
|
|
||||||
delete(m, "caFile")
|
|
||||||
delete(m, "ca")
|
|
||||||
|
|
||||||
if tlsConfig.CertFile == "" {
|
|
||||||
tlsConfig = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := mdutil.GetString(md, "dns"); v != "" {
|
|
||||||
md.Set("dns", strings.Split(v, ","))
|
|
||||||
}
|
|
||||||
|
|
||||||
selector := parseSelector(m)
|
|
||||||
handlerCfg := &config.HandlerConfig{
|
|
||||||
Type: handler,
|
|
||||||
Auth: auth,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
listenerCfg := &config.ListenerConfig{
|
|
||||||
Type: listener,
|
|
||||||
TLS: tlsConfig,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
if listenerCfg.Type == "ssh" || listenerCfg.Type == "sshd" {
|
|
||||||
handlerCfg.Auth = nil
|
|
||||||
listenerCfg.Auth = auth
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, svc := range services {
|
|
||||||
if svc.Forwarder != nil {
|
|
||||||
svc.Forwarder.Selector = selector
|
|
||||||
}
|
|
||||||
svc.Handler = handlerCfg
|
|
||||||
svc.Listener = listenerCfg
|
|
||||||
svc.Metadata = m
|
|
||||||
}
|
|
||||||
|
|
||||||
return services, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
|
||||||
var connector, dialer string
|
|
||||||
schemes := strings.Split(url.Scheme, "+")
|
|
||||||
if len(schemes) == 1 {
|
|
||||||
connector = schemes[0]
|
|
||||||
dialer = schemes[0]
|
|
||||||
}
|
|
||||||
if len(schemes) == 2 {
|
|
||||||
connector = schemes[0]
|
|
||||||
dialer = schemes[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]any{}
|
|
||||||
for k, v := range url.Query() {
|
|
||||||
if len(v) > 0 {
|
|
||||||
m[k] = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
|
|
||||||
node := &config.NodeConfig{
|
|
||||||
Addr: url.Host,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := registry.ConnectorRegistry().Get(connector); c == nil {
|
|
||||||
connector = "http"
|
|
||||||
}
|
|
||||||
if d := registry.DialerRegistry().Get(dialer); d == nil {
|
|
||||||
dialer = "tcp"
|
|
||||||
if connector == "ssu" {
|
|
||||||
dialer = "udp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var auth *config.AuthConfig
|
|
||||||
if url.User != nil {
|
|
||||||
auth = &config.AuthConfig{
|
|
||||||
Username: url.User.Username(),
|
|
||||||
}
|
|
||||||
auth.Password, _ = url.User.Password()
|
|
||||||
}
|
|
||||||
|
|
||||||
if sauth := mdutil.GetString(md, "auth"); sauth != "" && auth == nil {
|
|
||||||
au, err := parseAuthFromCmd(sauth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
auth = au
|
|
||||||
}
|
|
||||||
delete(m, "auth")
|
|
||||||
|
|
||||||
tlsConfig := &config.TLSConfig{
|
|
||||||
CertFile: mdutil.GetString(md, "tls.certFile", "certFile", "cert"),
|
|
||||||
KeyFile: mdutil.GetString(md, "tls.keyFile", "keyFile", "key"),
|
|
||||||
CAFile: mdutil.GetString(md, "tls.caFile", "caFile", "ca"),
|
|
||||||
Secure: mdutil.GetBool(md, "tls.secure", "secure"),
|
|
||||||
ServerName: mdutil.GetString(md, "tls.servername", "servername"),
|
|
||||||
}
|
|
||||||
if tlsConfig.ServerName == "" {
|
|
||||||
tlsConfig.ServerName = url.Hostname()
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "tls.certFile")
|
|
||||||
delete(m, "certFile")
|
|
||||||
delete(m, "cert")
|
|
||||||
delete(m, "tls.keyFile")
|
|
||||||
delete(m, "keyFile")
|
|
||||||
delete(m, "key")
|
|
||||||
delete(m, "tls.caFile")
|
|
||||||
delete(m, "caFile")
|
|
||||||
delete(m, "ca")
|
|
||||||
delete(m, "tls.secure")
|
|
||||||
delete(m, "secure")
|
|
||||||
delete(m, "tls.servername")
|
|
||||||
delete(m, "serverName")
|
|
||||||
|
|
||||||
if !tlsConfig.Secure && tlsConfig.CertFile == "" && tlsConfig.CAFile == "" && tlsConfig.ServerName == "" {
|
|
||||||
tlsConfig = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
node.Connector = &config.ConnectorConfig{
|
|
||||||
Type: connector,
|
|
||||||
Auth: auth,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
node.Dialer = &config.DialerConfig{
|
|
||||||
Type: dialer,
|
|
||||||
TLS: tlsConfig,
|
|
||||||
Metadata: m,
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" {
|
|
||||||
node.Connector.Auth = nil
|
|
||||||
node.Dialer.Auth = auth
|
|
||||||
}
|
|
||||||
|
|
||||||
return node, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Norm(s string) (*url.URL, error) {
|
|
||||||
s = strings.TrimSpace(s)
|
|
||||||
if s == "" {
|
|
||||||
return nil, ErrInvalidCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
if s[0] == ':' || !strings.Contains(s, "://") {
|
|
||||||
s = "auto://" + s
|
|
||||||
}
|
|
||||||
|
|
||||||
url, err := url.Parse(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if url.Scheme == "https" {
|
|
||||||
url.Scheme = "http+tls"
|
|
||||||
}
|
|
||||||
|
|
||||||
return url, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseAuthFromCmd(sa string) (*config.AuthConfig, error) {
|
|
||||||
v, err := base64.StdEncoding.DecodeString(sa)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cs := string(v)
|
|
||||||
n := strings.IndexByte(cs, ':')
|
|
||||||
if n < 0 {
|
|
||||||
return &config.AuthConfig{
|
|
||||||
Username: cs,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &config.AuthConfig{
|
|
||||||
Username: cs[:n],
|
|
||||||
Password: cs[n+1:],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSelector(m map[string]any) *config.SelectorConfig {
|
|
||||||
md := mdx.NewMetadata(m)
|
|
||||||
strategy := mdutil.GetString(md, "strategy")
|
|
||||||
maxFails := mdutil.GetInt(md, "maxFails", "max_fails")
|
|
||||||
failTimeout := mdutil.GetDuration(md, "failTimeout", "fail_timeout")
|
|
||||||
if strategy == "" && maxFails <= 0 && failTimeout <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strategy == "" {
|
|
||||||
strategy = "round"
|
|
||||||
}
|
|
||||||
if maxFails <= 0 {
|
|
||||||
maxFails = 1
|
|
||||||
}
|
|
||||||
if failTimeout <= 0 {
|
|
||||||
failTimeout = 30 * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m, "strategy")
|
|
||||||
delete(m, "maxFails")
|
|
||||||
delete(m, "max_fails")
|
|
||||||
delete(m, "failTimeout")
|
|
||||||
delete(m, "fail_timeout")
|
|
||||||
|
|
||||||
return &config.SelectorConfig{
|
|
||||||
Strategy: strategy,
|
|
||||||
MaxFails: maxFails,
|
|
||||||
FailTimeout: failTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
159
config/config.go
159
config/config.go
@ -79,11 +79,6 @@ type LogRotationConfig struct {
|
|||||||
Compress bool `yaml:"compress,omitempty" json:"compress,omitempty"`
|
Compress bool `yaml:"compress,omitempty" json:"compress,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoggerConfig struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProfilingConfig struct {
|
type ProfilingConfig struct {
|
||||||
Addr string `json:"addr"`
|
Addr string `json:"addr"`
|
||||||
}
|
}
|
||||||
@ -249,21 +244,6 @@ type SDConfig struct {
|
|||||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouterRouteConfig struct {
|
|
||||||
Net string `json:"net"`
|
|
||||||
Gateway string `json:"gateway"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouterConfig struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Routes []*RouterRouteConfig `yaml:",omitempty" json:"routes,omitempty"`
|
|
||||||
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
|
|
||||||
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
|
|
||||||
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
|
||||||
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
|
||||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RecorderConfig struct {
|
type RecorderConfig struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
File *FileRecorder `yaml:",omitempty" json:"file,omitempty"`
|
File *FileRecorder `yaml:",omitempty" json:"file,omitempty"`
|
||||||
@ -297,9 +277,9 @@ type RedisRecorder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RecorderObject struct {
|
type RecorderObject struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Record string `json:"record"`
|
Record string `json:"record"`
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
Metadata map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
type LimiterConfig struct {
|
type LimiterConfig struct {
|
||||||
@ -309,12 +289,6 @@ type LimiterConfig struct {
|
|||||||
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
|
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
|
||||||
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
||||||
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
||||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ObserverConfig struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenerConfig struct {
|
type ListenerConfig struct {
|
||||||
@ -337,69 +311,33 @@ type HandlerConfig struct {
|
|||||||
Authers []string `yaml:",omitempty" json:"authers,omitempty"`
|
Authers []string `yaml:",omitempty" json:"authers,omitempty"`
|
||||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||||
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
|
Ingress string `yaml:",omitempty" json:"ingress,omitempty"`
|
||||||
Observer string `yaml:",omitempty" json:"observer,omitempty"`
|
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForwarderConfig struct {
|
type ForwarderConfig struct {
|
||||||
// DEPRECATED by hop field
|
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
|
||||||
// the referenced hop name
|
|
||||||
Hop string `yaml:",omitempty" json:"hop,omitempty"`
|
|
||||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||||
Nodes []*ForwardNodeConfig `json:"nodes"`
|
Nodes []*ForwardNodeConfig `json:"nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForwardNodeConfig struct {
|
type ForwardNodeConfig struct {
|
||||||
Name string `yaml:",omitempty" json:"name,omitempty"`
|
Name string `yaml:",omitempty" json:"name,omitempty"`
|
||||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
||||||
// DEPRECATED by filter.protocol
|
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||||
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||||
// DEPRECATED by filter.host
|
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||||
// DEPRECATED by filter.path
|
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||||
// DEPRECATED by http.auth
|
|
||||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
|
||||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
|
||||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
|
||||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPURLRewriteConfig struct {
|
|
||||||
Match string
|
|
||||||
Replacement string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPBodyRewriteConfig struct {
|
|
||||||
// filter by MIME types
|
|
||||||
Type string
|
|
||||||
Match string
|
|
||||||
Replacement string
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeFilterConfig struct {
|
|
||||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
|
||||||
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
|
||||||
Path string `yaml:",omitempty" json:"path,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPNodeConfig struct {
|
type HTTPNodeConfig struct {
|
||||||
// rewrite host header
|
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||||
Host string `yaml:",omitempty" json:"host,omitempty"`
|
|
||||||
// additional request header
|
|
||||||
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
|
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
|
||||||
// rewrite URL
|
|
||||||
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
|
|
||||||
// rewrite response body
|
|
||||||
RewriteBody []HTTPBodyRewriteConfig `yaml:"rewriteBody,omitempty" json:"rewriteBody,omitempty"`
|
|
||||||
// HTTP basic auth
|
|
||||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLSNodeConfig struct {
|
type TLSNodeConfig struct {
|
||||||
@ -442,36 +380,11 @@ type ServiceConfig struct {
|
|||||||
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
|
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
|
||||||
CLimiter string `yaml:"climiter,omitempty" json:"climiter,omitempty"`
|
CLimiter string `yaml:"climiter,omitempty" json:"climiter,omitempty"`
|
||||||
RLimiter string `yaml:"rlimiter,omitempty" json:"rlimiter,omitempty"`
|
RLimiter string `yaml:"rlimiter,omitempty" json:"rlimiter,omitempty"`
|
||||||
Logger string `yaml:",omitempty" json:"logger,omitempty"`
|
|
||||||
Loggers []string `yaml:",omitempty" json:"loggers,omitempty"`
|
|
||||||
Observer string `yaml:",omitempty" json:"observer,omitempty"`
|
|
||||||
Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
|
Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
|
||||||
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
|
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
|
||||||
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
|
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
|
||||||
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
|
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||||
// service status, read-only
|
|
||||||
Status *ServiceStatus `yaml:",omitempty" json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceStatus struct {
|
|
||||||
CreateTime int64 `yaml:"createTime" json:"createTime"`
|
|
||||||
State string `yaml:"state" json:"state"`
|
|
||||||
Events []ServiceEvent `yaml:",omitempty" json:"events,omitempty"`
|
|
||||||
Stats *ServiceStats `yaml:",omitempty" json:"stats,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceEvent struct {
|
|
||||||
Time int64 `yaml:"time" json:"time"`
|
|
||||||
Msg string `yaml:"msg" json:"msg"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServiceStats struct {
|
|
||||||
TotalConns uint64 `yaml:"totalConns" json:"totalConns"`
|
|
||||||
CurrentConns uint64 `yaml:"currentConns" json:"currentConns"`
|
|
||||||
TotalErrs uint64 `yaml:"totalErrs" json:"totalErrs"`
|
|
||||||
InputBytes uint64 `yaml:"inputBytes" json:"inputBytes"`
|
|
||||||
OutputBytes uint64 `yaml:"outputBytes" json:"outputBytes"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainConfig struct {
|
type ChainConfig struct {
|
||||||
@ -500,26 +413,27 @@ type HopConfig struct {
|
|||||||
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
|
||||||
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
|
||||||
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||||
Network string `yaml:",omitempty" json:"network,omitempty"`
|
Host string `yaml:",omitempty" json:"host,omitempty"`
|
||||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
Network string `yaml:",omitempty" json:"network,omitempty"`
|
||||||
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
|
||||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
Path string `yaml:",omitempty" json:"path,omitempty"`
|
||||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
||||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
||||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||||
Interface string `yaml:",omitempty" json:"interface,omitempty"`
|
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||||
Netns string `yaml:",omitempty" json:"netns,omitempty"`
|
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||||
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
|
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||||
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
|
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||||
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||||
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
||||||
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
|
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
|
||||||
|
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||||
|
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -532,14 +446,11 @@ type Config struct {
|
|||||||
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
|
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
|
||||||
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
|
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
|
||||||
Ingresses []*IngressConfig `yaml:",omitempty" json:"ingresses,omitempty"`
|
Ingresses []*IngressConfig `yaml:",omitempty" json:"ingresses,omitempty"`
|
||||||
Routers []*RouterConfig `yaml:",omitempty" json:"routers,omitempty"`
|
|
||||||
SDs []*SDConfig `yaml:"sds,omitempty" json:"sds,omitempty"`
|
SDs []*SDConfig `yaml:"sds,omitempty" json:"sds,omitempty"`
|
||||||
Recorders []*RecorderConfig `yaml:",omitempty" json:"recorders,omitempty"`
|
Recorders []*RecorderConfig `yaml:",omitempty" json:"recorders,omitempty"`
|
||||||
Limiters []*LimiterConfig `yaml:",omitempty" json:"limiters,omitempty"`
|
Limiters []*LimiterConfig `yaml:",omitempty" json:"limiters,omitempty"`
|
||||||
CLimiters []*LimiterConfig `yaml:"climiters,omitempty" json:"climiters,omitempty"`
|
CLimiters []*LimiterConfig `yaml:"climiters,omitempty" json:"climiters,omitempty"`
|
||||||
RLimiters []*LimiterConfig `yaml:"rlimiters,omitempty" json:"rlimiters,omitempty"`
|
RLimiters []*LimiterConfig `yaml:"rlimiters,omitempty" json:"rlimiters,omitempty"`
|
||||||
Observers []*ObserverConfig `yaml:",omitempty" json:"observers,omitempty"`
|
|
||||||
Loggers []*LoggerConfig `yaml:",omitempty" json:"loggers,omitempty"`
|
|
||||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||||
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
|
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
|
||||||
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
|
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/go-gost/core/admission"
|
"github.com/go-gost/core/admission"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
xadmission "github.com/go-gost/x/admission"
|
xadmission "github.com/go-gost/x/admission"
|
||||||
admission_plugin "github.com/go-gost/x/admission/plugin"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
@ -29,13 +28,13 @@ func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return admission_plugin.NewHTTPPlugin(
|
return xadmission.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return admission_plugin.NewGRPCPlugin(
|
return xadmission.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/go-gost/core/auth"
|
"github.com/go-gost/core/auth"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
xauth "github.com/go-gost/x/auth"
|
xauth "github.com/go-gost/x/auth"
|
||||||
auth_plugin "github.com/go-gost/x/auth/plugin"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
@ -29,13 +28,13 @@ func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
|||||||
}
|
}
|
||||||
switch cfg.Plugin.Type {
|
switch cfg.Plugin.Type {
|
||||||
case "http":
|
case "http":
|
||||||
return auth_plugin.NewHTTPPlugin(
|
return xauth.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return auth_plugin.NewGRPCPlugin(
|
return xauth.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/go-gost/core/bypass"
|
"github.com/go-gost/core/bypass"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
xbypass "github.com/go-gost/x/bypass"
|
xbypass "github.com/go-gost/x/bypass"
|
||||||
bypass_plugin "github.com/go-gost/x/bypass/plugin"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
@ -29,13 +28,13 @@ func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return bypass_plugin.NewHTTPPlugin(
|
return xbypass.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return bypass_plugin.NewGRPCPlugin(
|
return xbypass.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -12,12 +12,12 @@ import (
|
|||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseChain(cfg *config.ChainConfig, log logger.Logger) (chain.Chainer, error) {
|
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
chainLogger := log.WithFields(map[string]any{
|
chainLogger := logger.Default().WithFields(map[string]any{
|
||||||
"kind": "chain",
|
"kind": "chain",
|
||||||
"chain": cfg.Name,
|
"chain": cfg.Name,
|
||||||
})
|
})
|
||||||
@ -37,7 +37,7 @@ func ParseChain(cfg *config.ChainConfig, log logger.Logger) (chain.Chainer, erro
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if ch.Nodes != nil || ch.Plugin != nil {
|
if ch.Nodes != nil || ch.Plugin != nil {
|
||||||
if hop, err = hop_parser.ParseHop(ch, log); err != nil {
|
if hop, err = hop_parser.ParseHop(ch); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,20 +8,16 @@ import (
|
|||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
"github.com/go-gost/core/hop"
|
"github.com/go-gost/core/hop"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
|
||||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||||
node_parser "github.com/go-gost/x/config/parsing/node"
|
node_parser "github.com/go-gost/x/config/parsing/node"
|
||||||
selector_parser "github.com/go-gost/x/config/parsing/selector"
|
selector_parser "github.com/go-gost/x/config/parsing/selector"
|
||||||
xhop "github.com/go-gost/x/hop"
|
xhop "github.com/go-gost/x/hop"
|
||||||
hop_plugin "github.com/go-gost/x/hop/plugin"
|
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
"github.com/go-gost/x/metadata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -35,14 +31,14 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case plugin.HTTP:
|
case "http":
|
||||||
return hop_plugin.NewHTTPPlugin(
|
return xhop.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
), nil
|
), nil
|
||||||
default:
|
default:
|
||||||
return hop_plugin.NewGRPCPlugin(
|
return xhop.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
@ -50,16 +46,6 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ifce := cfg.Interface
|
|
||||||
var netns string
|
|
||||||
if cfg.Metadata != nil {
|
|
||||||
md := metadata.NewMetadata(cfg.Metadata)
|
|
||||||
if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" {
|
|
||||||
ifce = v
|
|
||||||
}
|
|
||||||
netns = mdutil.GetString(md, "netns")
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodes []*chain.Node
|
var nodes []*chain.Node
|
||||||
for _, v := range cfg.Nodes {
|
for _, v := range cfg.Nodes {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@ -73,31 +59,25 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
v.Hosts = cfg.Hosts
|
v.Hosts = cfg.Hosts
|
||||||
}
|
}
|
||||||
if v.Interface == "" {
|
if v.Interface == "" {
|
||||||
v.Interface = ifce
|
v.Interface = cfg.Interface
|
||||||
}
|
}
|
||||||
if v.Netns == "" {
|
|
||||||
v.Netns = netns
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.SockOpts == nil {
|
if v.SockOpts == nil {
|
||||||
v.SockOpts = cfg.SockOpts
|
v.SockOpts = cfg.SockOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Connector == nil {
|
if v.Connector == nil {
|
||||||
v.Connector = &config.ConnectorConfig{}
|
v.Connector = &config.ConnectorConfig{
|
||||||
}
|
Type: "http",
|
||||||
if strings.TrimSpace(v.Connector.Type) == "" {
|
}
|
||||||
v.Connector.Type = "http"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Dialer == nil {
|
if v.Dialer == nil {
|
||||||
v.Dialer = &config.DialerConfig{}
|
v.Dialer = &config.DialerConfig{
|
||||||
}
|
Type: "tcp",
|
||||||
if strings.TrimSpace(v.Dialer.Type) == "" {
|
}
|
||||||
v.Dialer.Type = "tcp"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := node_parser.ParseNode(cfg.Name, v, log)
|
node, err := node_parser.ParseNode(cfg.Name, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -117,7 +97,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
|
|||||||
xhop.SelectorOption(sel),
|
xhop.SelectorOption(sel),
|
||||||
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||||
xhop.ReloadPeriodOption(cfg.Reload),
|
xhop.ReloadPeriodOption(cfg.Reload),
|
||||||
xhop.LoggerOption(log.WithFields(map[string]any{
|
xhop.LoggerOption(logger.Default().WithFields(map[string]any{
|
||||||
"kind": "hop",
|
"kind": "hop",
|
||||||
"hop": cfg.Name,
|
"hop": cfg.Name,
|
||||||
})),
|
})),
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
xhosts "github.com/go-gost/x/hosts"
|
xhosts "github.com/go-gost/x/hosts"
|
||||||
hosts_plugin "github.com/go-gost/x/hosts/plugin"
|
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
)
|
)
|
||||||
@ -29,13 +28,13 @@ func ParseHostMapper(cfg *config.HostsConfig) hosts.HostMapper {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return hosts_plugin.NewHTTPPlugin(
|
return xhosts.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return hosts_plugin.NewGRPCPlugin(
|
return xhosts.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
xingress "github.com/go-gost/x/ingress"
|
xingress "github.com/go-gost/x/ingress"
|
||||||
ingress_plugin "github.com/go-gost/x/ingress/plugin"
|
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
)
|
)
|
||||||
@ -28,13 +27,13 @@ func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return ingress_plugin.NewHTTPPlugin(
|
return xingress.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return ingress_plugin.NewGRPCPlugin(
|
return xingress.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
@ -42,13 +41,13 @@ func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var rules []*ingress.Rule
|
var rules []xingress.Rule
|
||||||
for _, rule := range cfg.Rules {
|
for _, rule := range cfg.Rules {
|
||||||
if rule.Hostname == "" || rule.Endpoint == "" {
|
if rule.Hostname == "" || rule.Endpoint == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rules = append(rules, &ingress.Rule{
|
rules = append(rules, xingress.Rule{
|
||||||
Hostname: rule.Hostname,
|
Hostname: rule.Hostname,
|
||||||
Endpoint: rule.Endpoint,
|
Endpoint: rule.Endpoint,
|
||||||
})
|
})
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
package limiter
|
package limiter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/limiter/conn"
|
"github.com/go-gost/core/limiter/conn"
|
||||||
"github.com/go-gost/core/limiter/rate"
|
"github.com/go-gost/core/limiter/rate"
|
||||||
"github.com/go-gost/core/limiter/traffic"
|
"github.com/go-gost/core/limiter/traffic"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/loader"
|
"github.com/go-gost/x/internal/loader"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
xconn "github.com/go-gost/x/limiter/conn"
|
xconn "github.com/go-gost/x/limiter/conn"
|
||||||
xrate "github.com/go-gost/x/limiter/rate"
|
xrate "github.com/go-gost/x/limiter/rate"
|
||||||
xtraffic "github.com/go-gost/x/limiter/traffic"
|
xtraffic "github.com/go-gost/x/limiter/traffic"
|
||||||
traffic_plugin "github.com/go-gost/x/limiter/traffic/plugin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter) {
|
func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter) {
|
||||||
@ -22,30 +17,6 @@ func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Plugin != nil {
|
|
||||||
var tlsCfg *tls.Config
|
|
||||||
if cfg.Plugin.TLS != nil {
|
|
||||||
tlsCfg = &tls.Config{
|
|
||||||
ServerName: cfg.Plugin.TLS.ServerName,
|
|
||||||
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
|
||||||
case "http":
|
|
||||||
return traffic_plugin.NewHTTPPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return traffic_plugin.NewGRPCPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts []xtraffic.Option
|
var opts []xtraffic.Option
|
||||||
|
|
||||||
if cfg.File != nil && cfg.File.Path != "" {
|
if cfg.File != nil && cfg.File.Path != "" {
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
xlogger "github.com/go-gost/x/logger"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseLogger(cfg *config.LoggerConfig) logger.Logger {
|
|
||||||
if cfg == nil || cfg.Log == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
opts := []xlogger.Option{
|
|
||||||
xlogger.NameOption(cfg.Name),
|
|
||||||
xlogger.FormatOption(logger.LogFormat(cfg.Log.Format)),
|
|
||||||
xlogger.LevelOption(logger.LogLevel(cfg.Log.Level)),
|
|
||||||
}
|
|
||||||
|
|
||||||
var out io.Writer = os.Stderr
|
|
||||||
switch cfg.Log.Output {
|
|
||||||
case "none", "null":
|
|
||||||
return xlogger.Nop()
|
|
||||||
case "stdout":
|
|
||||||
out = os.Stdout
|
|
||||||
case "stderr", "":
|
|
||||||
out = os.Stderr
|
|
||||||
default:
|
|
||||||
if cfg.Log.Rotation != nil {
|
|
||||||
out = &lumberjack.Logger{
|
|
||||||
Filename: cfg.Log.Output,
|
|
||||||
MaxSize: cfg.Log.Rotation.MaxSize,
|
|
||||||
MaxAge: cfg.Log.Rotation.MaxAge,
|
|
||||||
MaxBackups: cfg.Log.Rotation.MaxBackups,
|
|
||||||
LocalTime: cfg.Log.Rotation.LocalTime,
|
|
||||||
Compress: cfg.Log.Rotation.Compress,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os.MkdirAll(filepath.Dir(cfg.Log.Output), 0755)
|
|
||||||
f, err := os.OpenFile(cfg.Log.Output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
logger.Default().Warn(err)
|
|
||||||
} else {
|
|
||||||
out = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts = append(opts, xlogger.OutputOption(out))
|
|
||||||
|
|
||||||
return xlogger.NewLogger(opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func List(name string, names ...string) []logger.Logger {
|
|
||||||
var loggers []logger.Logger
|
|
||||||
if adm := registry.LoggerRegistry().Get(name); adm != nil {
|
|
||||||
loggers = append(loggers, adm)
|
|
||||||
}
|
|
||||||
for _, s := range names {
|
|
||||||
if lg := registry.LoggerRegistry().Get(s); lg != nil {
|
|
||||||
loggers = append(loggers, lg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return loggers
|
|
||||||
}
|
|
@ -3,8 +3,8 @@ package node
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-gost/core/bypass"
|
"github.com/go-gost/core/bypass"
|
||||||
"github.com/go-gost/core/chain"
|
"github.com/go-gost/core/chain"
|
||||||
@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/go-gost/core/metadata"
|
"github.com/go-gost/core/metadata"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
xauth "github.com/go-gost/x/auth"
|
xauth "github.com/go-gost/x/auth"
|
||||||
xchain "github.com/go-gost/x/chain"
|
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/config/parsing"
|
"github.com/go-gost/x/config/parsing"
|
||||||
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||||
@ -24,7 +23,7 @@ import (
|
|||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
|
func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -41,7 +40,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeLogger := log.WithFields(map[string]any{
|
nodeLogger := logger.Default().WithFields(map[string]any{
|
||||||
"hop": hop,
|
"hop": hop,
|
||||||
"kind": "node",
|
"kind": "node",
|
||||||
"node": cfg.Name,
|
"node": cfg.Name,
|
||||||
@ -140,77 +139,40 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := xchain.NewTransport(d, cr,
|
tr := chain.NewTransport(d, cr,
|
||||||
chain.AddrTransportOption(cfg.Addr),
|
chain.AddrTransportOption(cfg.Addr),
|
||||||
chain.InterfaceTransportOption(cfg.Interface),
|
chain.InterfaceTransportOption(cfg.Interface),
|
||||||
chain.NetnsTransportOption(cfg.Netns),
|
|
||||||
chain.SockOptsTransportOption(sockOpts),
|
chain.SockOptsTransportOption(sockOpts),
|
||||||
|
chain.TimeoutTransportOption(10*time.Second),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// convert *.example.com to .example.com
|
||||||
|
// convert *example.com to example.com
|
||||||
|
host := cfg.Host
|
||||||
|
if strings.HasPrefix(host, "*") {
|
||||||
|
host = host[1:]
|
||||||
|
if !strings.HasPrefix(host, ".") {
|
||||||
|
host = "." + host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opts := []chain.NodeOption{
|
opts := []chain.NodeOption{
|
||||||
chain.TransportNodeOption(tr),
|
chain.TransportNodeOption(tr),
|
||||||
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||||
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
||||||
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
||||||
chain.MetadataNodeOption(nm),
|
chain.MetadataNodeOption(nm),
|
||||||
|
chain.HostNodeOption(host),
|
||||||
|
chain.ProtocolNodeOption(cfg.Protocol),
|
||||||
|
chain.PathNodeOption(cfg.Path),
|
||||||
chain.NetworkNodeOption(cfg.Network),
|
chain.NetworkNodeOption(cfg.Network),
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter := cfg.Filter; filter != nil {
|
|
||||||
// convert *.example.com to .example.com
|
|
||||||
// convert *example.com to example.com
|
|
||||||
host := filter.Host
|
|
||||||
if strings.HasPrefix(host, "*") {
|
|
||||||
host = host[1:]
|
|
||||||
if !strings.HasPrefix(host, ".") {
|
|
||||||
host = "." + host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
settings := &chain.NodeFilterSettings{
|
|
||||||
Protocol: filter.Protocol,
|
|
||||||
Host: host,
|
|
||||||
Path: filter.Path,
|
|
||||||
}
|
|
||||||
opts = append(opts, chain.NodeFilterOption(settings))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.HTTP != nil {
|
if cfg.HTTP != nil {
|
||||||
settings := &chain.HTTPNodeSettings{
|
opts = append(opts, chain.HTTPNodeOption(&chain.HTTPNodeSettings{
|
||||||
Host: cfg.HTTP.Host,
|
Host: cfg.HTTP.Host,
|
||||||
Header: cfg.HTTP.Header,
|
Header: cfg.HTTP.Header,
|
||||||
}
|
}))
|
||||||
|
|
||||||
if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
|
|
||||||
settings.Auther = xauth.NewAuthenticator(
|
|
||||||
xauth.AuthsOption(map[string]string{auth.Username: auth.Password}),
|
|
||||||
xauth.LoggerOption(log.WithFields(map[string]any{
|
|
||||||
"kind": "node",
|
|
||||||
"node": cfg.Name,
|
|
||||||
"addr": cfg.Addr,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for _, v := range cfg.HTTP.Rewrite {
|
|
||||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
|
||||||
settings.RewriteURL = append(settings.RewriteURL, chain.HTTPURLRewriteSetting{
|
|
||||||
Pattern: pattern,
|
|
||||||
Replacement: v.Replacement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, v := range cfg.HTTP.RewriteBody {
|
|
||||||
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
|
|
||||||
settings.RewriteBody = append(settings.RewriteBody, chain.HTTPBodyRewriteSettings{
|
|
||||||
Type: v.Type,
|
|
||||||
Pattern: pattern,
|
|
||||||
Replacement: []byte(v.Replacement),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opts = append(opts, chain.HTTPNodeOption(settings))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.TLS != nil {
|
if cfg.TLS != nil {
|
||||||
tlsCfg := &chain.TLSNodeSettings{
|
tlsCfg := &chain.TLSNodeSettings{
|
||||||
ServerName: cfg.TLS.ServerName,
|
ServerName: cfg.TLS.ServerName,
|
||||||
@ -223,5 +185,18 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
|
|||||||
}
|
}
|
||||||
opts = append(opts, chain.TLSNodeOption(tlsCfg))
|
opts = append(opts, chain.TLSNodeOption(tlsCfg))
|
||||||
}
|
}
|
||||||
|
if cfg.Auth != nil {
|
||||||
|
opts = append(opts, chain.AutherNodeOption(
|
||||||
|
xauth.NewAuthenticator(
|
||||||
|
xauth.AuthsOption(map[string]string{cfg.Auth.Username: cfg.Auth.Password}),
|
||||||
|
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
|
||||||
|
"kind": "node",
|
||||||
|
"node": cfg.Name,
|
||||||
|
"addr": cfg.Addr,
|
||||||
|
"host": cfg.Host,
|
||||||
|
"protocol": cfg.Protocol,
|
||||||
|
})),
|
||||||
|
)))
|
||||||
|
}
|
||||||
return chain.NewNode(cfg.Name, cfg.Addr, opts...), nil
|
return chain.NewNode(cfg.Name, cfg.Addr, opts...), nil
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
package observer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/observer"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
observer_plugin "github.com/go-gost/x/observer/plugin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseObserver(cfg *config.ObserverConfig) observer.Observer {
|
|
||||||
if cfg == nil || cfg.Plugin == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var tlsCfg *tls.Config
|
|
||||||
if cfg.Plugin.TLS != nil {
|
|
||||||
tlsCfg = &tls.Config{
|
|
||||||
ServerName: cfg.Plugin.TLS.ServerName,
|
|
||||||
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
|
||||||
case "http":
|
|
||||||
return observer_plugin.NewHTTPPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return observer_plugin.NewGRPCPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ const (
|
|||||||
MDKeyPostUp = "postUp"
|
MDKeyPostUp = "postUp"
|
||||||
MDKeyPostDown = "postDown"
|
MDKeyPostDown = "postDown"
|
||||||
MDKeyIgnoreChain = "ignoreChain"
|
MDKeyIgnoreChain = "ignoreChain"
|
||||||
MDKeyEnableStats = "enableStats"
|
|
||||||
|
|
||||||
MDKeyRecorderDirection = "direction"
|
MDKeyRecorderDirection = "direction"
|
||||||
MDKeyRecorderTimestampFormat = "timeStampFormat"
|
MDKeyRecorderTimestampFormat = "timeStampFormat"
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
xrecorder "github.com/go-gost/x/recorder"
|
xrecorder "github.com/go-gost/x/recorder"
|
||||||
recorder_plugin "github.com/go-gost/x/recorder/plugin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
||||||
@ -26,13 +25,13 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return recorder_plugin.NewHTTPPlugin(
|
return xrecorder.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return recorder_plugin.NewGRPCPlugin(
|
return xrecorder.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
xresolver "github.com/go-gost/x/resolver"
|
xresolver "github.com/go-gost/x/resolver"
|
||||||
resolver_plugin "github.com/go-gost/x/resolver/plugin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
||||||
@ -29,13 +28,13 @@ func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return resolver_plugin.NewHTTPPlugin(
|
return xresolver.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
), nil
|
), nil
|
||||||
default:
|
default:
|
||||||
return resolver_plugin.NewGRPCPlugin(
|
return xresolver.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
package router
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
"github.com/go-gost/core/router"
|
|
||||||
"github.com/go-gost/x/config"
|
|
||||||
"github.com/go-gost/x/internal/loader"
|
|
||||||
"github.com/go-gost/x/internal/plugin"
|
|
||||||
xrouter "github.com/go-gost/x/router"
|
|
||||||
router_plugin "github.com/go-gost/x/router/plugin"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseRouter(cfg *config.RouterConfig) router.Router {
|
|
||||||
if cfg == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Plugin != nil {
|
|
||||||
var tlsCfg *tls.Config
|
|
||||||
if cfg.Plugin.TLS != nil {
|
|
||||||
tlsCfg = &tls.Config{
|
|
||||||
ServerName: cfg.Plugin.TLS.ServerName,
|
|
||||||
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
|
||||||
case "http":
|
|
||||||
return router_plugin.NewHTTPPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
|
||||||
)
|
|
||||||
default:
|
|
||||||
return router_plugin.NewGRPCPlugin(
|
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var routes []*router.Route
|
|
||||||
for _, route := range cfg.Routes {
|
|
||||||
_, ipNet, _ := net.ParseCIDR(route.Net)
|
|
||||||
if ipNet == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
gw := net.ParseIP(route.Gateway)
|
|
||||||
if gw == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
routes = append(routes, &router.Route{
|
|
||||||
Net: ipNet,
|
|
||||||
Gateway: gw,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
opts := []xrouter.Option{
|
|
||||||
xrouter.RoutesOption(routes),
|
|
||||||
xrouter.ReloadPeriodOption(cfg.Reload),
|
|
||||||
xrouter.LoggerOption(logger.Default().WithFields(map[string]any{
|
|
||||||
"kind": "router",
|
|
||||||
"router": cfg.Name,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
if cfg.File != nil && cfg.File.Path != "" {
|
|
||||||
opts = append(opts, xrouter.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
|
|
||||||
}
|
|
||||||
if cfg.Redis != nil && cfg.Redis.Addr != "" {
|
|
||||||
switch cfg.Redis.Type {
|
|
||||||
case "list": // rediss list
|
|
||||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisListLoader(
|
|
||||||
cfg.Redis.Addr,
|
|
||||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
|
||||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
|
||||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
|
||||||
)))
|
|
||||||
case "set": // redis set
|
|
||||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisSetLoader(
|
|
||||||
cfg.Redis.Addr,
|
|
||||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
|
||||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
|
||||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
|
||||||
)))
|
|
||||||
default: // redis hash
|
|
||||||
opts = append(opts, xrouter.RedisLoaderOption(loader.RedisHashLoader(
|
|
||||||
cfg.Redis.Addr,
|
|
||||||
loader.DBRedisLoaderOption(cfg.Redis.DB),
|
|
||||||
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
|
|
||||||
loader.KeyRedisLoaderOption(cfg.Redis.Key),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
|
|
||||||
opts = append(opts, xrouter.HTTPLoaderOption(loader.HTTPLoader(
|
|
||||||
cfg.HTTP.URL,
|
|
||||||
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
return xrouter.NewRouter(opts...)
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/go-gost/core/sd"
|
"github.com/go-gost/core/sd"
|
||||||
"github.com/go-gost/x/config"
|
"github.com/go-gost/x/config"
|
||||||
"github.com/go-gost/x/internal/plugin"
|
"github.com/go-gost/x/internal/plugin"
|
||||||
sd_plugin "github.com/go-gost/x/sd/plugin"
|
xsd "github.com/go-gost/x/sd"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseSD(cfg *config.SDConfig) sd.SD {
|
func ParseSD(cfg *config.SDConfig) sd.SD {
|
||||||
@ -24,13 +24,13 @@ func ParseSD(cfg *config.SDConfig) sd.SD {
|
|||||||
}
|
}
|
||||||
switch strings.ToLower(cfg.Plugin.Type) {
|
switch strings.ToLower(cfg.Plugin.Type) {
|
||||||
case "http":
|
case "http":
|
||||||
return sd_plugin.NewHTTPPlugin(
|
return xsd.NewHTTPPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
plugin.TimeoutOption(cfg.Plugin.Timeout),
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return sd_plugin.NewGRPCPlugin(
|
return xsd.NewGRPCPlugin(
|
||||||
cfg.Name, cfg.Plugin.Addr,
|
cfg.Name, cfg.Plugin.Addr,
|
||||||
plugin.TokenOption(cfg.Plugin.Token),
|
plugin.TokenOption(cfg.Plugin.Token),
|
||||||
plugin.TLSConfigOption(tlsCfg),
|
plugin.TLSConfigOption(tlsCfg),
|
||||||
|
@ -2,9 +2,6 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/admission"
|
"github.com/go-gost/core/admission"
|
||||||
"github.com/go-gost/core/auth"
|
"github.com/go-gost/core/auth"
|
||||||
@ -15,7 +12,6 @@ import (
|
|||||||
"github.com/go-gost/core/listener"
|
"github.com/go-gost/core/listener"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
"github.com/go-gost/core/observer/stats"
|
|
||||||
"github.com/go-gost/core/recorder"
|
"github.com/go-gost/core/recorder"
|
||||||
"github.com/go-gost/core/selector"
|
"github.com/go-gost/core/selector"
|
||||||
"github.com/go-gost/core/service"
|
"github.com/go-gost/core/service"
|
||||||
@ -26,49 +22,42 @@ import (
|
|||||||
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||||
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
|
||||||
hop_parser "github.com/go-gost/x/config/parsing/hop"
|
hop_parser "github.com/go-gost/x/config/parsing/hop"
|
||||||
logger_parser "github.com/go-gost/x/config/parsing/logger"
|
|
||||||
selector_parser "github.com/go-gost/x/config/parsing/selector"
|
selector_parser "github.com/go-gost/x/config/parsing/selector"
|
||||||
tls_util "github.com/go-gost/x/internal/util/tls"
|
tls_util "github.com/go-gost/x/internal/util/tls"
|
||||||
"github.com/go-gost/x/metadata"
|
"github.com/go-gost/x/metadata"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
xservice "github.com/go-gost/x/service"
|
xservice "github.com/go-gost/x/service"
|
||||||
"github.com/vishvananda/netns"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||||
if cfg.Listener == nil {
|
if cfg.Listener == nil {
|
||||||
cfg.Listener = &config.ListenerConfig{}
|
cfg.Listener = &config.ListenerConfig{
|
||||||
|
Type: "tcp",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(cfg.Listener.Type) == "" {
|
|
||||||
cfg.Listener.Type = "tcp"
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Handler == nil {
|
if cfg.Handler == nil {
|
||||||
cfg.Handler = &config.HandlerConfig{}
|
cfg.Handler = &config.HandlerConfig{
|
||||||
|
Type: "auto",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(cfg.Handler.Type) == "" {
|
serviceLogger := logger.Default().WithFields(map[string]any{
|
||||||
cfg.Handler.Type = "auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
log := logger.Default()
|
|
||||||
if loggers := logger_parser.List(cfg.Logger, cfg.Loggers...); len(loggers) > 0 {
|
|
||||||
log = logger.LoggerGroup(loggers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceLogger := log.WithFields(map[string]any{
|
|
||||||
"kind": "service",
|
"kind": "service",
|
||||||
"service": cfg.Name,
|
"service": cfg.Name,
|
||||||
"listener": cfg.Listener.Type,
|
"listener": cfg.Listener.Type,
|
||||||
"handler": cfg.Handler.Type,
|
"handler": cfg.Handler.Type,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
listenerLogger := serviceLogger.WithFields(map[string]any{
|
||||||
|
"kind": "listener",
|
||||||
|
})
|
||||||
|
|
||||||
tlsCfg := cfg.Listener.TLS
|
tlsCfg := cfg.Listener.TLS
|
||||||
if tlsCfg == nil {
|
if tlsCfg == nil {
|
||||||
tlsCfg = &config.TLSConfig{}
|
tlsCfg = &config.TLSConfig{}
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls_util.LoadServerConfig(tlsCfg)
|
tlsConfig, err := tls_util.LoadServerConfig(tlsCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serviceLogger.Error(err)
|
listenerLogger.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if tlsConfig == nil {
|
if tlsConfig == nil {
|
||||||
@ -99,10 +88,6 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
ifce := cfg.Interface
|
ifce := cfg.Interface
|
||||||
var preUp, preDown, postUp, postDown []string
|
var preUp, preDown, postUp, postDown []string
|
||||||
var ignoreChain bool
|
var ignoreChain bool
|
||||||
var pStats *stats.Stats
|
|
||||||
var observePeriod time.Duration
|
|
||||||
var netnsIn, netnsOut string
|
|
||||||
var dialTimeout time.Duration
|
|
||||||
if cfg.Metadata != nil {
|
if cfg.Metadata != nil {
|
||||||
md := metadata.NewMetadata(cfg.Metadata)
|
md := metadata.NewMetadata(cfg.Metadata)
|
||||||
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
|
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
|
||||||
@ -119,83 +104,31 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
postUp = mdutil.GetStrings(md, parsing.MDKeyPostUp)
|
postUp = mdutil.GetStrings(md, parsing.MDKeyPostUp)
|
||||||
postDown = mdutil.GetStrings(md, parsing.MDKeyPostDown)
|
postDown = mdutil.GetStrings(md, parsing.MDKeyPostDown)
|
||||||
ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
|
ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
|
||||||
|
|
||||||
if mdutil.GetBool(md, parsing.MDKeyEnableStats) {
|
|
||||||
pStats = &stats.Stats{}
|
|
||||||
}
|
|
||||||
observePeriod = mdutil.GetDuration(md, "observePeriod")
|
|
||||||
netnsIn = mdutil.GetString(md, "netns")
|
|
||||||
netnsOut = mdutil.GetString(md, "netns.out")
|
|
||||||
dialTimeout = mdutil.GetDuration(md, "dialTimeout")
|
|
||||||
}
|
|
||||||
|
|
||||||
listenerLogger := serviceLogger.WithFields(map[string]any{
|
|
||||||
"kind": "listener",
|
|
||||||
})
|
|
||||||
|
|
||||||
routerOpts := []chain.RouterOption{
|
|
||||||
chain.TimeoutRouterOption(dialTimeout),
|
|
||||||
chain.InterfaceRouterOption(ifce),
|
|
||||||
chain.NetnsRouterOption(netnsOut),
|
|
||||||
chain.SockOptsRouterOption(sockOpts),
|
|
||||||
chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
|
||||||
chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
|
||||||
chain.LoggerRouterOption(listenerLogger),
|
|
||||||
}
|
|
||||||
if !ignoreChain {
|
|
||||||
routerOpts = append(routerOpts,
|
|
||||||
chain.ChainRouterOption(chainGroup(cfg.Listener.Chain, cfg.Listener.ChainGroup)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenOpts := []listener.Option{
|
listenOpts := []listener.Option{
|
||||||
listener.AddrOption(cfg.Addr),
|
listener.AddrOption(cfg.Addr),
|
||||||
listener.RouterOption(xchain.NewRouter(routerOpts...)),
|
|
||||||
listener.AutherOption(auther),
|
listener.AutherOption(auther),
|
||||||
listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
|
listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
|
||||||
listener.TLSConfigOption(tlsConfig),
|
listener.TLSConfigOption(tlsConfig),
|
||||||
listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
|
||||||
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
|
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
|
||||||
listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
|
listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
|
||||||
|
listener.LoggerOption(listenerLogger),
|
||||||
listener.ServiceOption(cfg.Name),
|
listener.ServiceOption(cfg.Name),
|
||||||
listener.ProxyProtocolOption(ppv),
|
listener.ProxyProtocolOption(ppv),
|
||||||
listener.StatsOption(pStats),
|
|
||||||
listener.NetnsOption(netnsIn),
|
|
||||||
listener.LoggerOption(listenerLogger),
|
|
||||||
}
|
}
|
||||||
|
if !ignoreChain {
|
||||||
if netnsIn != "" {
|
listenOpts = append(listenOpts,
|
||||||
runtime.LockOSThread()
|
listener.ChainOption(chainGroup(cfg.Listener.Chain, cfg.Listener.ChainGroup)),
|
||||||
defer runtime.UnlockOSThread()
|
)
|
||||||
|
|
||||||
originNs, err := netns.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("netns.Get(): %v", err)
|
|
||||||
}
|
|
||||||
defer netns.Set(originNs)
|
|
||||||
|
|
||||||
var ns netns.NsHandle
|
|
||||||
|
|
||||||
if strings.HasPrefix(netnsIn, "/") {
|
|
||||||
ns, err = netns.GetFromPath(netnsIn)
|
|
||||||
} else {
|
|
||||||
ns, err = netns.GetFromName(netnsIn)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("netns.Get(%s): %v", netnsIn, err)
|
|
||||||
}
|
|
||||||
defer ns.Close()
|
|
||||||
|
|
||||||
if err := netns.Set(ns); err != nil {
|
|
||||||
return nil, fmt.Errorf("netns.Set(%s): %v", netnsIn, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ln listener.Listener
|
var ln listener.Listener
|
||||||
if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil {
|
if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil {
|
||||||
ln = rf(listenOpts...)
|
ln = rf(listenOpts...)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unknown listener: %s", cfg.Listener.Type)
|
return nil, fmt.Errorf("unregistered listener: %s", cfg.Listener.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Listener.Metadata == nil {
|
if cfg.Listener.Metadata == nil {
|
||||||
@ -250,11 +183,10 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
routerOpts = []chain.RouterOption{
|
routerOpts := []chain.RouterOption{
|
||||||
chain.RetriesRouterOption(cfg.Handler.Retries),
|
chain.RetriesRouterOption(cfg.Handler.Retries),
|
||||||
chain.TimeoutRouterOption(dialTimeout),
|
// chain.TimeoutRouterOption(10*time.Second),
|
||||||
chain.InterfaceRouterOption(ifce),
|
chain.InterfaceRouterOption(ifce),
|
||||||
chain.NetnsRouterOption(netnsOut),
|
|
||||||
chain.SockOptsRouterOption(sockOpts),
|
chain.SockOptsRouterOption(sockOpts),
|
||||||
chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)),
|
||||||
chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)),
|
||||||
@ -266,28 +198,26 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
chain.ChainRouterOption(chainGroup(cfg.Handler.Chain, cfg.Handler.ChainGroup)),
|
chain.ChainRouterOption(chainGroup(cfg.Handler.Chain, cfg.Handler.ChainGroup)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
router := chain.NewRouter(routerOpts...)
|
||||||
|
|
||||||
var h handler.Handler
|
var h handler.Handler
|
||||||
if rf := registry.HandlerRegistry().Get(cfg.Handler.Type); rf != nil {
|
if rf := registry.HandlerRegistry().Get(cfg.Handler.Type); rf != nil {
|
||||||
h = rf(
|
h = rf(
|
||||||
handler.RouterOption(xchain.NewRouter(routerOpts...)),
|
handler.RouterOption(router),
|
||||||
handler.AutherOption(auther),
|
handler.AutherOption(auther),
|
||||||
handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
|
handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
|
||||||
handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
|
||||||
handler.TLSConfigOption(tlsConfig),
|
handler.TLSConfigOption(tlsConfig),
|
||||||
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
|
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
|
||||||
handler.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Handler.Limiter)),
|
|
||||||
handler.ObserverOption(registry.ObserverRegistry().Get(cfg.Handler.Observer)),
|
|
||||||
handler.LoggerOption(handlerLogger),
|
handler.LoggerOption(handlerLogger),
|
||||||
handler.ServiceOption(cfg.Name),
|
handler.ServiceOption(cfg.Name),
|
||||||
handler.NetnsOption(netnsIn),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("unknown handler: %s", cfg.Handler.Type)
|
return nil, fmt.Errorf("unregistered handler: %s", cfg.Handler.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if forwarder, ok := h.(handler.Forwarder); ok {
|
if forwarder, ok := h.(handler.Forwarder); ok {
|
||||||
hop, err := parseForwarder(cfg.Forwarder, log)
|
hop, err := parseForwarder(cfg.Forwarder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -310,9 +240,6 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
xservice.PostUpOption(postUp),
|
xservice.PostUpOption(postUp),
|
||||||
xservice.PostDownOption(postDown),
|
xservice.PostDownOption(postDown),
|
||||||
xservice.RecordersOption(recorders...),
|
xservice.RecordersOption(recorders...),
|
||||||
xservice.StatsOption(pStats),
|
|
||||||
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
|
|
||||||
xservice.ObservePeriodOption(observePeriod),
|
|
||||||
xservice.LoggerOption(serviceLogger),
|
xservice.LoggerOption(serviceLogger),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -320,61 +247,38 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, error) {
|
func parseForwarder(cfg *config.ForwarderConfig) (hop.Hop, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hopName := cfg.Hop
|
|
||||||
if hopName == "" {
|
|
||||||
hopName = cfg.Name
|
|
||||||
}
|
|
||||||
if hopName != "" {
|
|
||||||
return registry.HopRegistry().Get(hopName), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hc := config.HopConfig{
|
hc := config.HopConfig{
|
||||||
Name: cfg.Name,
|
Name: cfg.Name,
|
||||||
Selector: cfg.Selector,
|
Selector: cfg.Selector,
|
||||||
}
|
}
|
||||||
for _, node := range cfg.Nodes {
|
for _, node := range cfg.Nodes {
|
||||||
if node == nil {
|
if node != nil {
|
||||||
continue
|
hc.Nodes = append(hc.Nodes,
|
||||||
}
|
&config.NodeConfig{
|
||||||
|
Name: node.Name,
|
||||||
filter := node.Filter
|
Addr: node.Addr,
|
||||||
if filter == nil {
|
|
||||||
if node.Protocol != "" || node.Host != "" || node.Path != "" {
|
|
||||||
filter = &config.NodeFilterConfig{
|
|
||||||
Protocol: node.Protocol,
|
|
||||||
Host: node.Host,
|
Host: node.Host,
|
||||||
|
Network: node.Network,
|
||||||
|
Protocol: node.Protocol,
|
||||||
Path: node.Path,
|
Path: node.Path,
|
||||||
}
|
Bypass: node.Bypass,
|
||||||
}
|
Bypasses: node.Bypasses,
|
||||||
|
HTTP: node.HTTP,
|
||||||
|
TLS: node.TLS,
|
||||||
|
Auth: node.Auth,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
httpCfg := node.HTTP
|
|
||||||
if node.Auth != nil {
|
|
||||||
if httpCfg == nil {
|
|
||||||
httpCfg = &config.HTTPNodeConfig{}
|
|
||||||
}
|
|
||||||
if httpCfg.Auth == nil {
|
|
||||||
httpCfg.Auth = node.Auth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hc.Nodes = append(hc.Nodes, &config.NodeConfig{
|
|
||||||
Name: node.Name,
|
|
||||||
Addr: node.Addr,
|
|
||||||
Network: node.Network,
|
|
||||||
Bypass: node.Bypass,
|
|
||||||
Bypasses: node.Bypasses,
|
|
||||||
Filter: filter,
|
|
||||||
HTTP: httpCfg,
|
|
||||||
TLS: node.TLS,
|
|
||||||
Metadata: node.Metadata,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return hop_parser.ParseHop(&hc, log)
|
if len(hc.Nodes) > 0 {
|
||||||
|
return hop_parser.ParseHop(&hc)
|
||||||
|
}
|
||||||
|
return registry.HopRegistry().Get(hc.Name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {
|
func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {
|
||||||
|
@ -39,22 +39,14 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
|
|||||||
opt(&cOpts)
|
opt(&cOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := cOpts.Dialer.Dial(ctx, network, address)
|
conn, err := cOpts.NetDialer.Dial(ctx, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var localAddr, remoteAddr string
|
|
||||||
if addr := conn.LocalAddr(); addr != nil {
|
|
||||||
localAddr = addr.String()
|
|
||||||
}
|
|
||||||
if addr := conn.RemoteAddr(); addr != nil {
|
|
||||||
remoteAddr = addr.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
log := c.options.Logger.WithFields(map[string]any{
|
log := c.options.Logger.WithFields(map[string]any{
|
||||||
"remote": remoteAddr,
|
"remote": conn.RemoteAddr().String(),
|
||||||
"local": localAddr,
|
"local": conn.LocalAddr().String(),
|
||||||
"network": network,
|
"network": network,
|
||||||
"address": address,
|
"address": address,
|
||||||
})
|
})
|
||||||
|
@ -6,16 +6,20 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/common/net/udp"
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
"github.com/go-gost/x/internal/net/udp"
|
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
relay_util "github.com/go-gost/x/internal/util/relay"
|
relay_util "github.com/go-gost/x/internal/util/relay"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bind implements connector.Binder.
|
// Bind implements connector.Binder.
|
||||||
func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
||||||
|
if !c.md.tunnelID.IsZero() {
|
||||||
|
return c.bindTunnel(ctx, conn, network, address, c.options.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
log := c.options.Logger.WithFields(map[string]any{
|
log := c.options.Logger.WithFields(map[string]any{
|
||||||
"network": network,
|
"network": network,
|
||||||
"address": address,
|
"address": address,
|
||||||
@ -39,6 +43,86 @@ func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, addre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *relayConnector) bindTunnel(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
|
||||||
|
addr, cid, err := c.initTunnel(conn, network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Infof("create tunnel on %s/%s OK, tunnel=%s, connector=%s", addr, network, c.md.tunnelID.String(), cid)
|
||||||
|
|
||||||
|
session, err := mux.ServerSession(conn, c.md.muxCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bindListener{
|
||||||
|
network: network,
|
||||||
|
addr: addr,
|
||||||
|
session: session,
|
||||||
|
logger: log,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *relayConnector) initTunnel(conn net.Conn, network, address string) (addr net.Addr, cid relay.ConnectorID, err error) {
|
||||||
|
req := relay.Request{
|
||||||
|
Version: relay.Version1,
|
||||||
|
Cmd: relay.CmdBind,
|
||||||
|
}
|
||||||
|
|
||||||
|
if network == "udp" {
|
||||||
|
req.Cmd |= relay.FUDP
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.options.Auth != nil {
|
||||||
|
pwd, _ := c.options.Auth.Password()
|
||||||
|
req.Features = append(req.Features, &relay.UserAuthFeature{
|
||||||
|
Username: c.options.Auth.Username(),
|
||||||
|
Password: pwd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
af := &relay.AddrFeature{}
|
||||||
|
af.ParseFrom(address)
|
||||||
|
|
||||||
|
req.Features = append(req.Features, af,
|
||||||
|
&relay.TunnelFeature{
|
||||||
|
ID: c.md.tunnelID.ID(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if _, err = req.WriteTo(conn); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// first reply, bind status
|
||||||
|
resp := relay.Response{}
|
||||||
|
if _, err = resp.ReadFrom(conn); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Status != relay.StatusOK {
|
||||||
|
err = fmt.Errorf("%d: create tunnel %s failed", resp.Status, c.md.tunnelID.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range resp.Features {
|
||||||
|
switch f.Type() {
|
||||||
|
case relay.FeatureAddr:
|
||||||
|
if feature, _ := f.(*relay.AddrFeature); feature != nil {
|
||||||
|
addr = &bindAddr{
|
||||||
|
network: network,
|
||||||
|
addr: net.JoinHostPort(feature.Host, strconv.Itoa(int(feature.Port))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case relay.FeatureTunnel:
|
||||||
|
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
|
||||||
|
cid = relay.NewConnectorID(feature.ID[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c *relayConnector) bindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
|
func (c *relayConnector) bindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
|
||||||
laddr, err := c.bind(conn, relay.CmdBind, network, address)
|
laddr, err := c.bind(conn, relay.CmdBind, network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,7 +157,7 @@ func (c *relayConnector) bindUDP(ctx context.Context, conn net.Conn, network, ad
|
|||||||
ReadQueueSize: opts.UDPDataQueueSize,
|
ReadQueueSize: opts.UDPDataQueueSize,
|
||||||
ReadBufferSize: opts.UDPDataBufferSize,
|
ReadBufferSize: opts.UDPDataBufferSize,
|
||||||
TTL: opts.UDPConnTTL,
|
TTL: opts.UDPConnTTL,
|
||||||
Keepalive: true,
|
KeepAlive: true,
|
||||||
Logger: log,
|
Logger: log,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -93,14 +177,6 @@ func (c *relayConnector) bind(conn net.Conn, cmd relay.CmdType, network, address
|
|||||||
Password: pwd,
|
Password: pwd,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
nid := relay.NetworkTCP
|
|
||||||
if network == "udp" || network == "udp4" || network == "udp6" {
|
|
||||||
nid = relay.NetworkUDP
|
|
||||||
}
|
|
||||||
req.Features = append(req.Features, &relay.NetworkFeature{
|
|
||||||
Network: nid,
|
|
||||||
})
|
|
||||||
fa := &relay.AddrFeature{}
|
fa := &relay.AddrFeature{}
|
||||||
fa.ParseFrom(address)
|
fa.ParseFrom(address)
|
||||||
req.Features = append(req.Features, fa)
|
req.Features = append(req.Features, fa)
|
||||||
|
@ -13,14 +13,12 @@ import (
|
|||||||
"github.com/go-gost/core/common/bufpool"
|
"github.com/go-gost/core/common/bufpool"
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
xrelay "github.com/go-gost/x/internal/util/relay"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type tcpConn struct {
|
type tcpConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
wbuf *bytes.Buffer
|
wbuf *bytes.Buffer
|
||||||
once sync.Once
|
once sync.Once
|
||||||
mu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tcpConn) Read(b []byte) (n int, err error) {
|
func (c *tcpConn) Read(b []byte) (n int, err error) {
|
||||||
@ -38,10 +36,6 @@ func (c *tcpConn) Read(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
func (c *tcpConn) Write(b []byte) (n int, err error) {
|
func (c *tcpConn) Write(b []byte) (n int, err error) {
|
||||||
n = len(b) // force byte length consistent
|
n = len(b) // force byte length consistent
|
||||||
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
||||||
c.wbuf.Write(b) // append the data to the cached header
|
c.wbuf.Write(b) // append the data to the cached header
|
||||||
_, err = c.Conn.Write(c.wbuf.Bytes())
|
_, err = c.Conn.Write(c.wbuf.Bytes())
|
||||||
@ -56,7 +50,6 @@ type udpConn struct {
|
|||||||
net.Conn
|
net.Conn
|
||||||
wbuf *bytes.Buffer
|
wbuf *bytes.Buffer
|
||||||
once sync.Once
|
once sync.Once
|
||||||
mu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) Read(b []byte) (n int, err error) {
|
func (c *udpConn) Read(b []byte) (n int, err error) {
|
||||||
@ -95,10 +88,6 @@ func (c *udpConn) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n = len(b)
|
n = len(b)
|
||||||
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
||||||
var bb [2]byte
|
var bb [2]byte
|
||||||
binary.BigEndian.PutUint16(bb[:], uint16(len(b)))
|
binary.BigEndian.PutUint16(bb[:], uint16(len(b)))
|
||||||
@ -130,7 +119,7 @@ func readResponse(r io.Reader) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.Status != relay.StatusOK {
|
if resp.Status != relay.StatusOK {
|
||||||
err = fmt.Errorf("%d %s", resp.Status, xrelay.StatusText(resp.Status))
|
err = fmt.Errorf("status %d", resp.Status)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -224,3 +213,16 @@ func (c *bindUDPConn) RemoteAddr() net.Addr {
|
|||||||
func (c *bindUDPConn) Metadata() mdata.Metadata {
|
func (c *bindUDPConn) Metadata() mdata.Metadata {
|
||||||
return c.md
|
return c.md
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type bindAddr struct {
|
||||||
|
network string
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *bindAddr) Network() string {
|
||||||
|
return p.network
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *bindAddr) String() string {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
@ -101,6 +101,12 @@ func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, ad
|
|||||||
req.Features = append(req.Features, af)
|
req.Features = append(req.Features, af)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !c.md.tunnelID.IsZero() {
|
||||||
|
req.Features = append(req.Features, &relay.TunnelFeature{
|
||||||
|
ID: c.md.tunnelID.ID(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if c.md.noDelay {
|
if c.md.noDelay {
|
||||||
if _, err := req.WriteTo(conn); err != nil {
|
if _, err := req.WriteTo(conn); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -5,12 +5,15 @@ import (
|
|||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
|
"github.com/go-gost/relay"
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
connectTimeout time.Duration
|
connectTimeout time.Duration
|
||||||
noDelay bool
|
noDelay bool
|
||||||
|
tunnelID relay.TunnelID
|
||||||
muxCfg *mux.Config
|
muxCfg *mux.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +26,14 @@ func (c *relayConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout)
|
c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout)
|
||||||
c.md.noDelay = mdutil.GetBool(md, noDelay)
|
c.md.noDelay = mdutil.GetBool(md, noDelay)
|
||||||
|
|
||||||
|
if s := mdutil.GetString(md, "tunnelID", "tunnel.id"); s != "" {
|
||||||
|
uuid, err := uuid.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.md.tunnelID = relay.NewTunnelID(uuid[:])
|
||||||
|
}
|
||||||
|
|
||||||
c.md.muxCfg = &mux.Config{
|
c.md.muxCfg = &mux.Config{
|
||||||
Version: mdutil.GetInt(md, "mux.version"),
|
Version: mdutil.GetInt(md, "mux.version"),
|
||||||
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),
|
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/common/net/udp"
|
||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
"github.com/go-gost/gosocks5"
|
"github.com/go-gost/gosocks5"
|
||||||
"github.com/go-gost/x/internal/net/udp"
|
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
"github.com/go-gost/x/internal/util/socks"
|
"github.com/go-gost/x/internal/util/socks"
|
||||||
)
|
)
|
||||||
@ -87,7 +87,7 @@ func (c *socks5Connector) bindUDP(ctx context.Context, conn net.Conn, network, a
|
|||||||
ReadQueueSize: opts.UDPDataQueueSize,
|
ReadQueueSize: opts.UDPDataQueueSize,
|
||||||
ReadBufferSize: opts.UDPDataBufferSize,
|
ReadBufferSize: opts.UDPDataBufferSize,
|
||||||
TTL: opts.UDPConnTTL,
|
TTL: opts.UDPConnTTL,
|
||||||
Keepalive: true,
|
KeepAlive: true,
|
||||||
Logger: log,
|
Logger: log,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -69,10 +69,6 @@ func (c *udpRelayConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
|||||||
if err = socksAddr.ParseFrom(addr.String()); err != nil {
|
if err = socksAddr.ParseFrom(addr.String()); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if socksAddr.Host == "" {
|
|
||||||
socksAddr.Type = gosocks5.AddrIPv4
|
|
||||||
socksAddr.Host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
header := gosocks5.UDPHeader{
|
header := gosocks5.UDPHeader{
|
||||||
Addr: &socksAddr,
|
Addr: &socksAddr,
|
||||||
|
@ -130,10 +130,6 @@ func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if addr.Host == "" {
|
|
||||||
addr.Type = gosocks5.AddrIPv4
|
|
||||||
addr.Host = "127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
req := gosocks5.NewRequest(gosocks5.CmdConnect, &addr)
|
req := gosocks5.NewRequest(gosocks5.CmdConnect, &addr)
|
||||||
log.Trace(req)
|
log.Trace(req)
|
||||||
@ -205,22 +201,16 @@ func (c *socks5Connector) relayUDP(ctx context.Context, conn net.Conn, addr net.
|
|||||||
}
|
}
|
||||||
log.Trace(reply)
|
log.Trace(reply)
|
||||||
|
|
||||||
|
log.Debugf("bind on: %v", reply.Addr)
|
||||||
|
|
||||||
if reply.Rep != gosocks5.Succeeded {
|
if reply.Rep != gosocks5.Succeeded {
|
||||||
return nil, errors.New("get socks5 UDP tunnel failure")
|
return nil, errors.New("get socks5 UDP tunnel failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("bind on: %v", reply.Addr)
|
cc, err := opts.NetDialer.Dial(ctx, "udp", reply.Addr.String())
|
||||||
|
|
||||||
cc, err := opts.Dialer.Dial(ctx, "udp", reply.Addr.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.options.Logger.Error(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("%s <- %s -> %s", cc.LocalAddr(), cc.RemoteAddr(), addr)
|
|
||||||
|
|
||||||
if c.md.udpTimeout > 0 {
|
|
||||||
cc.SetReadDeadline(time.Now().Add(c.md.udpTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &udpRelayConn{
|
return &udpRelayConn{
|
||||||
udpConn: cc.(*net.UDPConn),
|
udpConn: cc.(*net.UDPConn),
|
||||||
|
@ -17,19 +17,24 @@ type metadata struct {
|
|||||||
noTLS bool
|
noTLS bool
|
||||||
relay string
|
relay string
|
||||||
udpBufferSize int
|
udpBufferSize int
|
||||||
udpTimeout time.Duration
|
|
||||||
muxCfg *mux.Config
|
muxCfg *mux.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *socks5Connector) parseMetadata(md mdata.Metadata) (err error) {
|
func (c *socks5Connector) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
c.md.connectTimeout = mdutil.GetDuration(md, "timeout")
|
const (
|
||||||
c.md.noTLS = mdutil.GetBool(md, "notls")
|
connectTimeout = "timeout"
|
||||||
c.md.relay = mdutil.GetString(md, "relay")
|
noTLS = "notls"
|
||||||
c.md.udpBufferSize = mdutil.GetInt(md, "udp.bufferSize", "udpBufferSize")
|
relay = "relay"
|
||||||
|
udpBufferSize = "udpBufferSize"
|
||||||
|
)
|
||||||
|
|
||||||
|
c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout)
|
||||||
|
c.md.noTLS = mdutil.GetBool(md, noTLS)
|
||||||
|
c.md.relay = mdutil.GetString(md, relay)
|
||||||
|
c.md.udpBufferSize = mdutil.GetInt(md, udpBufferSize)
|
||||||
if c.md.udpBufferSize <= 0 {
|
if c.md.udpBufferSize <= 0 {
|
||||||
c.md.udpBufferSize = defaultUDPBufferSize
|
c.md.udpBufferSize = defaultUDPBufferSize
|
||||||
}
|
}
|
||||||
c.md.udpTimeout = mdutil.GetDuration(md, "udp.timeout")
|
|
||||||
|
|
||||||
c.md.muxCfg = &mux.Config{
|
c.md.muxCfg = &mux.Config{
|
||||||
Version: mdutil.GetInt(md, "mux.version"),
|
Version: mdutil.GetInt(md, "mux.version"),
|
||||||
|
@ -27,8 +27,7 @@ func (c *tunnelConnector) Bind(ctx context.Context, conn net.Conn, network, addr
|
|||||||
"endpoint": endpoint,
|
"endpoint": endpoint,
|
||||||
"tunnel": c.md.tunnelID.String(),
|
"tunnel": c.md.tunnelID.String(),
|
||||||
})
|
})
|
||||||
log.Infof("create tunnel on %s/%s OK, tunnel=%s, connector=%s, weight=%d",
|
log.Infof("create tunnel on %s/%s OK, tunnel=%s, connector=%s", addr, network, c.md.tunnelID.String(), cid)
|
||||||
addr, network, c.md.tunnelID.String(), cid, cid.Weight())
|
|
||||||
|
|
||||||
session, err := mux.ServerSession(conn, c.md.muxCfg)
|
session, err := mux.ServerSession(conn, c.md.muxCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,7 +72,7 @@ func (c *tunnelConnector) initTunnel(conn net.Conn, network, address string) (ad
|
|||||||
req.Features = append(req.Features, af) // dst address
|
req.Features = append(req.Features, af) // dst address
|
||||||
|
|
||||||
req.Features = append(req.Features, &relay.TunnelFeature{
|
req.Features = append(req.Features, &relay.TunnelFeature{
|
||||||
ID: c.md.tunnelID,
|
ID: c.md.tunnelID.ID(),
|
||||||
})
|
})
|
||||||
if _, err = req.WriteTo(conn); err != nil {
|
if _, err = req.WriteTo(conn); err != nil {
|
||||||
return
|
return
|
||||||
@ -101,7 +100,7 @@ func (c *tunnelConnector) initTunnel(conn net.Conn, network, address string) (ad
|
|||||||
}
|
}
|
||||||
case relay.FeatureTunnel:
|
case relay.FeatureTunnel:
|
||||||
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
|
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
|
||||||
cid = feature.ID
|
cid = relay.NewConnectorID(feature.ID[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,67 @@
|
|||||||
package tunnel
|
package tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/go-gost/core/common/bufpool"
|
"github.com/go-gost/core/common/bufpool"
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
xrelay "github.com/go-gost/x/internal/util/relay"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type tcpConn struct {
|
||||||
|
net.Conn
|
||||||
|
wbuf *bytes.Buffer
|
||||||
|
once sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tcpConn) Read(b []byte) (n int, err error) {
|
||||||
|
c.once.Do(func() {
|
||||||
|
if c.wbuf != nil {
|
||||||
|
err = readResponse(c.Conn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *tcpConn) Write(b []byte) (n int, err error) {
|
||||||
|
n = len(b) // force byte length consistent
|
||||||
|
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
||||||
|
c.wbuf.Write(b) // append the data to the cached header
|
||||||
|
_, err = c.Conn.Write(c.wbuf.Bytes())
|
||||||
|
c.wbuf.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.Conn.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type udpConn struct {
|
type udpConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
wbuf *bytes.Buffer
|
||||||
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) Read(b []byte) (n int, err error) {
|
func (c *udpConn) Read(b []byte) (n int, err error) {
|
||||||
|
c.once.Do(func() {
|
||||||
|
if c.wbuf != nil {
|
||||||
|
err = readResponse(c.Conn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var bb [2]byte
|
var bb [2]byte
|
||||||
_, err = io.ReadFull(c.Conn, bb[:])
|
_, err = io.ReadFull(c.Conn, bb[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,6 +88,14 @@ func (c *udpConn) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n = len(b)
|
n = len(b)
|
||||||
|
if c.wbuf != nil && c.wbuf.Len() > 0 {
|
||||||
|
var bb [2]byte
|
||||||
|
binary.BigEndian.PutUint16(bb[:], uint16(len(b)))
|
||||||
|
c.wbuf.Write(bb[:])
|
||||||
|
c.wbuf.Write(b) // append the data to the cached header
|
||||||
|
_, err = c.wbuf.WriteTo(c.Conn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var bb [2]byte
|
var bb [2]byte
|
||||||
binary.BigEndian.PutUint16(bb[:], uint16(len(b)))
|
binary.BigEndian.PutUint16(bb[:], uint16(len(b)))
|
||||||
@ -68,7 +119,7 @@ func readResponse(r io.Reader) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if resp.Status != relay.StatusOK {
|
if resp.Status != relay.StatusOK {
|
||||||
err = fmt.Errorf("%d %s", resp.Status, xrelay.StatusText(resp.Status))
|
err = fmt.Errorf("status %d", resp.Status)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tunnel
|
package tunnel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -9,7 +10,7 @@ import (
|
|||||||
"github.com/go-gost/core/connector"
|
"github.com/go-gost/core/connector"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
auth_util "github.com/go-gost/x/internal/util/auth"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
srcAddr := conn.LocalAddr().String()
|
srcAddr := conn.LocalAddr().String()
|
||||||
if v := ctxvalue.ClientAddrFromContext(ctx); v != "" {
|
if v := auth_util.ClientAddrFromContext(ctx); v != "" {
|
||||||
srcAddr = string(v)
|
srcAddr = string(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,23 +87,42 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a
|
|||||||
req.Features = append(req.Features, af) // dst address
|
req.Features = append(req.Features, af) // dst address
|
||||||
|
|
||||||
req.Features = append(req.Features, &relay.TunnelFeature{
|
req.Features = append(req.Features, &relay.TunnelFeature{
|
||||||
ID: c.md.tunnelID,
|
ID: c.md.tunnelID.ID(),
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, err := req.WriteTo(conn); err != nil {
|
if c.md.noDelay {
|
||||||
return nil, err
|
if _, err := req.WriteTo(conn); err != nil {
|
||||||
}
|
return nil, err
|
||||||
// drain the response
|
}
|
||||||
if err := readResponse(conn); err != nil {
|
// drain the response
|
||||||
return nil, err
|
if err := readResponse(conn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch network {
|
switch network {
|
||||||
case "tcp", "tcp4", "tcp6":
|
case "tcp", "tcp4", "tcp6":
|
||||||
|
if !c.md.noDelay {
|
||||||
|
cc := &tcpConn{
|
||||||
|
Conn: conn,
|
||||||
|
wbuf: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
if _, err := req.WriteTo(cc.wbuf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = cc
|
||||||
|
}
|
||||||
case "udp", "udp4", "udp6":
|
case "udp", "udp4", "udp6":
|
||||||
conn = &udpConn{
|
cc := &udpConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
}
|
}
|
||||||
|
if !c.md.noDelay {
|
||||||
|
cc.wbuf = &bytes.Buffer{}
|
||||||
|
if _, err := req.WriteTo(cc.wbuf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn = cc
|
||||||
default:
|
default:
|
||||||
err := fmt.Errorf("network %s is unsupported", network)
|
err := fmt.Errorf("network %s is unsupported", network)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
@ -18,11 +18,13 @@ var (
|
|||||||
type metadata struct {
|
type metadata struct {
|
||||||
connectTimeout time.Duration
|
connectTimeout time.Duration
|
||||||
tunnelID relay.TunnelID
|
tunnelID relay.TunnelID
|
||||||
|
noDelay bool
|
||||||
muxCfg *mux.Config
|
muxCfg *mux.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tunnelConnector) parseMetadata(md mdata.Metadata) (err error) {
|
func (c *tunnelConnector) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
c.md.connectTimeout = mdutil.GetDuration(md, "connectTimeout")
|
c.md.connectTimeout = mdutil.GetDuration(md, "connectTimeout")
|
||||||
|
c.md.noDelay = mdutil.GetBool(md, "nodelay")
|
||||||
|
|
||||||
if s := mdutil.GetString(md, "tunnelID", "tunnel.id"); s != "" {
|
if s := mdutil.GetString(md, "tunnelID", "tunnel.id"); s != "" {
|
||||||
uuid, err := uuid.Parse(s)
|
uuid, err := uuid.Parse(s)
|
||||||
@ -40,10 +42,6 @@ func (c *tunnelConnector) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
c.md.tunnelID = relay.NewTunnelID(uuid[:])
|
c.md.tunnelID = relay.NewTunnelID(uuid[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
if weight := mdutil.GetInt(md, "tunnel.weight"); weight > 0 {
|
|
||||||
c.md.tunnelID = c.md.tunnelID.SetWeight(uint8(weight))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.md.muxCfg = &mux.Config{
|
c.md.muxCfg = &mux.Config{
|
||||||
Version: mdutil.GetInt(md, "mux.version"),
|
Version: mdutil.GetInt(md, "mux.version"),
|
||||||
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),
|
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),
|
||||||
|
76
ctx/value.go
76
ctx/value.go
@ -1,76 +0,0 @@
|
|||||||
package ctx
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
// clientAddrKey saves the client address.
|
|
||||||
type clientAddrKey struct{}
|
|
||||||
|
|
||||||
type ClientAddr string
|
|
||||||
|
|
||||||
var (
|
|
||||||
keyClientAddr clientAddrKey
|
|
||||||
)
|
|
||||||
|
|
||||||
func ContextWithClientAddr(ctx context.Context, addr ClientAddr) context.Context {
|
|
||||||
return context.WithValue(ctx, keyClientAddr, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClientAddrFromContext(ctx context.Context) ClientAddr {
|
|
||||||
v, _ := ctx.Value(keyClientAddr).(ClientAddr)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// sidKey saves the session ID.
|
|
||||||
type sidKey struct{}
|
|
||||||
type Sid string
|
|
||||||
|
|
||||||
var (
|
|
||||||
keySid sidKey
|
|
||||||
)
|
|
||||||
|
|
||||||
func ContextWithSid(ctx context.Context, sid Sid) context.Context {
|
|
||||||
return context.WithValue(ctx, keySid, sid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SidFromContext(ctx context.Context) Sid {
|
|
||||||
v, _ := ctx.Value(keySid).(Sid)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashKey saves the hash source for Selector.
|
|
||||||
type hashKey struct{}
|
|
||||||
|
|
||||||
type Hash struct {
|
|
||||||
Source string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
clientHashKey = &hashKey{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ContextWithHash(ctx context.Context, hash *Hash) context.Context {
|
|
||||||
return context.WithValue(ctx, clientHashKey, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HashFromContext(ctx context.Context) *Hash {
|
|
||||||
if v, _ := ctx.Value(clientHashKey).(*Hash); v != nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientIDKey struct{}
|
|
||||||
type ClientID string
|
|
||||||
|
|
||||||
var (
|
|
||||||
keyClientID = &clientIDKey{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ContextWithClientID(ctx context.Context, clientID ClientID) context.Context {
|
|
||||||
return context.WithValue(ctx, keyClientID, clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClientIDFromContext(ctx context.Context) ClientID {
|
|
||||||
v, _ := ctx.Value(keyClientID).(ClientID)
|
|
||||||
return v
|
|
||||||
}
|
|
@ -45,7 +45,7 @@ func (d *dtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "udp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "udp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,14 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
grpcOpts := []grpc.DialOption{
|
grpcOpts := []grpc.DialOption{
|
||||||
// grpc.WithBlock(),
|
// grpc.WithBlock(),
|
||||||
grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {
|
grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {
|
||||||
return options.Dialer.Dial(c, "tcp", s)
|
return options.NetDialer.Dial(c, "tcp", s)
|
||||||
}),
|
}),
|
||||||
grpc.WithAuthority(host),
|
grpc.WithAuthority(host),
|
||||||
grpc.WithConnectParams(grpc.ConnectParams{
|
grpc.WithConnectParams(grpc.ConnectParams{
|
||||||
Backoff: backoff.DefaultConfig,
|
Backoff: backoff.DefaultConfig,
|
||||||
MinConnectTimeout: d.md.minConnectTimeout,
|
MinConnectTimeout: d.md.minConnectTimeout,
|
||||||
}),
|
}),
|
||||||
|
grpc.FailOnNonTempDialError(true),
|
||||||
}
|
}
|
||||||
if !d.md.insecure {
|
if !d.md.insecure {
|
||||||
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(d.options.TLSConfig)))
|
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(d.options.TLSConfig)))
|
||||||
@ -93,7 +94,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, err := grpc.NewClient(addr, grpcOpts...)
|
cc, err := grpc.DialContext(ctx, addr, grpcOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.options.Logger.Error(err)
|
d.options.Logger.Error(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -7,10 +7,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
net_dialer "github.com/go-gost/core/common/net/dialer"
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
net_dialer "github.com/go-gost/x/internal/net/dialer"
|
|
||||||
mdx "github.com/go-gost/x/metadata"
|
mdx "github.com/go-gost/x/metadata"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
)
|
)
|
||||||
@ -70,40 +70,28 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
// Check whether the connection is established properly
|
|
||||||
netd := options.Dialer
|
|
||||||
if netd == nil {
|
|
||||||
netd = net_dialer.DefaultNetDialer
|
|
||||||
}
|
|
||||||
conn, err := netd.Dial(ctx, "tcp", address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
client = &http.Client{
|
client = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: d.options.TLSConfig,
|
TLSClientConfig: d.options.TLSConfig,
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
netd := options.Dialer
|
netd := options.NetDialer
|
||||||
if netd == nil {
|
if netd == nil {
|
||||||
netd = net_dialer.DefaultNetDialer
|
netd = net_dialer.DefaultNetDialer
|
||||||
}
|
}
|
||||||
return netd.Dial(ctx, network, addr)
|
return netd.Dial(ctx, network, addr)
|
||||||
},
|
},
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
MaxIdleConns: 16,
|
MaxIdleConns: 100,
|
||||||
IdleConnTimeout: 30 * time.Second,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
TLSHandshakeTimeout: 30 * time.Second,
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
ExpectContinueTimeout: 15 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
d.clients[address] = client
|
d.clients[address] = client
|
||||||
}
|
}
|
||||||
|
|
||||||
var c net.Conn = &conn{
|
var c net.Conn
|
||||||
|
c = &conn{
|
||||||
localAddr: &net.TCPAddr{},
|
localAddr: &net.TCPAddr{},
|
||||||
remoteAddr: raddr,
|
remoteAddr: raddr,
|
||||||
onClose: func() {
|
onClose: func() {
|
||||||
|
@ -94,14 +94,14 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
|
|||||||
client.Transport = &http2.Transport{
|
client.Transport = &http2.Transport{
|
||||||
AllowHTTP: true,
|
AllowHTTP: true,
|
||||||
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
return options.Dialer.Dial(ctx, network, addr)
|
return options.NetDialer.Dial(ctx, network, addr)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
client.Transport = &http.Transport{
|
client.Transport = &http.Transport{
|
||||||
TLSClientConfig: d.options.TLSConfig,
|
TLSClientConfig: d.options.TLSConfig,
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
return options.Dialer.Dial(ctx, network, addr)
|
return options.NetDialer.Dial(ctx, network, addr)
|
||||||
},
|
},
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
|
@ -79,14 +79,14 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
udpConn, err := options.Dialer.Dial(ctx, "udp", "")
|
udpConn, err := options.NetDialer.Dial(ctx, "udp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return quic.DialEarly(context.Background(), udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
|
return quic.DialEarly(context.Background(), udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
|
||||||
},
|
},
|
||||||
QUICConfig: &quic.Config{
|
QuicConfig: &quic.Config{
|
||||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||||
|
@ -50,7 +50,7 @@ func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
d.md.host = mdutil.GetString(md, "host")
|
d.md.host = mdutil.GetString(md, "host")
|
||||||
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
||||||
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
||||||
if d.md.keepAlivePeriod <= 0 {
|
if d.md.keepAlivePeriod <= 0 {
|
||||||
d.md.keepAlivePeriod = 10 * time.Second
|
d.md.keepAlivePeriod = 10 * time.Second
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
md "github.com/go-gost/core/metadata"
|
md "github.com/go-gost/core/metadata"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
|
"github.com/quic-go/quic-go/http3"
|
||||||
wt "github.com/quic-go/webtransport-go"
|
wt "github.com/quic-go/webtransport-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,32 +74,33 @@ func (d *wtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
|
|||||||
path: d.md.path,
|
path: d.md.path,
|
||||||
header: d.md.header,
|
header: d.md.header,
|
||||||
dialer: &wt.Dialer{
|
dialer: &wt.Dialer{
|
||||||
TLSClientConfig: d.options.TLSConfig,
|
RoundTripper: &http3.RoundTripper{
|
||||||
DialAddr: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
TLSClientConfig: d.options.TLSConfig,
|
||||||
// d.options.Logger.Infof("dial: %s, %s, %s", addr, adr, host)
|
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
// d.options.Logger.Infof("dial: %s, %s, %s", addr, adr, host)
|
||||||
if err != nil {
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
udpConn, err := options.Dialer.Dial(ctx, "udp", "")
|
udpConn, err := options.NetDialer.Dial(ctx, "udp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return quic.DialEarly(ctx, udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
|
return quic.DialEarly(ctx, udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
|
||||||
},
|
},
|
||||||
QUICConfig: &quic.Config{
|
QuicConfig: &quic.Config{
|
||||||
KeepAlivePeriod: d.md.keepAlivePeriod,
|
KeepAlivePeriod: d.md.keepAlivePeriod,
|
||||||
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
HandshakeIdleTimeout: d.md.handshakeTimeout,
|
||||||
MaxIdleTimeout: d.md.maxIdleTimeout,
|
MaxIdleTimeout: d.md.maxIdleTimeout,
|
||||||
/*
|
/*
|
||||||
Versions: []quic.VersionNumber{
|
Versions: []quic.VersionNumber{
|
||||||
quic.Version1,
|
quic.Version1,
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
MaxIncomingStreams: int64(d.md.maxStreams),
|
MaxIncomingStreams: int64(d.md.maxStreams),
|
||||||
EnableDatagrams: true,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func (d *wtDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
d.md.path = defaultPath
|
d.md.path = defaultPath
|
||||||
}
|
}
|
||||||
|
|
||||||
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
||||||
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
||||||
if d.md.keepAlivePeriod <= 0 {
|
if d.md.keepAlivePeriod <= 0 {
|
||||||
d.md.keepAlivePeriod = 10 * time.Second
|
d.md.keepAlivePeriod = 10 * time.Second
|
||||||
|
@ -19,11 +19,9 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.DialerRegistry().Register("icmp", NewDialer)
|
registry.DialerRegistry().Register("icmp", NewDialer)
|
||||||
registry.DialerRegistry().Register("icmp6", NewDialer6)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type icmpDialer struct {
|
type icmpDialer struct {
|
||||||
ip6 bool
|
|
||||||
sessions map[string]*quicSession
|
sessions map[string]*quicSession
|
||||||
sessionMutex sync.Mutex
|
sessionMutex sync.Mutex
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
@ -44,19 +42,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialer6(opts ...dialer.Option) dialer.Dialer {
|
|
||||||
options := dialer.Options{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &icmpDialer{
|
|
||||||
ip6: true,
|
|
||||||
sessions: make(map[string]*quicSession),
|
|
||||||
logger: options.Logger,
|
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (d *icmpDialer) Init(md md.Metadata) (err error) {
|
func (d *icmpDialer) Init(md md.Metadata) (err error) {
|
||||||
if err = d.parseMetadata(md); err != nil {
|
if err = d.parseMetadata(md); err != nil {
|
||||||
return
|
return
|
||||||
@ -86,11 +71,7 @@ func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pc net.PacketConn
|
var pc net.PacketConn
|
||||||
if d.ip6 {
|
pc, err = icmp.ListenPacket("ip4:icmp", "")
|
||||||
pc, err = icmp.ListenPacket("ip6:ipv6-icmp", "")
|
|
||||||
} else {
|
|
||||||
pc, err = icmp.ListenPacket("ip4:icmp", "")
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -100,7 +81,7 @@ func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
id = rand.New(rand.NewSource(time.Now().UnixNano())).Intn(math.MaxUint16) + 1
|
id = rand.New(rand.NewSource(time.Now().UnixNano())).Intn(math.MaxUint16) + 1
|
||||||
raddr.Port = id
|
raddr.Port = id
|
||||||
}
|
}
|
||||||
pc = icmp_pkg.ClientConn(d.ip6, pc, id)
|
pc = icmp_pkg.ClientConn(pc, id)
|
||||||
|
|
||||||
session, err = d.initSession(ctx, raddr, pc)
|
session, err = d.initSession(ctx, raddr, pc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -134,7 +115,7 @@ func (d *icmpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
tlsCfg := d.options.TLSConfig
|
tlsCfg := d.options.TLSConfig
|
||||||
tlsCfg.NextProtos = []string{"h3", "quic/v1"}
|
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
|
||||||
|
|
||||||
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
|
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14,14 +14,21 @@ type metadata struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
if mdutil.GetBool(md, "keepalive") {
|
const (
|
||||||
d.md.keepAlivePeriod = mdutil.GetDuration(md, "ttl")
|
keepAlive = "keepAlive"
|
||||||
|
keepAlivePeriod = "ttl"
|
||||||
|
handshakeTimeout = "handshakeTimeout"
|
||||||
|
maxIdleTimeout = "maxIdleTimeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
if mdutil.GetBool(md, keepAlive) {
|
||||||
|
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
||||||
if d.md.keepAlivePeriod <= 0 {
|
if d.md.keepAlivePeriod <= 0 {
|
||||||
d.md.keepAlivePeriod = 10 * time.Second
|
d.md.keepAlivePeriod = 10 * time.Second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.md.handshakeTimeout = mdutil.GetDuration(md, "handshakeTimeout")
|
d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
|
||||||
d.md.maxIdleTimeout = mdutil.GetDuration(md, "maxIdleTimeout")
|
d.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func (d *kcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
PacketConn: pc,
|
PacketConn: pc,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c, err := options.Dialer.Dial(ctx, "udp", "")
|
c, err := options.NetDialer.Dial(ctx, "udp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -149,9 +149,7 @@ func (d *kcpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pac
|
|||||||
smuxConfig.Version = config.SmuxVer
|
smuxConfig.Version = config.SmuxVer
|
||||||
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
|
||||||
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
smuxConfig.MaxStreamBuffer = config.StreamBuf
|
||||||
if config.KeepAlive > 0 {
|
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
|
||||||
}
|
|
||||||
var cc net.Conn = kcpconn
|
var cc net.Conn = kcpconn
|
||||||
if !config.NoComp {
|
if !config.NoComp {
|
||||||
cc = kcp_util.CompStreamConn(kcpconn)
|
cc = kcp_util.CompStreamConn(kcpconn)
|
||||||
|
@ -21,14 +21,14 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
handshakeTimeout = "handshakeTimeout"
|
handshakeTimeout = "handshakeTimeout"
|
||||||
)
|
)
|
||||||
|
|
||||||
if file := mdutil.GetString(md, "kcp.configFile", "configFile", "c"); file != "" {
|
if file := mdutil.GetString(md, configFile); file != "" {
|
||||||
d.md.config, err = kcp_util.ParseFromFile(file)
|
d.md.config, err = kcp_util.ParseFromFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m := mdutil.GetStringMap(md, "kcp.config", "config"); len(m) > 0 {
|
if m := mdutil.GetStringMap(md, config); len(m) > 0 {
|
||||||
b, err := json.Marshal(m)
|
b, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -42,19 +42,6 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
if d.md.config == nil {
|
if d.md.config == nil {
|
||||||
d.md.config = kcp_util.DefaultConfig
|
d.md.config = kcp_util.DefaultConfig
|
||||||
}
|
}
|
||||||
d.md.config.TCP = mdutil.GetBool(md, "kcp.tcp", "tcp")
|
|
||||||
d.md.config.Key = mdutil.GetString(md, "kcp.key")
|
|
||||||
d.md.config.Crypt = mdutil.GetString(md, "kcp.crypt")
|
|
||||||
d.md.config.Mode = mdutil.GetString(md, "kcp.mode")
|
|
||||||
d.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive")
|
|
||||||
d.md.config.Interval = mdutil.GetInt(md, "kcp.interval")
|
|
||||||
d.md.config.MTU = mdutil.GetInt(md, "kcp.mtu")
|
|
||||||
d.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd")
|
|
||||||
d.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd")
|
|
||||||
d.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver")
|
|
||||||
d.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf")
|
|
||||||
d.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf")
|
|
||||||
d.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp")
|
|
||||||
|
|
||||||
d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
|
d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
|
||||||
return
|
return
|
||||||
|
@ -67,7 +67,7 @@ func (d *mtcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package mtls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
tls "github.com/refraction-networking/utls"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -68,7 +68,7 @@ func (d *mtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -123,20 +123,7 @@ func (d *mtlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...di
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *mtlsDialer) initSession(ctx context.Context, conn net.Conn) (*muxSession, error) {
|
func (d *mtlsDialer) initSession(ctx context.Context, conn net.Conn) (*muxSession, error) {
|
||||||
tlsConfig := d.options.TLSConfig
|
tlsConn := tls.Client(conn, d.options.TLSConfig)
|
||||||
var utlsConf = &tls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: tls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
|
|
||||||
if len(tlsConfig.Certificates) > 0 {
|
|
||||||
for _, certificate := range tlsConfig.Certificates {
|
|
||||||
utlsConf.Certificates = append(utlsConf.Certificates, tls.Certificate{
|
|
||||||
Certificate: certificate.Certificate,
|
|
||||||
PrivateKey: certificate.PrivateKey,
|
|
||||||
OCSPStaple: certificate.OCSPStaple,
|
|
||||||
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
|
|
||||||
Leaf: certificate.Leaf,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tlsConn := tls.UClient(conn, utlsConf, tls.HelloChrome_Auto)
|
|
||||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ package mws
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/go-gost/x/util"
|
|
||||||
tls "github.com/refraction-networking/utls"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
@ -84,7 +82,7 @@ func (d *mwsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -160,33 +158,6 @@ func (d *mwsDialer) initSession(ctx context.Context, host string, conn net.Conn,
|
|||||||
if d.tlsEnabled {
|
if d.tlsEnabled {
|
||||||
url.Scheme = "wss"
|
url.Scheme = "wss"
|
||||||
dialer.TLSClientConfig = d.options.TLSConfig
|
dialer.TLSClientConfig = d.options.TLSConfig
|
||||||
tlsConfig := d.options.TLSConfig
|
|
||||||
dialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
utlsConf := &tls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: tls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
|
|
||||||
if len(tlsConfig.Certificates) > 0 {
|
|
||||||
for _, certificate := range tlsConfig.Certificates {
|
|
||||||
utlsConf.Certificates = append(utlsConf.Certificates, tls.Certificate{
|
|
||||||
Certificate: certificate.Certificate,
|
|
||||||
PrivateKey: certificate.PrivateKey,
|
|
||||||
OCSPStaple: certificate.OCSPStaple,
|
|
||||||
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
|
|
||||||
Leaf: certificate.Leaf,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var client *tls.UConn
|
|
||||||
if d.md.useH2 {
|
|
||||||
client = tls.UClient(conn, utlsConf, tls.HelloChrome_Auto)
|
|
||||||
} else {
|
|
||||||
client = tls.UClient(conn, utlsConf, tls.HelloCustom)
|
|
||||||
client.ApplyPreset(util.NewWsSpec())
|
|
||||||
}
|
|
||||||
err := client.Handshake()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.md.handshakeTimeout > 0 {
|
if d.md.handshakeTimeout > 0 {
|
||||||
|
@ -27,9 +27,6 @@ type metadata struct {
|
|||||||
header http.Header
|
header http.Header
|
||||||
keepaliveInterval time.Duration
|
keepaliveInterval time.Duration
|
||||||
muxCfg *mux.Config
|
muxCfg *mux.Config
|
||||||
|
|
||||||
//Evan Enhanced
|
|
||||||
useH2 bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
@ -70,6 +67,5 @@ func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d.md.useH2 = mdutil.GetBool(md, "h2")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
type obfsHTTPConn struct {
|
type obfsHTTPConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
host string
|
host string
|
||||||
path string
|
|
||||||
rbuf bytes.Buffer
|
rbuf bytes.Buffer
|
||||||
wbuf bytes.Buffer
|
wbuf bytes.Buffer
|
||||||
headerDrained bool
|
headerDrained bool
|
||||||
|
@ -2,7 +2,6 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
"github.com/go-gost/core/dialer"
|
||||||
@ -13,13 +12,11 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.DialerRegistry().Register("ohttp", NewDialer)
|
registry.DialerRegistry().Register("ohttp", NewDialer)
|
||||||
registry.DialerRegistry().Register("ohttps", NewDialer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type obfsHTTPDialer struct {
|
type obfsHTTPDialer struct {
|
||||||
tlsEnabled bool
|
md metadata
|
||||||
md metadata
|
logger logger.Logger
|
||||||
logger logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||||
@ -33,18 +30,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTLSDialer(opts ...dialer.Option) dialer.Dialer {
|
|
||||||
options := &dialer.Options{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &obfsHTTPDialer{
|
|
||||||
tlsEnabled: true,
|
|
||||||
logger: options.Logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *obfsHTTPDialer) Init(md md.Metadata) (err error) {
|
func (d *obfsHTTPDialer) Init(md md.Metadata) (err error) {
|
||||||
return d.parseMetadata(md)
|
return d.parseMetadata(md)
|
||||||
}
|
}
|
||||||
@ -55,7 +40,7 @@ func (d *obfsHTTPDialer) Dial(ctx context.Context, addr string, opts ...dialer.D
|
|||||||
opt(options)
|
opt(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err)
|
d.logger.Error(err)
|
||||||
}
|
}
|
||||||
@ -74,16 +59,9 @@ func (d *obfsHTTPDialer) Handshake(ctx context.Context, conn net.Conn, options .
|
|||||||
host = opts.Addr
|
host = opts.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.tlsEnabled {
|
|
||||||
conn = tls.Client(conn, &tls.Config{
|
|
||||||
ServerName: host,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return &obfsHTTPConn{
|
return &obfsHTTPConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
host: host,
|
host: host,
|
||||||
path: d.md.path,
|
|
||||||
header: d.md.header,
|
header: d.md.header,
|
||||||
logger: d.logger,
|
logger: d.logger,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -7,29 +7,24 @@ import (
|
|||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPath = "/"
|
|
||||||
)
|
|
||||||
|
|
||||||
type metadata struct {
|
type metadata struct {
|
||||||
host string
|
host string
|
||||||
path string
|
|
||||||
header http.Header
|
header http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *obfsHTTPDialer) parseMetadata(md mdata.Metadata) (err error) {
|
func (d *obfsHTTPDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
d.md.host = mdutil.GetString(md, "obfs.host", "host")
|
const (
|
||||||
d.md.path = mdutil.GetString(md, "obfs.path", "path")
|
header = "header"
|
||||||
if d.md.path == "" {
|
host = "host"
|
||||||
d.md.path = defaultPath
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if m := mdutil.GetStringMapString(md, "obfs.header", "header"); len(m) > 0 {
|
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
|
||||||
h := http.Header{}
|
h := http.Header{}
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
h.Add(k, v)
|
h.Add(k, v)
|
||||||
}
|
}
|
||||||
d.md.header = h
|
d.md.header = h
|
||||||
}
|
}
|
||||||
|
d.md.host = mdutil.GetString(md, host)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func (d *obfsTLSDialer) Dial(ctx context.Context, addr string, opts ...dialer.Di
|
|||||||
opt(options)
|
opt(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err)
|
d.logger.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ func (d *phtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
// Proxy: http.ProxyFromEnvironment,
|
// Proxy: http.ProxyFromEnvironment,
|
||||||
DialContext: func(ctx context.Context, network, adr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, adr string) (net.Conn, error) {
|
||||||
return options.Dialer.Dial(ctx, network, addr)
|
return options.NetDialer.Dial(ctx, network, addr)
|
||||||
},
|
},
|
||||||
ForceAttemptHTTP2: true,
|
ForceAttemptHTTP2: true,
|
||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
|
@ -67,7 +67,7 @@ func (d *quicDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
opt(options)
|
opt(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := options.Dialer.Dial(ctx, "udp", "")
|
c, err := options.NetDialer.Dial(ctx, "udp", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func (d *quicDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
tlsCfg := d.options.TLSConfig
|
tlsCfg := d.options.TLSConfig
|
||||||
tlsCfg.NextProtos = []string{"h3", "quic/v1"}
|
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
|
||||||
|
|
||||||
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
|
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -31,7 +31,7 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
d.md.cipherKey = []byte(key)
|
d.md.cipherKey = []byte(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
|
||||||
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
|
||||||
if d.md.keepAlivePeriod <= 0 {
|
if d.md.keepAlivePeriod <= 0 {
|
||||||
d.md.keepAlivePeriod = 10 * time.Second
|
d.md.keepAlivePeriod = 10 * time.Second
|
||||||
|
@ -64,7 +64,7 @@ func (d *sshDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/zalando/go-keyring"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,35 +20,21 @@ type metadata struct {
|
|||||||
|
|
||||||
func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
|
func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
const (
|
const (
|
||||||
handshakeTimeout = "handshakeTimeout"
|
handshakeTimeout = "handshakeTimeout"
|
||||||
privateKeyFile = "privateKeyFile"
|
privateKeyFile = "privateKeyFile"
|
||||||
passphrase = "passphrase"
|
passphrase = "passphrase"
|
||||||
passphraseFromKeyring = "passphraseFromKeyring"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if key := mdutil.GetString(md, privateKeyFile); key != "" {
|
if key := mdutil.GetString(md, privateKeyFile); key != "" {
|
||||||
key, err = homedir.Expand(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(key)
|
data, err := os.ReadFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pp string
|
if pp := mdutil.GetString(md, passphrase); pp != "" {
|
||||||
if mdutil.GetBool(md, passphraseFromKeyring) {
|
|
||||||
pp, err = keyring.Get(fmt.Sprintf("SSH %s", key), d.options.Auth.Username())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get secret(%s) from keyring: %w", key, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pp = mdutil.GetString(md, passphrase)
|
|
||||||
}
|
|
||||||
if pp == "" {
|
|
||||||
d.md.signer, err = ssh.ParsePrivateKey(data)
|
|
||||||
} else {
|
|
||||||
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp))
|
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp))
|
||||||
|
} else {
|
||||||
|
d.md.signer, err = ssh.ParsePrivateKey(data)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -64,7 +64,7 @@ func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package sshd
|
package sshd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mdata "github.com/go-gost/core/metadata"
|
mdata "github.com/go-gost/core/metadata"
|
||||||
mdutil "github.com/go-gost/core/metadata/util"
|
mdutil "github.com/go-gost/core/metadata/util"
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/zalando/go-keyring"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,24 +26,12 @@ func (d *sshdDialer) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if key := mdutil.GetString(md, privateKeyFile); key != "" {
|
if key := mdutil.GetString(md, privateKeyFile); key != "" {
|
||||||
key, err = homedir.Expand(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data, err := os.ReadFile(key)
|
data, err := os.ReadFile(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pp string
|
pp := mdutil.GetString(md, passphrase)
|
||||||
if mdutil.GetBool(md, "passphraseFromKeyring") {
|
|
||||||
pp, err = keyring.Get(fmt.Sprintf("SSH %s", key), key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get secret(%s) from keyring: %w", key, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pp = mdutil.GetString(md, passphrase)
|
|
||||||
}
|
|
||||||
if pp == "" {
|
if pp == "" {
|
||||||
d.md.signer, err = ssh.ParsePrivateKey(data)
|
d.md.signer, err = ssh.ParsePrivateKey(data)
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,7 +40,7 @@ func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err)
|
d.logger.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package tls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
tls "github.com/refraction-networking/utls"
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err)
|
d.logger.Error(err)
|
||||||
}
|
}
|
||||||
@ -57,20 +57,8 @@ func (d *tlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dia
|
|||||||
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
|
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
|
||||||
defer conn.SetDeadline(time.Time{})
|
defer conn.SetDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
tlsConfig := d.options.TLSConfig
|
|
||||||
utlsConf := &tls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: tls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
|
tlsConn := tls.Client(conn, d.options.TLSConfig)
|
||||||
if len(tlsConfig.Certificates) > 0 {
|
|
||||||
for _, certificate := range tlsConfig.Certificates {
|
|
||||||
utlsConf.Certificates = append(utlsConf.Certificates, tls.Certificate{
|
|
||||||
Certificate: certificate.Certificate,
|
|
||||||
PrivateKey: certificate.PrivateKey,
|
|
||||||
OCSPStaple: certificate.OCSPStaple,
|
|
||||||
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
|
|
||||||
Leaf: certificate.Leaf,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tlsConn := tls.UClient(conn, utlsConf, tls.HelloChrome_Auto)
|
|
||||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -40,7 +40,7 @@ func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := options.Dialer.Dial(ctx, "udp", addr)
|
c, err := options.NetDialer.Dial(ctx, "udp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package wg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/go-gost/core/dialer"
|
|
||||||
"github.com/go-gost/core/logger"
|
|
||||||
md "github.com/go-gost/core/metadata"
|
|
||||||
"github.com/go-gost/x/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
registry.DialerRegistry().Register("wg", NewDialer)
|
|
||||||
}
|
|
||||||
|
|
||||||
type wgDialer struct {
|
|
||||||
md metadata
|
|
||||||
logger logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
|
||||||
options := &dialer.Options{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &wgDialer{
|
|
||||||
logger: options.Logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *wgDialer) Init(md md.Metadata) (err error) {
|
|
||||||
return d.parseMetadata(md)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *wgDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
|
|
||||||
var options dialer.DialOptions
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
d.logger.Error(err)
|
|
||||||
}
|
|
||||||
return conn, err
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package wg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
md "github.com/go-gost/core/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
dialTimeout = "dialTimeout"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultDialTimeout = 5 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
type metadata struct {
|
|
||||||
dialTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *wgDialer) parseMetadata(md md.Metadata) (err error) {
|
|
||||||
return
|
|
||||||
}
|
|
@ -2,8 +2,6 @@ package ws
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/go-gost/x/util"
|
|
||||||
tls "github.com/refraction-networking/utls"
|
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
@ -59,7 +57,7 @@ func (d *wsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
|
|||||||
opt(&options)
|
opt(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
|
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.options.Logger.Error(err)
|
d.options.Logger.Error(err)
|
||||||
}
|
}
|
||||||
@ -93,43 +91,13 @@ func (d *wsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dial
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
urlObj := url.URL{Scheme: "ws", Host: host, Path: d.md.path}
|
url := url.URL{Scheme: "ws", Host: host, Path: d.md.path}
|
||||||
if d.tlsEnabled {
|
if d.tlsEnabled {
|
||||||
urlObj.Scheme = "wss"
|
url.Scheme = "wss"
|
||||||
dialer.TLSClientConfig = d.options.TLSConfig
|
dialer.TLSClientConfig = d.options.TLSConfig
|
||||||
tlsConfig := d.options.TLSConfig
|
|
||||||
dialer.NetDialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
utlsConf := &tls.Config{InsecureSkipVerify: tlsConfig.InsecureSkipVerify, ServerName: tlsConfig.ServerName, ClientAuth: tls.ClientAuthType(tlsConfig.ClientAuth), ClientCAs: tlsConfig.ClientCAs, RootCAs: tlsConfig.RootCAs}
|
|
||||||
if len(tlsConfig.Certificates) > 0 {
|
|
||||||
for _, certificate := range tlsConfig.Certificates {
|
|
||||||
utlsConf.Certificates = append(utlsConf.Certificates, tls.Certificate{
|
|
||||||
Certificate: certificate.Certificate,
|
|
||||||
PrivateKey: certificate.PrivateKey,
|
|
||||||
OCSPStaple: certificate.OCSPStaple,
|
|
||||||
SignedCertificateTimestamps: certificate.SignedCertificateTimestamps,
|
|
||||||
Leaf: certificate.Leaf,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var client *tls.UConn
|
|
||||||
if d.md.useH2 {
|
|
||||||
client = tls.UClient(conn, utlsConf, tls.HelloChrome_Auto)
|
|
||||||
} else {
|
|
||||||
client = tls.UClient(conn, utlsConf, tls.HelloCustom)
|
|
||||||
client.ApplyPreset(util.NewWsSpec())
|
|
||||||
}
|
|
||||||
err := client.Handshake()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
urlStr, errUnescape := url.QueryUnescape(urlObj.String())
|
|
||||||
if errUnescape != nil {
|
c, resp, err := dialer.DialContext(ctx, url.String(), d.md.header)
|
||||||
d.options.Logger.Debugf("[ws] URL QueryUnescape Error URL.String() -> %s", urlObj.String())
|
|
||||||
}
|
|
||||||
c, resp, err := dialer.DialContext(ctx, urlStr, d.md.header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user