connections.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package clashapi
  2. import (
  3. "bytes"
  4. "context"
  5. "net/http"
  6. "strconv"
  7. "time"
  8. "github.com/sagernet/sing-box/adapter"
  9. "github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
  10. "github.com/sagernet/sing/common/json"
  11. "github.com/sagernet/ws"
  12. "github.com/sagernet/ws/wsutil"
  13. "github.com/go-chi/chi/v5"
  14. "github.com/go-chi/render"
  15. "github.com/gofrs/uuid/v5"
  16. )
  17. func connectionRouter(ctx context.Context, router adapter.Router, trafficManager *trafficontrol.Manager) http.Handler {
  18. r := chi.NewRouter()
  19. r.Get("/", getConnections(ctx, trafficManager))
  20. r.Delete("/", closeAllConnections(router, trafficManager))
  21. r.Delete("/{id}", closeConnection(trafficManager))
  22. return r
  23. }
  24. func getConnections(ctx context.Context, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
  25. return func(w http.ResponseWriter, r *http.Request) {
  26. if r.Header.Get("Upgrade") != "websocket" {
  27. snapshot := trafficManager.Snapshot()
  28. render.JSON(w, r, snapshot)
  29. return
  30. }
  31. conn, _, _, err := ws.UpgradeHTTP(r, w)
  32. if err != nil {
  33. return
  34. }
  35. defer conn.Close()
  36. intervalStr := r.URL.Query().Get("interval")
  37. interval := 1000
  38. if intervalStr != "" {
  39. t, err := strconv.Atoi(intervalStr)
  40. if err != nil {
  41. render.Status(r, http.StatusBadRequest)
  42. render.JSON(w, r, ErrBadRequest)
  43. return
  44. }
  45. interval = t
  46. }
  47. buf := &bytes.Buffer{}
  48. sendSnapshot := func() error {
  49. buf.Reset()
  50. snapshot := trafficManager.Snapshot()
  51. if err := json.NewEncoder(buf).Encode(snapshot); err != nil {
  52. return err
  53. }
  54. return wsutil.WriteServerText(conn, buf.Bytes())
  55. }
  56. if err = sendSnapshot(); err != nil {
  57. return
  58. }
  59. tick := time.NewTicker(time.Millisecond * time.Duration(interval))
  60. defer tick.Stop()
  61. for {
  62. select {
  63. case <-ctx.Done():
  64. return
  65. case <-tick.C:
  66. }
  67. if err = sendSnapshot(); err != nil {
  68. break
  69. }
  70. }
  71. }
  72. }
  73. func closeConnection(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
  74. return func(w http.ResponseWriter, r *http.Request) {
  75. id := uuid.FromStringOrNil(chi.URLParam(r, "id"))
  76. snapshot := trafficManager.Snapshot()
  77. for _, c := range snapshot.Connections {
  78. if id == c.Metadata().ID {
  79. c.Close()
  80. break
  81. }
  82. }
  83. render.NoContent(w, r)
  84. }
  85. }
  86. func closeAllConnections(router adapter.Router, trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
  87. return func(w http.ResponseWriter, r *http.Request) {
  88. snapshot := trafficManager.Snapshot()
  89. for _, c := range snapshot.Connections {
  90. c.Close()
  91. }
  92. router.ResetNetwork()
  93. render.NoContent(w, r)
  94. }
  95. }