mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 11:42:43 +08:00
feat: proxies group URLTest api
This commit is contained in:
parent
58d299c737
commit
4092a7c84b
|
@ -4,6 +4,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/Dreamacro/clash/common/queue"
|
||||||
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
@ -11,10 +14,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/queue"
|
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
|
|
||||||
"go.uber.org/atomic"
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,10 @@ func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
||||||
return c, errors.New("no support")
|
return c, errors.New("no support")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||||
|
return nil, errors.New("no support")
|
||||||
|
}
|
||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||||
return nil, errors.New("no support")
|
return nil, errors.New("no support")
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package outboundgroup
|
package outboundgroup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"github.com/Dreamacro/clash/adapter/outbound"
|
"github.com/Dreamacro/clash/adapter/outbound"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/constant/provider"
|
"github.com/Dreamacro/clash/constant/provider"
|
||||||
|
@ -105,6 +107,34 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||||
return proxies
|
return proxies
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gb *GroupBase) URLTest(ctx context.Context, url string) (map[string]uint16, error) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var lock sync.Mutex
|
||||||
|
mp := map[string]uint16{}
|
||||||
|
proxies := gb.GetProxies(false)
|
||||||
|
for _, proxy := range proxies {
|
||||||
|
proxy := proxy
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
delay, err := proxy.URLTest(ctx, url)
|
||||||
|
lock.Lock()
|
||||||
|
if err == nil {
|
||||||
|
mp[proxy.Name()] = delay
|
||||||
|
}
|
||||||
|
lock.Unlock()
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if len(mp) == 0 {
|
||||||
|
return mp, fmt.Errorf("get delay: all proxies timeout")
|
||||||
|
} else {
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (gb *GroupBase) onDialFailed() {
|
func (gb *GroupBase) onDialFailed() {
|
||||||
if gb.failedTesting.Load() {
|
if gb.failedTesting.Load() {
|
||||||
return
|
return
|
||||||
|
|
|
@ -108,6 +108,11 @@ type ProxyAdapter interface {
|
||||||
Unwrap(metadata *Metadata) Proxy
|
Unwrap(metadata *Metadata) Proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Group interface {
|
||||||
|
URLTest(ctx context.Context, url string) (mp map[string]uint16, err error)
|
||||||
|
GetProxies(touch bool) []Proxy
|
||||||
|
}
|
||||||
|
|
||||||
type DelayHistory struct {
|
type DelayHistory struct {
|
||||||
Time time.Time `json:"time"`
|
Time time.Time `json:"time"`
|
||||||
Delay uint16 `json:"delay"`
|
Delay uint16 `json:"delay"`
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Vehicle Type
|
// Vehicle Type
|
||||||
|
@ -65,10 +65,10 @@ type Provider interface {
|
||||||
// ProxyProvider interface
|
// ProxyProvider interface
|
||||||
type ProxyProvider interface {
|
type ProxyProvider interface {
|
||||||
Provider
|
Provider
|
||||||
Proxies() []constant.Proxy
|
Proxies() []C.Proxy
|
||||||
// ProxiesWithTouch is used to inform the provider that the proxy is actually being used while getting the list of proxies.
|
// ProxiesWithTouch is used to inform the provider that the proxy is actually being used while getting the list of proxies.
|
||||||
// Commonly used in DialContext and DialPacketConn
|
// Commonly used in DialContext and DialPacketConn
|
||||||
ProxiesWithTouch() []constant.Proxy
|
ProxiesWithTouch() []C.Proxy
|
||||||
HealthCheck()
|
HealthCheck()
|
||||||
Version() uint
|
Version() uint
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ func (rt RuleType) String() string {
|
||||||
type RuleProvider interface {
|
type RuleProvider interface {
|
||||||
Provider
|
Provider
|
||||||
Behavior() RuleType
|
Behavior() RuleType
|
||||||
Match(*constant.Metadata) bool
|
Match(*C.Metadata) bool
|
||||||
ShouldResolveIP() bool
|
ShouldResolveIP() bool
|
||||||
AsRule(adaptor string) constant.Rule
|
AsRule(adaptor string) C.Rule
|
||||||
}
|
}
|
||||||
|
|
79
hub/route/groups.go
Normal file
79
hub/route/groups.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/Dreamacro/clash/adapter"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GroupRouter() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/", getGroups)
|
||||||
|
|
||||||
|
r.Route("/{name}", func(r chi.Router) {
|
||||||
|
r.Use(parseProxyName, findProxyByName)
|
||||||
|
r.Get("/", getGroup)
|
||||||
|
r.Get("/delay", getGroupDelay)
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var gs []C.Proxy
|
||||||
|
for _, p := range tunnel.Proxies() {
|
||||||
|
if _, ok := p.(*adapter.Proxy).ProxyAdapter.(C.Group); ok {
|
||||||
|
gs = append(gs, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render.JSON(w, r, render.M{
|
||||||
|
"proxies": gs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroup(w http.ResponseWriter, r *http.Request) {
|
||||||
|
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
|
||||||
|
if _, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group); ok {
|
||||||
|
render.JSON(w, r, proxy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
render.JSON(w, r, ErrNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupDelay(w http.ResponseWriter, r *http.Request) {
|
||||||
|
proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
|
||||||
|
group, ok := proxy.(*adapter.Proxy).ProxyAdapter.(C.Group)
|
||||||
|
if !ok {
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
render.JSON(w, r, ErrNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query := r.URL.Query()
|
||||||
|
url := query.Get("url")
|
||||||
|
timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusBadRequest)
|
||||||
|
render.JSON(w, r, ErrBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
dm, err := group.URLTest(ctx, url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusGatewayTimeout)
|
||||||
|
render.JSON(w, r, newError(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.JSON(w, r, dm)
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ func Start(addr string, secret string) {
|
||||||
r.Get("/version", version)
|
r.Get("/version", version)
|
||||||
r.Mount("/configs", configRouter())
|
r.Mount("/configs", configRouter())
|
||||||
r.Mount("/proxies", proxyRouter())
|
r.Mount("/proxies", proxyRouter())
|
||||||
|
r.Mount("/group", GroupRouter())
|
||||||
r.Mount("/rules", ruleRouter())
|
r.Mount("/rules", ruleRouter())
|
||||||
r.Mount("/connections", connectionRouter())
|
r.Mount("/connections", connectionRouter())
|
||||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user