mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-09-19 20:15:59 +08:00
chore: rebuild slowdown code
This commit is contained in:
34
component/proxydialer/slowdown.go
Normal file
34
component/proxydialer/slowdown.go
Normal file
@ -0,0 +1,34 @@
|
||||
package proxydialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/component/slowdown"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type SlowDownDialer struct {
|
||||
C.Dialer
|
||||
Slowdown *slowdown.SlowDown
|
||||
}
|
||||
|
||||
func (d SlowDownDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {
|
||||
return d.Dialer.DialContext(ctx, network, address)
|
||||
})
|
||||
}
|
||||
|
||||
func (d SlowDownDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||
return slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {
|
||||
return d.Dialer.ListenPacket(ctx, network, address, rAddrPort)
|
||||
})
|
||||
}
|
||||
|
||||
func NewSlowDownDialer(d C.Dialer, sd *slowdown.SlowDown) SlowDownDialer {
|
||||
return SlowDownDialer{
|
||||
Dialer: d,
|
||||
Slowdown: sd,
|
||||
}
|
||||
}
|
33
component/proxydialer/slowdown_sing.go
Normal file
33
component/proxydialer/slowdown_sing.go
Normal file
@ -0,0 +1,33 @@
|
||||
package proxydialer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/component/slowdown"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type SlowDownSingDialer struct {
|
||||
SingDialer
|
||||
Slowdown *slowdown.SlowDown
|
||||
}
|
||||
|
||||
func (d SlowDownSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
return slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {
|
||||
return d.SingDialer.DialContext(ctx, network, destination)
|
||||
})
|
||||
}
|
||||
|
||||
func (d SlowDownSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
return slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {
|
||||
return d.SingDialer.ListenPacket(ctx, destination)
|
||||
})
|
||||
}
|
||||
|
||||
func NewSlowDownSingDialer(d SingDialer, sd *slowdown.SlowDown) SlowDownSingDialer {
|
||||
return SlowDownSingDialer{
|
||||
SingDialer: d,
|
||||
Slowdown: sd,
|
||||
}
|
||||
}
|
101
component/slowdown/backoff.go
Normal file
101
component/slowdown/backoff.go
Normal file
@ -0,0 +1,101 @@
|
||||
// modify from https://github.com/jpillora/backoff/blob/v1.0.0/backoff.go
|
||||
|
||||
package slowdown
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Backoff is a time.Duration counter, starting at Min. After every call to
|
||||
// the Duration method the current timing is multiplied by Factor, but it
|
||||
// never exceeds Max.
|
||||
//
|
||||
// Backoff is not generally concurrent-safe, but the ForAttempt method can
|
||||
// be used concurrently.
|
||||
type Backoff struct {
|
||||
attempt atomic.Uint64
|
||||
// Factor is the multiplying factor for each increment step
|
||||
Factor float64
|
||||
// Jitter eases contention by randomizing backoff steps
|
||||
Jitter bool
|
||||
// Min and Max are the minimum and maximum values of the counter
|
||||
Min, Max time.Duration
|
||||
}
|
||||
|
||||
// Duration returns the duration for the current attempt before incrementing
|
||||
// the attempt counter. See ForAttempt.
|
||||
func (b *Backoff) Duration() time.Duration {
|
||||
d := b.ForAttempt(float64(b.attempt.Add(1) - 1))
|
||||
return d
|
||||
}
|
||||
|
||||
const maxInt64 = float64(math.MaxInt64 - 512)
|
||||
|
||||
// ForAttempt returns the duration for a specific attempt. This is useful if
|
||||
// you have a large number of independent Backoffs, but don't want use
|
||||
// unnecessary memory storing the Backoff parameters per Backoff. The first
|
||||
// attempt should be 0.
|
||||
//
|
||||
// ForAttempt is concurrent-safe.
|
||||
func (b *Backoff) ForAttempt(attempt float64) time.Duration {
|
||||
// Zero-values are nonsensical, so we use
|
||||
// them to apply defaults
|
||||
min := b.Min
|
||||
if min <= 0 {
|
||||
min = 100 * time.Millisecond
|
||||
}
|
||||
max := b.Max
|
||||
if max <= 0 {
|
||||
max = 10 * time.Second
|
||||
}
|
||||
if min >= max {
|
||||
// short-circuit
|
||||
return max
|
||||
}
|
||||
factor := b.Factor
|
||||
if factor <= 0 {
|
||||
factor = 2
|
||||
}
|
||||
//calculate this duration
|
||||
minf := float64(min)
|
||||
durf := minf * math.Pow(factor, attempt)
|
||||
if b.Jitter {
|
||||
durf = rand.Float64()*(durf-minf) + minf
|
||||
}
|
||||
//ensure float64 wont overflow int64
|
||||
if durf > maxInt64 {
|
||||
return max
|
||||
}
|
||||
dur := time.Duration(durf)
|
||||
//keep within bounds
|
||||
if dur < min {
|
||||
return min
|
||||
}
|
||||
if dur > max {
|
||||
return max
|
||||
}
|
||||
return dur
|
||||
}
|
||||
|
||||
// Reset restarts the current attempt counter at zero.
|
||||
func (b *Backoff) Reset() {
|
||||
b.attempt.Store(0)
|
||||
}
|
||||
|
||||
// Attempt returns the current attempt counter value.
|
||||
func (b *Backoff) Attempt() float64 {
|
||||
return float64(b.attempt.Load())
|
||||
}
|
||||
|
||||
// Copy returns a backoff with equals constraints as the original
|
||||
func (b *Backoff) Copy() *Backoff {
|
||||
return &Backoff{
|
||||
Factor: b.Factor,
|
||||
Jitter: b.Jitter,
|
||||
Min: b.Min,
|
||||
Max: b.Max,
|
||||
}
|
||||
}
|
49
component/slowdown/slowdown.go
Normal file
49
component/slowdown/slowdown.go
Normal file
@ -0,0 +1,49 @@
|
||||
package slowdown
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SlowDown struct {
|
||||
errTimes atomic.Int64
|
||||
backoff Backoff
|
||||
}
|
||||
|
||||
func (s *SlowDown) Wait(ctx context.Context) (err error) {
|
||||
select {
|
||||
case <-time.After(s.backoff.Duration()):
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func New() *SlowDown {
|
||||
return &SlowDown{
|
||||
backoff: Backoff{
|
||||
Min: 10 * time.Millisecond,
|
||||
Max: 1 * time.Second,
|
||||
Factor: 2,
|
||||
Jitter: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Do[T any](s *SlowDown, ctx context.Context, fn func() (T, error)) (t T, err error) {
|
||||
if s.errTimes.Load() > 10 {
|
||||
err = s.Wait(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
t, err = fn()
|
||||
if err != nil {
|
||||
s.errTimes.Add(1)
|
||||
return
|
||||
}
|
||||
s.errTimes.Store(0)
|
||||
s.backoff.Reset()
|
||||
return
|
||||
}
|
Reference in New Issue
Block a user