v2ray-core/app/proxyman/mux/mux.go

138 lines
2.5 KiB
Go
Raw Normal View History

2017-02-07 20:11:47 +00:00
package mux
2017-03-31 22:53:01 +00:00
import (
"context"
"sync"
"time"
"v2ray.com/core/common/buf"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy"
"v2ray.com/core/transport/ray"
)
2017-02-07 20:11:47 +00:00
2017-03-26 23:47:01 +00:00
const (
maxParallel = 8
maxTotal = 128
)
2017-03-31 22:53:01 +00:00
type clientSession struct {
sync.Mutex
outboundRay ray.OutboundRay
parent *Client
id uint16
uplinkClosed bool
downlinkClosed bool
}
func (s *clientSession) checkAndRemove() {
s.Lock()
if s.uplinkClosed && s.downlinkClosed {
s.parent.remove(s.id)
}
s.Unlock()
}
func (s *clientSession) closeUplink() {
s.Lock()
s.uplinkClosed = true
s.Unlock()
s.checkAndRemove()
}
func (s *clientSession) closeDownlink() {
s.Lock()
s.downlinkClosed = true
s.Unlock()
s.checkAndRemove()
}
type Client struct {
access sync.RWMutex
count uint16
sessions map[uint16]*clientSession
inboundRay ray.InboundRay
}
func (m *Client) IsFullyOccupied() bool {
m.access.RLock()
defer m.access.RUnlock()
return len(m.sessions) >= maxParallel
}
func (m *Client) IsFullyUsed() bool {
m.access.RLock()
defer m.access.RUnlock()
return m.count >= maxTotal
2017-02-07 20:11:47 +00:00
}
2017-03-31 22:53:01 +00:00
func (m *Client) remove(id uint16) {
m.access.Lock()
delete(m.sessions, id)
m.access.Unlock()
2017-03-26 23:47:01 +00:00
}
2017-03-31 22:53:01 +00:00
func (m *Client) fetchInput(ctx context.Context, session *clientSession) {
dest, _ := proxy.TargetFromContext(ctx)
writer := &muxWriter{
dest: dest,
id: session.id,
writer: m.inboundRay.InboundInput(),
}
_, timer := signal.CancelAfterInactivity(ctx, time.Minute*5)
buf.PipeUntilEOF(timer, session.outboundRay.OutboundInput(), writer)
writer.Close()
session.closeUplink()
}
func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) {
m.access.Lock()
defer m.access.Unlock()
m.count++
id := m.count
session := &clientSession{
outboundRay: outboundRay,
parent: m,
id: id,
}
m.sessions[id] = session
go m.fetchInput(ctx, session)
}
func (m *Client) fetchOutput() {
reader := NewReader(m.inboundRay.InboundOutput())
for {
meta, err := reader.ReadMetadata()
if err != nil {
break
}
m.access.RLock()
session, found := m.sessions[meta.SessionID]
m.access.RUnlock()
if found && meta.SessionStatus == SessionStatusEnd {
session.closeDownlink()
}
if !meta.Option.Has(OptionData) {
continue
}
for {
data, more, err := reader.Read()
if err != nil {
break
}
if found {
if err := session.outboundRay.OutboundOutput().Write(data); err != nil {
break
}
}
if !more {
break
}
}
}
2017-02-07 20:11:47 +00:00
}