github.com/chwjbn/xclash@v0.2.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/chwjbn/xclash/adapter"
    11  	"github.com/chwjbn/xclash/adapter/outboundgroup"
    12  	"github.com/chwjbn/xclash/component/profile/cachefile"
    13  	C "github.com/chwjbn/xclash/constant"
    14  	"github.com/chwjbn/xclash/tunnel"
    15  
    16  	"github.com/go-chi/chi/v5"
    17  	"github.com/go-chi/render"
    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  		proxies := tunnel.Proxies()
    45  		proxy, exist := proxies[name]
    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  type UpdateProxyRequest struct {
    70  	Name string `json:"name"`
    71  }
    72  
    73  func updateProxy(w http.ResponseWriter, r *http.Request) {
    74  	req := UpdateProxyRequest{}
    75  	if err := render.DecodeJSON(r.Body, &req); err != nil {
    76  		render.Status(r, http.StatusBadRequest)
    77  		render.JSON(w, r, ErrBadRequest)
    78  		return
    79  	}
    80  
    81  	proxy := r.Context().Value(CtxKeyProxy).(*adapter.Proxy)
    82  	selector, ok := proxy.ProxyAdapter.(*outboundgroup.Selector)
    83  	if !ok {
    84  		render.Status(r, http.StatusBadRequest)
    85  		render.JSON(w, r, newError("Must be a Selector"))
    86  		return
    87  	}
    88  
    89  	if err := selector.Set(req.Name); err != nil {
    90  		render.Status(r, http.StatusBadRequest)
    91  		render.JSON(w, r, newError(fmt.Sprintf("Selector update error: %s", err.Error())))
    92  		return
    93  	}
    94  
    95  	cachefile.Cache().SetSelected(proxy.Name(), req.Name)
    96  	render.NoContent(w, r)
    97  }
    98  
    99  func getProxyDelay(w http.ResponseWriter, r *http.Request) {
   100  	query := r.URL.Query()
   101  	url := query.Get("url")
   102  	timeout, err := strconv.ParseInt(query.Get("timeout"), 10, 16)
   103  	if err != nil {
   104  		render.Status(r, http.StatusBadRequest)
   105  		render.JSON(w, r, ErrBadRequest)
   106  		return
   107  	}
   108  
   109  	proxy := r.Context().Value(CtxKeyProxy).(C.Proxy)
   110  
   111  	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))
   112  	defer cancel()
   113  
   114  	delay, err := proxy.URLTest(ctx, url)
   115  	if ctx.Err() != nil {
   116  		render.Status(r, http.StatusGatewayTimeout)
   117  		render.JSON(w, r, ErrRequestTimeout)
   118  		return
   119  	}
   120  
   121  	if err != nil || delay == 0 {
   122  		render.Status(r, http.StatusServiceUnavailable)
   123  		render.JSON(w, r, newError("An error occurred in the delay test"))
   124  		return
   125  	}
   126  
   127  	render.JSON(w, r, render.M{
   128  		"delay": delay,
   129  	})
   130  }