|
|
@@ -57,6 +57,52 @@ func (s *session) closeDownlink() {
|
|
|
s.checkAndRemove()
|
|
|
}
|
|
|
|
|
|
+type ClientManager struct {
|
|
|
+ access sync.Mutex
|
|
|
+ clients []*Client
|
|
|
+ proxy proxy.Outbound
|
|
|
+ dialer proxy.Dialer
|
|
|
+}
|
|
|
+
|
|
|
+func NewClientManager(p proxy.Outbound, d proxy.Dialer) *ClientManager {
|
|
|
+ return &ClientManager{
|
|
|
+ proxy: p,
|
|
|
+ dialer: d,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (m *ClientManager) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) error {
|
|
|
+ m.access.Lock()
|
|
|
+ defer m.access.Unlock()
|
|
|
+
|
|
|
+ for _, client := range m.clients {
|
|
|
+ if client.Dispatch(ctx, outboundRay) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ client, err := NewClient(m.proxy, m.dialer, m)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ m.clients = append(m.clients, client)
|
|
|
+ client.Dispatch(ctx, outboundRay)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (m *ClientManager) onClientFinish() {
|
|
|
+ m.access.Lock()
|
|
|
+ defer m.access.Unlock()
|
|
|
+
|
|
|
+ nActive := 0
|
|
|
+ for idx, client := range m.clients {
|
|
|
+ if nActive != idx && !client.Closed() {
|
|
|
+ m.clients[nActive] = client
|
|
|
+ }
|
|
|
+ }
|
|
|
+ m.clients = m.clients[:nActive]
|
|
|
+}
|
|
|
+
|
|
|
type Client struct {
|
|
|
access sync.RWMutex
|
|
|
count uint16
|
|
|
@@ -64,11 +110,12 @@ type Client struct {
|
|
|
inboundRay ray.InboundRay
|
|
|
ctx context.Context
|
|
|
cancel context.CancelFunc
|
|
|
+ manager *ClientManager
|
|
|
}
|
|
|
|
|
|
var muxCoolDestination = net.TCPDestination(net.DomainAddress("v1.mux.cool"), net.Port(9527))
|
|
|
|
|
|
-func NewClient(p proxy.Outbound, dialer proxy.Dialer) (*Client, error) {
|
|
|
+func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) {
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
ctx = proxy.ContextWithTarget(ctx, muxCoolDestination)
|
|
|
pipe := ray.NewRay(ctx)
|
|
|
@@ -82,6 +129,7 @@ func NewClient(p proxy.Outbound, dialer proxy.Dialer) (*Client, error) {
|
|
|
inboundRay: pipe,
|
|
|
ctx: ctx,
|
|
|
cancel: cancel,
|
|
|
+ manager: m,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
@@ -101,6 +149,7 @@ func (m *Client) remove(id uint16) {
|
|
|
if len(m.sessions) == 0 {
|
|
|
m.cancel()
|
|
|
m.inboundRay.InboundInput().Close()
|
|
|
+ go m.manager.onClientFinish()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -121,7 +170,10 @@ func (m *Client) fetchInput(ctx context.Context, s *session) {
|
|
|
writer: m.inboundRay.InboundInput(),
|
|
|
}
|
|
|
_, timer := signal.CancelAfterInactivity(ctx, time.Minute*5)
|
|
|
- buf.PipeUntilEOF(timer, s.input, writer)
|
|
|
+ if err := buf.PipeUntilEOF(timer, s.input, writer); err != nil {
|
|
|
+ log.Info("Proxyman|Mux|Client: Failed to fetch all input: ", err)
|
|
|
+ }
|
|
|
+
|
|
|
writer.Close()
|
|
|
s.closeUplink()
|
|
|
}
|