github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/experimental/clashapi/connections.go (about)

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