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