Add Snell protocol. Refactor MASQUE HTTP/2, Fair Queue. Update XHTTP, OpenVPN, Sudoku, Fallback. Fixes

This commit is contained in:
Shtorm
2026-06-26 01:25:57 +03:00
parent d174962a04
commit edf38d33d6
107 changed files with 5346 additions and 708 deletions

View File

@@ -2,11 +2,11 @@ package bandwidth
import (
"context"
"slices"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/list"
)
type BandwidthLimiter interface {
@@ -14,123 +14,144 @@ type BandwidthLimiter interface {
SetSpeed(speed uint64)
}
type FlowKeysLimiter struct {
type FairQueueLimiter struct {
limiter BandwidthLimiter
connIDGetter ConnIDGetter
waits map[string][]*wait
conns map[string]int
flows *list.List[*flow]
index map[string]*list.Element[*flow]
bytes map[string]uint64
pool sync.Pool
queue chan struct{}
reset time.Time
mtx sync.Mutex
}
func NewFlowKeysLimiter(connIDGetter ConnIDGetter, limiter BandwidthLimiter) *FlowKeysLimiter {
return &FlowKeysLimiter{
func NewFairQueueLimiter(connIDGetter ConnIDGetter, limiter BandwidthLimiter) *FairQueueLimiter {
return &FairQueueLimiter{
limiter: limiter,
connIDGetter: connIDGetter,
waits: make(map[string][]*wait),
conns: make(map[string]int),
flows: list.New[*flow](),
index: make(map[string]*list.Element[*flow]),
bytes: make(map[string]uint64),
pool: sync.Pool{New: func() any { return list.New[*request]() }},
queue: make(chan struct{}, 1),
reset: time.Now().Add(time.Second),
}
}
func (l *FlowKeysLimiter) SetSpeed(speed uint64) {
func (l *FairQueueLimiter) SetSpeed(speed uint64) {
l.limiter.SetSpeed(speed)
}
func (l *FlowKeysLimiter) WaitN(ctx context.Context, n int) error {
func (l *FairQueueLimiter) WaitN(ctx context.Context, n int) error {
id, _ := l.connIDGetter(ctx, adapter.ContextFrom(ctx))
mainWait := &wait{ctx, make(chan struct{}), n}
mainRequest := &request{ctx: ctx, done: make(chan struct{}), n: n}
l.mtx.Lock()
if waits, ok := l.waits[id]; ok {
l.waits[id] = append(waits, mainWait)
} else {
l.waits[id] = []*wait{mainWait}
elem, ok := l.index[id]
if !ok {
f := &flow{id: id, pending: l.pool.Get().(*list.List[*request])}
elem = l.flows.PushFront(f)
l.index[id] = elem
}
mainRequestElem := elem.Value.pending.PushBack(mainRequest)
l.reorder(elem)
l.mtx.Unlock()
select {
case l.queue <- struct{}{}:
case <-mainWait.finish:
case <-mainRequest.done:
return nil
case <-ctx.Done():
l.mtx.Lock()
for i, wait := range l.waits[id] {
if wait == mainWait {
l.waits[id] = slices.Delete(l.waits[id], i, i+1)
close(wait.finish)
break
}
}
l.removeRequest(id, mainRequestElem)
l.mtx.Unlock()
return ctx.Err()
}
select {
case <-mainRequest.done:
<-l.queue
return nil
default:
}
for {
if ctx.Err() != nil {
l.mtx.Lock()
for i, wait := range l.waits[id] {
if wait == mainWait {
l.waits[id] = slices.Delete(l.waits[id], i, i+1)
close(wait.finish)
break
}
}
l.removeRequest(id, mainRequestElem)
l.mtx.Unlock()
<-l.queue
return ctx.Err()
}
l.mtx.Lock()
now := time.Now()
if l.reset.Compare(now) == -1 {
clear(l.conns)
clear(l.bytes)
l.reset = now.Add(time.Second)
}
l.mtx.Lock()
var minConnId string
var minN int
for connID, waits := range l.waits {
if len(waits) == 0 {
continue
}
if n, ok := l.conns[connID]; ok {
if minConnId == "" {
minConnId = connID
minN = n
continue
}
if n+waits[0].n < minN {
minConnId = connID
minN = n
}
} else {
l.conns[connID] = 0
minConnId = connID
break
}
}
minWait := l.waits[minConnId][0]
l.waits[minConnId][0] = nil
l.waits[minConnId] = l.waits[minConnId][1:]
if len(l.waits) == 0 {
delete(l.waits, minConnId)
flowElem := l.flows.Front()
flow := flowElem.Value
firstRequestElem := flow.pending.Front()
firstRequest := firstRequestElem.Value
l.bytes[flow.id] += uint64(firstRequest.n)
firstRequestElem.Remove()
if flow.pending.Len() == 0 {
l.flows.Remove(flowElem)
delete(l.index, flow.id)
l.pool.Put(flow.pending)
} else {
l.reorder(flowElem)
}
l.mtx.Unlock()
err := l.limiter.WaitN(ctx, minWait.n)
if err != nil {
continue
}
l.conns[minConnId] = l.conns[minConnId] + minWait.n
close(minWait.finish)
if minWait == mainWait {
l.limiter.WaitN(firstRequest.ctx, firstRequest.n)
close(firstRequest.done)
if firstRequest == mainRequest {
<-l.queue
return nil
}
}
}
type wait struct {
ctx context.Context
finish chan struct{}
n int
func (l *FairQueueLimiter) reorder(elem *list.Element[*flow]) {
f := elem.Value
front := f.pending.Front()
if front == nil {
return
}
cost := l.bytes[f.id] + uint64(front.Value.n)
for e := l.flows.Front(); e != nil; e = e.Next() {
if e == elem {
continue
}
eFront := e.Value.pending.Front()
if eFront == nil {
continue
}
if cost < l.bytes[e.Value.id]+uint64(eFront.Value.n) {
l.flows.MoveBefore(elem, e)
return
}
}
l.flows.MoveToBack(elem)
}
func (l *FairQueueLimiter) removeRequest(id string, elem *list.Element[*request]) {
if !elem.Remove() {
return
}
if flowElem, ok := l.index[id]; ok && flowElem.Value.pending.Len() == 0 {
l.flows.Remove(flowElem)
delete(l.index, id)
l.pool.Put(flowElem.Value.pending)
}
}
type flow struct {
id string
pending *list.List[*request]
}
type request struct {
ctx context.Context
done chan struct{}
n int
}

View File

@@ -357,7 +357,7 @@ func createSpeedLimiter(speed uint64, flowKeys []string) (BandwidthLimiter, erro
if err != nil {
return nil, err
}
limiter = NewFlowKeysLimiter(getter, limiter)
limiter = NewFairQueueLimiter(getter, limiter)
}
return limiter, nil
}