proxies.go 3.7 KB

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