github.com/kelleygo/clashcore@v1.0.2/hub/route/proxies.go (about) 1 package route 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "strconv" 8 "time" 9 10 "github.com/kelleygo/clashcore/adapter" 11 "github.com/kelleygo/clashcore/adapter/outboundgroup" 12 "github.com/kelleygo/clashcore/common/utils" 13 "github.com/kelleygo/clashcore/component/profile/cachefile" 14 C "github.com/kelleygo/clashcore/constant" 15 "github.com/kelleygo/clashcore/tunnel" 16 17 "github.com/go-chi/chi/v5" 18 "github.com/go-chi/render" 19 ) 20 21 var ( 22 SwitchProxiesCallback func(sGroup string, sProxy string) 23 ) 24 25 func proxyRouter() http.Handler { 26 r := chi.NewRouter() 27 r.Get("/", getProxies) 28 29 r.Route("/{name}", func(r chi.Router) { 30 r.Use(parseProxyName, findProxyByName) 31 r.Get("/", getProxy) 32 r.Get("/delay", getProxyDelay) 33 r.Put("/", updateProxy) 34 }) 35 return r 36 } 37 38 func parseProxyName(next http.Handler) http.Handler { 39 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 name := getEscapeParam(r, "name") 41 ctx := context.WithValue(r.Context(), CtxKeyProxyName, name) 42 next.ServeHTTP(w, r.WithContext(ctx)) 43 }) 44 } 45 46 func findProxyByName(next http.Handler) http.Handler { 47 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 48 name := r.Context().Value(CtxKeyProxyName).(string) 49 proxies := tunnel.ProxiesWithProviders() 50 proxy, exist := proxies[name] 51 if !exist { 52 render.Status(r, http.StatusNotFound) 53 render.JSON(w, r, ErrNotFound) 54 return 55 } 56 57 ctx := context.WithValue(r.Context(), CtxKeyProxy, proxy) 58 next.ServeHTTP(w, r.WithContext(ctx)) 59 }) 60 } 61 62 func getProxies(w http.ResponseWriter, r *http.Request) { 63 proxies := tunnel.ProxiesWithProviders() 64 render.JSON(w, r, render.M{ 65 "proxies": proxies, 66 }) 67 } 68 69 func getProxy(w http.ResponseWriter, r *http.Request) { 70 proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) 71 render.JSON(w, r, proxy) 72 } 73 74 func updateProxy(w http.ResponseWriter, r *http.Request) { 75 req := struct { 76 Name string `json:"name"` 77 }{} 78 if err := render.DecodeJSON(r.Body, &req); err != nil { 79 render.Status(r, http.StatusBadRequest) 80 render.JSON(w, r, ErrBadRequest) 81 return 82 } 83 84 proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy) 85 selector, ok := proxy.ProxyAdapter.(outboundgroup.SelectAble) 86 if !ok { 87 render.Status(r, http.StatusBadRequest) 88 render.JSON(w, r, newError("Must be a Selector")) 89 return 90 } 91 92 if err := selector.Set(req.Name); err != nil { 93 render.Status(r, http.StatusBadRequest) 94 render.JSON(w, r, newError(fmt.Sprintf("Selector update error: %s", err.Error()))) 95 return 96 } 97 98 cachefile.Cache().SetSelected(proxy.Name(), req.Name) 99 if SwitchProxiesCallback != nil { 100 // refresh tray menu 101 go SwitchProxiesCallback(proxy.Name(), req.Name) 102 } 103 render.NoContent(w, r) 104 } 105 106 func getProxyDelay(w http.ResponseWriter, r *http.Request) { 107 query := r.URL.Query() 108 url := query.Get("url") 109 timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 16) 110 if err != nil { 111 render.Status(r, http.StatusBadRequest) 112 render.JSON(w, r, ErrBadRequest) 113 return 114 } 115 116 expectedStatus, err := utils.NewUnsignedRanges[uint16](query.Get("expected")) 117 if err != nil { 118 render.Status(r, http.StatusBadRequest) 119 render.JSON(w, r, ErrBadRequest) 120 return 121 } 122 123 proxy := r.Context().Value(CtxKeyProxy).(C.Proxy) 124 125 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout)) 126 defer cancel() 127 128 delay, err := proxy.URLTest(ctx, url, expectedStatus) 129 if ctx.Err() != nil { 130 render.Status(r, http.StatusGatewayTimeout) 131 render.JSON(w, r, ErrRequestTimeout) 132 return 133 } 134 135 if err != nil || delay == 0 { 136 render.Status(r, http.StatusServiceUnavailable) 137 if err != nil && delay != 0 { 138 render.JSON(w, r, err) 139 } else { 140 render.JSON(w, r, newError("An error occurred in the delay test")) 141 } 142 return 143 } 144 145 render.JSON(w, r, render.M{ 146 "delay": delay, 147 }) 148 }