add scope param for traffic limiter
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/plugin/limiter/traffic/proto"
|
||||
@@ -44,18 +45,20 @@ func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) traffic.Traf
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *grpcPlugin) In(ctx context.Context, key string, opts ...traffic.Option) traffic.Limiter {
|
||||
func (p *grpcPlugin) In(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
if p.client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var options traffic.Options
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
r, err := p.client.Limit(ctx,
|
||||
&proto.LimitRequest{
|
||||
Service: options.Service,
|
||||
Scope: options.Scope,
|
||||
Network: options.Network,
|
||||
Addr: options.Addr,
|
||||
Client: options.Client,
|
||||
@@ -69,18 +72,20 @@ func (p *grpcPlugin) In(ctx context.Context, key string, opts ...traffic.Option)
|
||||
return xtraffic.NewLimiter(int(r.In))
|
||||
}
|
||||
|
||||
func (p *grpcPlugin) Out(ctx context.Context, key string, opts ...traffic.Option) traffic.Limiter {
|
||||
func (p *grpcPlugin) Out(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
if p.client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var options traffic.Options
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
r, err := p.client.Limit(ctx,
|
||||
&proto.LimitRequest{
|
||||
Service: options.Service,
|
||||
Scope: options.Scope,
|
||||
Network: options.Network,
|
||||
Addr: options.Addr,
|
||||
Client: options.Client,
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/internal/plugin"
|
||||
@@ -13,6 +14,8 @@ import (
|
||||
)
|
||||
|
||||
type httpPluginRequest struct {
|
||||
Service string `json:"service"`
|
||||
Scope string `json:"scope"`
|
||||
Network string `json:"network"`
|
||||
Addr string `json:"addr"`
|
||||
Client string `json:"client"`
|
||||
@@ -49,17 +52,19 @@ func NewHTTPPlugin(name string, url string, opts ...plugin.Option) traffic.Traff
|
||||
}
|
||||
}
|
||||
|
||||
func (p *httpPlugin) In(ctx context.Context, key string, opts ...traffic.Option) traffic.Limiter {
|
||||
func (p *httpPlugin) In(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
if p.client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var options traffic.Options
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
rb := httpPluginRequest{
|
||||
Service: options.Service,
|
||||
Scope: options.Scope,
|
||||
Network: options.Network,
|
||||
Addr: options.Addr,
|
||||
Client: options.Client,
|
||||
@@ -67,11 +72,13 @@ func (p *httpPlugin) In(ctx context.Context, key string, opts ...traffic.Option)
|
||||
}
|
||||
v, err := json.Marshal(&rb)
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.url, bytes.NewReader(v))
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -81,32 +88,37 @@ func (p *httpPlugin) In(ctx context.Context, key string, opts ...traffic.Option)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
p.log.Errorf("server return non-200 code: %s", resp.Status)
|
||||
return nil
|
||||
}
|
||||
|
||||
res := httpPluginResponse{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
return xtraffic.NewLimiter(int(res.In))
|
||||
}
|
||||
|
||||
func (p *httpPlugin) Out(ctx context.Context, key string, opts ...traffic.Option) traffic.Limiter {
|
||||
func (p *httpPlugin) Out(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
if p.client == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var options traffic.Options
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
rb := httpPluginRequest{
|
||||
Service: options.Service,
|
||||
Scope: options.Scope,
|
||||
Network: options.Network,
|
||||
Addr: options.Addr,
|
||||
Client: options.Client,
|
||||
@@ -114,11 +126,13 @@ func (p *httpPlugin) Out(ctx context.Context, key string, opts ...traffic.Option
|
||||
}
|
||||
v, err := json.Marshal(&rb)
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.url, bytes.NewReader(v))
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -128,16 +142,19 @@ func (p *httpPlugin) Out(ctx context.Context, key string, opts ...traffic.Option
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
p.log.Errorf("server return non-200 code: %s", resp.Status)
|
||||
return nil
|
||||
}
|
||||
|
||||
res := httpPluginResponse{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
p.log.Error(err)
|
||||
return nil
|
||||
}
|
||||
return xtraffic.NewLimiter(int(res.Out))
|
||||
|
||||
+61
-30
@@ -10,7 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/units"
|
||||
limiter "github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/x/internal/loader"
|
||||
"github.com/patrickmn/go-cache"
|
||||
@@ -93,7 +94,7 @@ type trafficLimiter struct {
|
||||
options options
|
||||
}
|
||||
|
||||
func NewTrafficLimiter(opts ...Option) limiter.TrafficLimiter {
|
||||
func NewTrafficLimiter(opts ...Option) traffic.TrafficLimiter {
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
@@ -120,20 +121,35 @@ func NewTrafficLimiter(opts ...Option) limiter.TrafficLimiter {
|
||||
}
|
||||
|
||||
// In obtains a traffic input limiter based on key.
|
||||
// The key should be client connection address.
|
||||
func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Option) limiter.Limiter {
|
||||
var lims []limiter.Limiter
|
||||
|
||||
// service level limiter
|
||||
if lim, ok := l.inLimits.Get(GlobalLimitKey); ok && lim != nil {
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
// For connection scope, the key should be client connection address.
|
||||
func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
switch options.Scope {
|
||||
case limiter.ScopeService:
|
||||
if lim, ok := l.inLimits.Get(GlobalLimitKey); ok && lim != nil {
|
||||
return lim.(traffic.Limiter)
|
||||
}
|
||||
return nil
|
||||
|
||||
case limiter.ScopeClient:
|
||||
return nil
|
||||
|
||||
case limiter.ScopeConn:
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
|
||||
var lims []traffic.Limiter
|
||||
|
||||
// connection level limiter
|
||||
if lim, ok := l.connInLimits.Get(key); ok {
|
||||
if lim != nil {
|
||||
// cached connection level limiter
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
lims = append(lims, lim.(traffic.Limiter))
|
||||
// reset expiration
|
||||
l.connInLimits.Set(key, lim, defaultExpiration)
|
||||
}
|
||||
@@ -153,7 +169,7 @@ func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Opt
|
||||
if lim, ok := l.inLimits.Get(host); ok {
|
||||
// cached IP limiter
|
||||
if lim != nil {
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
lims = append(lims, lim.(traffic.Limiter))
|
||||
}
|
||||
} else {
|
||||
l.mu.RLock()
|
||||
@@ -171,7 +187,7 @@ func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Opt
|
||||
}
|
||||
}
|
||||
|
||||
var lim limiter.Limiter
|
||||
var lim traffic.Limiter
|
||||
if len(lims) > 0 {
|
||||
lim = newLimiterGroup(lims...)
|
||||
}
|
||||
@@ -184,20 +200,35 @@ func (l *trafficLimiter) In(ctx context.Context, key string, opts ...limiter.Opt
|
||||
}
|
||||
|
||||
// Out obtains a traffic output limiter based on key.
|
||||
// The key should be client connection address.
|
||||
func (l *trafficLimiter) Out(ctx context.Context, key string, opts ...limiter.Option) limiter.Limiter {
|
||||
var lims []limiter.Limiter
|
||||
|
||||
// service level limiter
|
||||
if lim, ok := l.outLimits.Get(GlobalLimitKey); ok && lim != nil {
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
// For connection scope, the key should be client connection address.
|
||||
func (l *trafficLimiter) Out(ctx context.Context, key string, opts ...limiter.Option) traffic.Limiter {
|
||||
var options limiter.Options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
switch options.Scope {
|
||||
case limiter.ScopeService:
|
||||
if lim, ok := l.outLimits.Get(GlobalLimitKey); ok && lim != nil {
|
||||
return lim.(traffic.Limiter)
|
||||
}
|
||||
return nil
|
||||
|
||||
case limiter.ScopeClient:
|
||||
return nil
|
||||
|
||||
case limiter.ScopeConn:
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
|
||||
var lims []traffic.Limiter
|
||||
|
||||
// connection level limiter
|
||||
if lim, ok := l.connOutLimits.Get(key); ok {
|
||||
if lim != nil {
|
||||
// cached connection level limiter
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
lims = append(lims, lim.(traffic.Limiter))
|
||||
// reset expiration
|
||||
l.connOutLimits.Set(key, lim, defaultExpiration)
|
||||
}
|
||||
@@ -217,7 +248,7 @@ func (l *trafficLimiter) Out(ctx context.Context, key string, opts ...limiter.Op
|
||||
if lim, ok := l.outLimits.Get(host); ok {
|
||||
if lim != nil {
|
||||
// cached IP level limiter
|
||||
lims = append(lims, lim.(limiter.Limiter))
|
||||
lims = append(lims, lim.(traffic.Limiter))
|
||||
}
|
||||
} else {
|
||||
l.mu.RLock()
|
||||
@@ -235,7 +266,7 @@ func (l *trafficLimiter) Out(ctx context.Context, key string, opts ...limiter.Op
|
||||
}
|
||||
}
|
||||
|
||||
var lim limiter.Limiter
|
||||
var lim traffic.Limiter
|
||||
if len(lims) > 0 {
|
||||
lim = newLimiterGroup(lims...)
|
||||
}
|
||||
@@ -278,7 +309,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
{
|
||||
value := values[GlobalLimitKey]
|
||||
if v, _ := l.inLimits.Get(GlobalLimitKey); v != nil {
|
||||
lim := v.(limiter.Limiter)
|
||||
lim := v.(traffic.Limiter)
|
||||
if value.in <= 0 {
|
||||
l.inLimits.Delete(GlobalLimitKey)
|
||||
} else {
|
||||
@@ -291,7 +322,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if v, _ := l.outLimits.Get(GlobalLimitKey); v != nil {
|
||||
lim := v.(limiter.Limiter)
|
||||
lim := v.(traffic.Limiter)
|
||||
if value.out <= 0 {
|
||||
l.outLimits.Delete(GlobalLimitKey)
|
||||
} else {
|
||||
@@ -321,7 +352,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
if in != value.in {
|
||||
for _, item := range l.connInLimits.Items() {
|
||||
if v := item.Object; v != nil {
|
||||
v.(limiter.Limiter).Set(in)
|
||||
v.(traffic.Limiter).Set(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,7 +364,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
if out != value.out {
|
||||
for _, item := range l.connOutLimits.Items() {
|
||||
if v := item.Object; v != nil {
|
||||
v.(limiter.Limiter).Set(out)
|
||||
v.(traffic.Limiter).Set(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,7 +392,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if v, _ := l.inLimits.Get(key); v != nil {
|
||||
lim := v.(limiter.Limiter)
|
||||
lim := v.(traffic.Limiter)
|
||||
if value.in <= 0 {
|
||||
l.inLimits.Delete(key)
|
||||
} else {
|
||||
@@ -375,7 +406,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
}
|
||||
|
||||
if v, _ := l.outLimits.Get(key); v != nil {
|
||||
lim := v.(limiter.Limiter)
|
||||
lim := v.(traffic.Limiter)
|
||||
if value.out <= 0 {
|
||||
l.outLimits.Delete(key)
|
||||
} else {
|
||||
@@ -398,7 +429,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
l.inLimits.Delete(k)
|
||||
continue
|
||||
}
|
||||
lim := v.Object.(limiter.Limiter)
|
||||
lim := v.Object.(traffic.Limiter)
|
||||
if lim.Limit() != in {
|
||||
lim.Set(in)
|
||||
}
|
||||
@@ -415,7 +446,7 @@ func (l *trafficLimiter) reload(ctx context.Context) error {
|
||||
l.outLimits.Delete(k)
|
||||
continue
|
||||
}
|
||||
lim := v.Object.(limiter.Limiter)
|
||||
lim := v.Object.(traffic.Limiter)
|
||||
if lim.Limit() != out {
|
||||
lim.Set(out)
|
||||
}
|
||||
|
||||
+186
-235
@@ -7,74 +7,43 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
limiter "github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/metadata"
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
"github.com/go-gost/x/internal/net/udp"
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnsupport = errors.New("unsupported operation")
|
||||
)
|
||||
|
||||
// serverConn is a server side Conn with traffic limiter supported.
|
||||
type serverConn struct {
|
||||
// limitConn is a Conn with traffic limiter supported.
|
||||
type limitConn struct {
|
||||
net.Conn
|
||||
rbuf bytes.Buffer
|
||||
limiter limiter.TrafficLimiter
|
||||
limiterIn limiter.Limiter
|
||||
limiterOut limiter.Limiter
|
||||
expIn int64
|
||||
expOut int64
|
||||
opts []limiter.Option
|
||||
rbuf bytes.Buffer
|
||||
limiter traffic.TrafficLimiter
|
||||
opts []limiter.Option
|
||||
key string
|
||||
}
|
||||
|
||||
func WrapConn(tlimiter limiter.TrafficLimiter, c net.Conn) net.Conn {
|
||||
func WrapConn(c net.Conn, tlimiter traffic.TrafficLimiter, key string, opts ...limiter.Option) net.Conn {
|
||||
if tlimiter == nil {
|
||||
return c
|
||||
}
|
||||
|
||||
return &serverConn{
|
||||
return &limitConn{
|
||||
Conn: c,
|
||||
limiter: tlimiter,
|
||||
opts: []limiter.Option{
|
||||
limiter.NetworkOption(c.LocalAddr().Network()),
|
||||
limiter.SrcOption(c.RemoteAddr().String()),
|
||||
limiter.AddrOption(c.LocalAddr().String()),
|
||||
},
|
||||
opts: opts,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *serverConn) getInLimiter() limiter.Limiter {
|
||||
now := time.Now().UnixNano()
|
||||
// cache the limiter for 60s
|
||||
if c.limiter != nil && time.Duration(now-c.expIn) > 60*time.Second {
|
||||
if lim := c.limiter.In(context.Background(), c.RemoteAddr().String()); lim != nil {
|
||||
c.limiterIn = lim
|
||||
}
|
||||
c.expIn = now
|
||||
}
|
||||
return c.limiterIn
|
||||
}
|
||||
|
||||
func (c *serverConn) getOutLimiter() limiter.Limiter {
|
||||
now := time.Now().UnixNano()
|
||||
// cache the limiter for 60s
|
||||
if c.limiter != nil && time.Duration(now-c.expOut) > 60*time.Second {
|
||||
if lim := c.limiter.Out(context.Background(), c.RemoteAddr().String()); lim != nil {
|
||||
c.limiterOut = lim
|
||||
}
|
||||
c.expOut = now
|
||||
}
|
||||
return c.limiterOut
|
||||
}
|
||||
|
||||
func (c *serverConn) Read(b []byte) (n int, err error) {
|
||||
limiter := c.getInLimiter()
|
||||
if limiter == nil {
|
||||
func (c *limitConn) Read(b []byte) (n int, err error) {
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
@@ -102,9 +71,9 @@ func (c *serverConn) Read(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *serverConn) Write(b []byte) (n int, err error) {
|
||||
limiter := c.getOutLimiter()
|
||||
if limiter == nil {
|
||||
func (c *limitConn) Write(b []byte) (n int, err error) {
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
@@ -121,7 +90,7 @@ func (c *serverConn) Write(b []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) {
|
||||
func (c *limitConn) SyscallConn() (rc syscall.RawConn, err error) {
|
||||
if sc, ok := c.Conn.(syscall.Conn); ok {
|
||||
rc, err = sc.SyscallConn()
|
||||
return
|
||||
@@ -130,7 +99,7 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *serverConn) Metadata() metadata.Metadata {
|
||||
func (c *limitConn) Metadata() metadata.Metadata {
|
||||
if md, ok := c.Conn.(metadata.Metadatable); ok {
|
||||
return md.Metadata()
|
||||
}
|
||||
@@ -139,71 +108,23 @@ func (c *serverConn) Metadata() metadata.Metadata {
|
||||
|
||||
type packetConn struct {
|
||||
net.PacketConn
|
||||
limiter limiter.TrafficLimiter
|
||||
inLimits *cache.Cache
|
||||
outLimits *cache.Cache
|
||||
limiter traffic.TrafficLimiter
|
||||
opts []limiter.Option
|
||||
key string
|
||||
}
|
||||
|
||||
func WrapPacketConn(lim limiter.TrafficLimiter, pc net.PacketConn) net.PacketConn {
|
||||
func WrapPacketConn(pc net.PacketConn, lim traffic.TrafficLimiter, key string, opts ...limiter.Option) net.PacketConn {
|
||||
if lim == nil {
|
||||
return pc
|
||||
}
|
||||
return &packetConn{
|
||||
PacketConn: pc,
|
||||
limiter: lim,
|
||||
inLimits: cache.New(time.Second, 10*time.Second),
|
||||
outLimits: cache.New(time.Second, 10*time.Second),
|
||||
opts: opts,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *packetConn) getInLimiter(addr net.Addr) limiter.Limiter {
|
||||
if c.limiter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lim, ok := func() (lim limiter.Limiter, ok bool) {
|
||||
v, ok := c.inLimits.Get(addr.String())
|
||||
if ok {
|
||||
if v != nil {
|
||||
lim = v.(limiter.Limiter)
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
if ok {
|
||||
return lim
|
||||
}
|
||||
|
||||
lim = c.limiter.In(context.Background(), addr.String())
|
||||
c.inLimits.Set(addr.String(), lim, 0)
|
||||
|
||||
return lim
|
||||
}
|
||||
|
||||
func (c *packetConn) getOutLimiter(addr net.Addr) limiter.Limiter {
|
||||
if c.limiter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lim, ok := func() (lim limiter.Limiter, ok bool) {
|
||||
v, ok := c.outLimits.Get(addr.String())
|
||||
if ok {
|
||||
if v != nil {
|
||||
lim = v.(limiter.Limiter)
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
if ok {
|
||||
return lim
|
||||
}
|
||||
|
||||
lim = c.limiter.Out(context.Background(), addr.String())
|
||||
c.outLimits.Set(addr.String(), lim, 0)
|
||||
|
||||
return lim
|
||||
}
|
||||
|
||||
func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
for {
|
||||
n, addr, err = c.PacketConn.ReadFrom(p)
|
||||
@@ -211,8 +132,8 @@ func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
limiter := c.getInLimiter(addr)
|
||||
if limiter == nil {
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -227,7 +148,8 @@ func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
|
||||
func (c *packetConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getOutLimiter(addr); limiter != nil &&
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter != nil && limiter.Limit() > 0 &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
@@ -245,68 +167,20 @@ func (c *packetConn) Metadata() metadata.Metadata {
|
||||
|
||||
type udpConn struct {
|
||||
net.PacketConn
|
||||
limiter limiter.TrafficLimiter
|
||||
inLimits *cache.Cache
|
||||
outLimits *cache.Cache
|
||||
limiter traffic.TrafficLimiter
|
||||
opts []limiter.Option
|
||||
key string
|
||||
}
|
||||
|
||||
func WrapUDPConn(limiter limiter.TrafficLimiter, pc net.PacketConn) udp.Conn {
|
||||
func WrapUDPConn(pc net.PacketConn, limiter traffic.TrafficLimiter, key string, opts ...limiter.Option) udp.Conn {
|
||||
return &udpConn{
|
||||
PacketConn: pc,
|
||||
limiter: limiter,
|
||||
inLimits: cache.New(time.Second, 10*time.Second),
|
||||
outLimits: cache.New(time.Second, 10*time.Second),
|
||||
opts: opts,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpConn) getInLimiter(addr net.Addr) limiter.Limiter {
|
||||
if c.limiter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lim, ok := func() (lim limiter.Limiter, ok bool) {
|
||||
v, ok := c.inLimits.Get(addr.String())
|
||||
if ok {
|
||||
if v != nil {
|
||||
lim = v.(limiter.Limiter)
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
if ok {
|
||||
return lim
|
||||
}
|
||||
|
||||
lim = c.limiter.In(context.Background(), addr.String())
|
||||
c.inLimits.Set(addr.String(), lim, 0)
|
||||
|
||||
return lim
|
||||
}
|
||||
|
||||
func (c *udpConn) getOutLimiter(addr net.Addr) limiter.Limiter {
|
||||
if c.limiter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lim, ok := func() (lim limiter.Limiter, ok bool) {
|
||||
v, ok := c.outLimits.Get(addr.String())
|
||||
if ok {
|
||||
if v != nil {
|
||||
lim = v.(limiter.Limiter)
|
||||
}
|
||||
}
|
||||
return
|
||||
}()
|
||||
if ok {
|
||||
return lim
|
||||
}
|
||||
|
||||
lim = c.limiter.Out(context.Background(), addr.String())
|
||||
c.outLimits.Set(addr.String(), lim, 0)
|
||||
|
||||
return lim
|
||||
}
|
||||
|
||||
func (c *udpConn) RemoteAddr() net.Addr {
|
||||
if nc, ok := c.PacketConn.(xnet.RemoteAddr); ok {
|
||||
return nc.RemoteAddr()
|
||||
@@ -329,12 +203,34 @@ func (c *udpConn) SetWriteBuffer(n int) error {
|
||||
}
|
||||
|
||||
func (c *udpConn) Read(b []byte) (n int, err error) {
|
||||
if nc, ok := c.PacketConn.(io.Reader); ok {
|
||||
n, err = nc.Read(b)
|
||||
nc, ok := c.PacketConn.(io.Reader)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
n, err = nc.Read(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.limiter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
@@ -344,105 +240,160 @@ func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.limiter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getInLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), n) < n {
|
||||
if limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
nc, ok := c.PacketConn.(udp.ReadUDP)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
n, addr, err = nc.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.limiter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
|
||||
nc, ok := c.PacketConn.(udp.ReadUDP)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
n, oobn, flags, addr, err = nc.ReadMsgUDP(b, oob)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.limiter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
limiter := c.limiter.In(context.Background(), c.key, c.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
if nc, ok := c.PacketConn.(udp.ReadUDP); ok {
|
||||
for {
|
||||
n, addr, err = nc.ReadFromUDP(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getInLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
|
||||
if nc, ok := c.PacketConn.(udp.ReadUDP); ok {
|
||||
for {
|
||||
n, oobn, flags, addr, err = nc.ReadMsgUDP(b, oob)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getInLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), n) < n {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) Write(b []byte) (n int, err error) {
|
||||
if nc, ok := c.PacketConn.(io.Writer); ok {
|
||||
n, err = nc.Write(b)
|
||||
func (c *udpConn) Write(p []byte) (n int, err error) {
|
||||
nc, ok := c.PacketConn.(io.Writer)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
err = errUnsupport
|
||||
|
||||
if c.limiter != nil {
|
||||
// discard when exceed the limit size.
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter != nil && limiter.Limit() > 0 &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
n, err = nc.Write(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getOutLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
if c.limiter != nil {
|
||||
// discard when exceed the limit size.
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter != nil && limiter.Limit() > 0 &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
n, err = c.PacketConn.WriteTo(p, addr)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) {
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getOutLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), len(b)) < len(b) {
|
||||
n = len(b)
|
||||
func (c *udpConn) WriteToUDP(p []byte, addr *net.UDPAddr) (n int, err error) {
|
||||
nc, ok := c.PacketConn.(udp.WriteUDP)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
if nc, ok := c.PacketConn.(udp.WriteUDP); ok {
|
||||
n, err = nc.WriteToUDP(b, addr)
|
||||
return
|
||||
if c.limiter != nil {
|
||||
// discard when exceed the limit size.
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter != nil && limiter.Limit() > 0 &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errUnsupport
|
||||
|
||||
n, err = nc.WriteToUDP(p, addr)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *udpConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
|
||||
// discard when exceed the limit size.
|
||||
if limiter := c.getOutLimiter(addr); limiter != nil &&
|
||||
limiter.Wait(context.Background(), len(b)) < len(b) {
|
||||
n = len(b)
|
||||
func (c *udpConn) WriteMsgUDP(p, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
|
||||
nc, ok := c.PacketConn.(udp.WriteUDP)
|
||||
if !ok {
|
||||
err = errUnsupport
|
||||
return
|
||||
}
|
||||
|
||||
if nc, ok := c.PacketConn.(udp.WriteUDP); ok {
|
||||
n, oobn, err = nc.WriteMsgUDP(b, oob, addr)
|
||||
return
|
||||
if c.limiter != nil {
|
||||
// discard when exceed the limit size.
|
||||
limiter := c.limiter.Out(context.Background(), c.key, c.opts...)
|
||||
if limiter != nil && limiter.Limit() > 0 &&
|
||||
limiter.Wait(context.Background(), len(p)) < len(p) {
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errUnsupport
|
||||
|
||||
n, oobn, err = nc.WriteMsgUDP(p, oob, addr)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,21 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
limiter "github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
)
|
||||
|
||||
// readWriter is an io.ReadWriter with traffic limiter supported.
|
||||
type readWriter struct {
|
||||
io.ReadWriter
|
||||
rbuf bytes.Buffer
|
||||
limiter limiter.TrafficLimiter
|
||||
limiterIn limiter.Limiter
|
||||
limiterOut limiter.Limiter
|
||||
expIn int64
|
||||
expOut int64
|
||||
opts []limiter.Option
|
||||
key string
|
||||
rbuf bytes.Buffer
|
||||
limiter traffic.TrafficLimiter
|
||||
opts []limiter.Option
|
||||
key string
|
||||
}
|
||||
|
||||
func WrapReadWriter(limiter limiter.TrafficLimiter, rw io.ReadWriter, opts ...limiter.Option) io.ReadWriter {
|
||||
func WrapReadWriter(limiter traffic.TrafficLimiter, rw io.ReadWriter, key string, opts ...limiter.Option) io.ReadWriter {
|
||||
if limiter == nil {
|
||||
return rw
|
||||
}
|
||||
@@ -31,36 +27,13 @@ func WrapReadWriter(limiter limiter.TrafficLimiter, rw io.ReadWriter, opts ...li
|
||||
ReadWriter: rw,
|
||||
limiter: limiter,
|
||||
opts: opts,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *readWriter) getInLimiter() limiter.Limiter {
|
||||
now := time.Now().UnixNano()
|
||||
// cache the limiter for 60s
|
||||
if p.limiter != nil && time.Duration(now-p.expIn) > 60*time.Second {
|
||||
if lim := p.limiter.In(context.Background(), p.key, p.opts...); lim != nil {
|
||||
p.limiterIn = lim
|
||||
}
|
||||
p.expIn = now
|
||||
}
|
||||
return p.limiterIn
|
||||
}
|
||||
|
||||
func (p *readWriter) getOutLimiter() limiter.Limiter {
|
||||
now := time.Now().UnixNano()
|
||||
// cache the limiter for 60s
|
||||
if p.limiter != nil && time.Duration(now-p.expOut) > 60*time.Second {
|
||||
if lim := p.limiter.Out(context.Background(), p.key, p.opts...); lim != nil {
|
||||
p.limiterOut = lim
|
||||
}
|
||||
p.expOut = now
|
||||
}
|
||||
return p.limiterOut
|
||||
}
|
||||
|
||||
func (p *readWriter) Read(b []byte) (n int, err error) {
|
||||
limiter := p.getInLimiter()
|
||||
if limiter == nil {
|
||||
limiter := p.limiter.In(context.Background(), p.key, p.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return p.ReadWriter.Read(b)
|
||||
}
|
||||
|
||||
@@ -89,8 +62,8 @@ func (p *readWriter) Read(b []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (p *readWriter) Write(b []byte) (n int, err error) {
|
||||
limiter := p.getOutLimiter()
|
||||
if limiter == nil {
|
||||
limiter := p.limiter.Out(context.Background(), p.key, p.opts...)
|
||||
if limiter == nil || limiter.Limit() <= 0 {
|
||||
return p.ReadWriter.Write(b)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,25 @@ package wrapper
|
||||
import (
|
||||
"net"
|
||||
|
||||
limiter "github.com/go-gost/core/limiter/traffic"
|
||||
"github.com/go-gost/core/limiter"
|
||||
"github.com/go-gost/core/limiter/traffic"
|
||||
)
|
||||
|
||||
type listener struct {
|
||||
net.Listener
|
||||
limiter limiter.TrafficLimiter
|
||||
limiter traffic.TrafficLimiter
|
||||
service string
|
||||
}
|
||||
|
||||
func WrapListener(limiter limiter.TrafficLimiter, ln net.Listener) net.Listener {
|
||||
func WrapListener(service string, ln net.Listener, limiter traffic.TrafficLimiter) net.Listener {
|
||||
if limiter == nil {
|
||||
return ln
|
||||
}
|
||||
|
||||
return &listener{
|
||||
limiter: limiter,
|
||||
Listener: ln,
|
||||
limiter: limiter,
|
||||
service: service,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +31,9 @@ func (ln *listener) Accept() (net.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return WrapConn(ln.limiter, c), nil
|
||||
return WrapConn(c, ln.limiter, "",
|
||||
limiter.ScopeOption(limiter.ScopeService),
|
||||
limiter.ServiceOption(ln.service),
|
||||
limiter.NetworkOption(ln.Addr().Network()),
|
||||
), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user