Compare commits

...

3 Commits

4 changed files with 84 additions and 3 deletions

View File

@@ -206,8 +206,9 @@ func (m *serverMetrics) GetProxiesByType(proxyType string) []*ProxyStats {
m.mu.Lock()
defer m.mu.Unlock()
filterAll := proxyType == "" || proxyType == "all"
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
if !filterAll && proxyStats.ProxyType != proxyType {
continue
}
@@ -233,8 +234,9 @@ func (m *serverMetrics) GetProxiesByTypeAndName(proxyType string, proxyName stri
m.mu.Lock()
defer m.mu.Unlock()
filterAll := proxyType == "" || proxyType == "all"
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
if !filterAll && proxyStats.ProxyType != proxyType {
continue
}

View File

@@ -14,7 +14,7 @@
package version
var version = "0.65.0"
var version = "LoliaFRP 0.65.0"
func Full() string {
return version

View File

@@ -94,6 +94,28 @@ func (cm *ControlManager) Close() error {
return nil
}
// KickByProxyName finds the Control that manages the given proxy (tunnel) name and closes
// the entire control connection (disconnects the frpc). Returns an error if no such proxy is found.
func (cm *ControlManager) KickByProxyName(proxyName string) error {
cm.mu.RLock()
var target *Control
for _, ctl := range cm.ctlsByRunID {
ctl.mu.RLock()
_, ok := ctl.proxies[proxyName]
ctl.mu.RUnlock()
if ok {
target = ctl
break
}
}
cm.mu.RUnlock()
if target == nil {
return fmt.Errorf("no proxy found with name [%s]", proxyName)
}
return target.Close()
}
type Control struct {
// all resource managers and controllers
rc *controller.ResourceController

View File

@@ -52,8 +52,10 @@ func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper)
subRouter.HandleFunc("/api/serverinfo", svr.apiServerInfo).Methods("GET")
subRouter.HandleFunc("/api/proxy/{type}", svr.apiProxyByType).Methods("GET")
subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.apiProxyByTypeAndName).Methods("GET")
subRouter.HandleFunc("/api/proxy/{name}/kick", svr.apiKickProxyByName).Methods("POST")
subRouter.HandleFunc("/api/traffic/{name}", svr.apiProxyTraffic).Methods("GET")
subRouter.HandleFunc("/api/proxies", svr.deleteProxies).Methods("DELETE")
subRouter.HandleFunc("/api/proxies", svr.apiProxiesAll).Methods("GET")
// view
subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET")
@@ -211,6 +213,29 @@ type GetProxyInfoResp struct {
Proxies []*ProxyStatsInfo `json:"proxies"`
}
// GET /api/proxies
// Return all proxies across types (tcp, udp, http, https, stcp, xtcp, tcpmux)
func (svr *Service) apiProxiesAll(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
defer func() {
log.Infof("http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
log.Infof("http request: [%s]", r.URL.Path)
proxyInfoResp := GetProxyInfoResp{}
proxyInfoResp.Proxies = svr.getProxyStatsByType("all")
slices.SortFunc(proxyInfoResp.Proxies, func(a, b *ProxyStatsInfo) int {
return cmp.Compare(a.Name, b.Name)
})
buf, _ := json.Marshal(&proxyInfoResp)
res.Msg = string(buf)
}
// /api/proxy/:type
func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
@@ -237,6 +262,7 @@ func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
}
func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
// mem.StatsCollector now supports proxyType=="all" or "" to return all proxies
proxyStats := mem.StatsCollector.GetProxiesByType(proxyType)
proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats))
for _, ps := range proxyStats {
@@ -308,6 +334,37 @@ func (svr *Service) apiProxyByTypeAndName(w http.ResponseWriter, r *http.Request
res.Msg = string(buf)
}
// POST /api/proxy/:name/kick
// Kick the client (frpc) that owns the proxy with given name.
func (svr *Service) apiKickProxyByName(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
params := mux.Vars(r)
name := params["name"]
defer func() {
log.Infof("http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
log.Infof("http request: [%s]", r.URL.Path)
if name == "" {
res.Code = 400
res.Msg = "proxy name required"
return
}
if err := svr.ctlManager.KickByProxyName(name); err != nil {
res.Code = 404
res.Msg = err.Error()
return
}
res.Msg = "ok"
}
func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
proxyInfo.Name = proxyName
ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName)