github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/clashapi/connections.go (about)

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