// Copyright 2017 fatedier, fatedier@gmail.com // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package server import ( "encoding/json" "net/http" "github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/metrics/mem" "github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/version" "github.com/gorilla/mux" ) type GeneralResponse struct { Code int Msg string } type serverInfoResp struct { Version string `json:"version"` BindPort int `json:"bind_port"` BindUDPPort int `json:"bind_udp_port"` VhostHTTPPort int `json:"vhost_http_port"` VhostHTTPSPort int `json:"vhost_https_port"` KCPBindPort int `json:"kcp_bind_port"` SubdomainHost string `json:"subdomain_host"` MaxPoolCount int64 `json:"max_pool_count"` MaxPortsPerClient int64 `json:"max_ports_per_client"` HeartBeatTimeout int64 `json:"heart_beat_timeout"` TotalTrafficIn int64 `json:"total_traffic_in"` TotalTrafficOut int64 `json:"total_traffic_out"` CurConns int64 `json:"cur_conns"` ClientCounts int64 `json:"client_counts"` ProxyTypeCounts map[string]int64 `json:"proxy_type_count"` } // api/serverinfo func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} defer func() { log.Info("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.Info("Http request: [%s]", r.URL.Path) serverStats := mem.StatsCollector.GetServer() svrResp := serverInfoResp{ Version: version.Full(), BindPort: svr.cfg.BindPort, BindUDPPort: svr.cfg.BindUDPPort, VhostHTTPPort: svr.cfg.VhostHTTPPort, VhostHTTPSPort: svr.cfg.VhostHTTPSPort, KCPBindPort: svr.cfg.KCPBindPort, SubdomainHost: svr.cfg.SubDomainHost, MaxPoolCount: svr.cfg.MaxPoolCount, MaxPortsPerClient: svr.cfg.MaxPortsPerClient, HeartBeatTimeout: svr.cfg.HeartbeatTimeout, TotalTrafficIn: serverStats.TotalTrafficIn, TotalTrafficOut: serverStats.TotalTrafficOut, CurConns: serverStats.CurConns, ClientCounts: serverStats.ClientCounts, ProxyTypeCounts: serverStats.ProxyTypeCounts, } buf, _ := json.Marshal(&svrResp) res.Msg = string(buf) } type BaseOutConf struct { config.BaseProxyConf } type TCPOutConf struct { BaseOutConf RemotePort int `json:"remote_port"` } type TCPMuxOutConf struct { BaseOutConf config.DomainConf Multiplexer string `json:"multiplexer"` } type UDPOutConf struct { BaseOutConf RemotePort int `json:"remote_port"` } type HTTPOutConf struct { BaseOutConf config.DomainConf Locations []string `json:"locations"` HostHeaderRewrite string `json:"host_header_rewrite"` } type HTTPSOutConf struct { BaseOutConf config.DomainConf } type STCPOutConf struct { BaseOutConf } type XTCPOutConf struct { BaseOutConf } func getConfByType(proxyType string) interface{} { switch proxyType { case consts.TCPProxy: return &TCPOutConf{} case consts.TCPMuxProxy: return &TCPMuxOutConf{} case consts.UDPProxy: return &UDPOutConf{} case consts.HTTPProxy: return &HTTPOutConf{} case consts.HTTPSProxy: return &HTTPSOutConf{} case consts.STCPProxy: return &STCPOutConf{} case consts.XTCPProxy: return &XTCPOutConf{} default: return nil } } // Get proxy info. type ProxyStatsInfo struct { Name string `json:"name"` Conf interface{} `json:"conf"` TodayTrafficIn int64 `json:"today_traffic_in"` TodayTrafficOut int64 `json:"today_traffic_out"` CurConns int64 `json:"cur_conns"` LastStartTime string `json:"last_start_time"` LastCloseTime string `json:"last_close_time"` Status string `json:"status"` } type GetProxyInfoResp struct { Proxies []*ProxyStatsInfo `json:"proxies"` } // api/proxy/:type func (svr *Service) APIProxyByType(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) proxyType := params["type"] defer func() { log.Info("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.Info("Http request: [%s]", r.URL.Path) proxyInfoResp := GetProxyInfoResp{} proxyInfoResp.Proxies = svr.getProxyStatsByType(proxyType) buf, _ := json.Marshal(&proxyInfoResp) res.Msg = string(buf) } func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) { proxyStats := mem.StatsCollector.GetProxiesByType(proxyType) proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats)) for _, ps := range proxyStats { proxyInfo := &ProxyStatsInfo{} if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok { content, err := json.Marshal(pxy.GetConf()) if err != nil { log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err) continue } proxyInfo.Conf = getConfByType(ps.Type) if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil { log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err) continue } proxyInfo.Status = consts.Online } else { proxyInfo.Status = consts.Offline } proxyInfo.Name = ps.Name proxyInfo.TodayTrafficIn = ps.TodayTrafficIn proxyInfo.TodayTrafficOut = ps.TodayTrafficOut proxyInfo.CurConns = ps.CurConns proxyInfo.LastStartTime = ps.LastStartTime proxyInfo.LastCloseTime = ps.LastCloseTime proxyInfos = append(proxyInfos, proxyInfo) } return } // Get proxy info by name. type GetProxyStatsResp struct { Name string `json:"name"` Conf interface{} `json:"conf"` TodayTrafficIn int64 `json:"today_traffic_in"` TodayTrafficOut int64 `json:"today_traffic_out"` CurConns int64 `json:"cur_conns"` LastStartTime string `json:"last_start_time"` LastCloseTime string `json:"last_close_time"` Status string `json:"status"` } // api/proxy/:type/:name func (svr *Service) APIProxyByTypeAndName(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) proxyType := params["type"] name := params["name"] defer func() { log.Info("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.Info("Http request: [%s]", r.URL.Path) proxyStatsResp := GetProxyStatsResp{} proxyStatsResp, res.Code, res.Msg = svr.getProxyStatsByTypeAndName(proxyType, name) if res.Code != 200 { return } buf, _ := json.Marshal(&proxyStatsResp) res.Msg = string(buf) } func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) { proxyInfo.Name = proxyName ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName) if ps == nil { code = 404 msg = "no proxy info found" } else { if pxy, ok := svr.pxyManager.GetByName(proxyName); ok { content, err := json.Marshal(pxy.GetConf()) if err != nil { log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err) code = 400 msg = "parse conf error" return } proxyInfo.Conf = getConfByType(ps.Type) if err = json.Unmarshal(content, &proxyInfo.Conf); err != nil { log.Warn("unmarshal proxy [%s] conf info error: %v", ps.Name, err) code = 400 msg = "parse conf error" return } proxyInfo.Status = consts.Online } else { proxyInfo.Status = consts.Offline } proxyInfo.TodayTrafficIn = ps.TodayTrafficIn proxyInfo.TodayTrafficOut = ps.TodayTrafficOut proxyInfo.CurConns = ps.CurConns proxyInfo.LastStartTime = ps.LastStartTime proxyInfo.LastCloseTime = ps.LastCloseTime code = 200 } return } // api/traffic/:name type GetProxyTrafficResp struct { Name string `json:"name"` TrafficIn []int64 `json:"traffic_in"` TrafficOut []int64 `json:"traffic_out"` } func (svr *Service) APIProxyTraffic(w http.ResponseWriter, r *http.Request) { res := GeneralResponse{Code: 200} params := mux.Vars(r) name := params["name"] defer func() { log.Info("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.Info("Http request: [%s]", r.URL.Path) trafficResp := GetProxyTrafficResp{} trafficResp.Name = name proxyTrafficInfo := mem.StatsCollector.GetProxyTraffic(name) if proxyTrafficInfo == nil { res.Code = 404 res.Msg = "no proxy info found" return } trafficResp.TrafficIn = proxyTrafficInfo.TrafficIn trafficResp.TrafficOut = proxyTrafficInfo.TrafficOut buf, _ := json.Marshal(&trafficResp) res.Msg = string(buf) }