-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
223 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters