Implement incoming rate limit (fixes #613)

This commit is contained in:
Jakob Borg 2014-09-08 17:25:55 +02:00
parent baf8a63121
commit 6e8272f78f
7 changed files with 58 additions and 17 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,24 @@
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
package main
import (
"io"
"github.com/juju/ratelimit"
)
type limitedReader struct {
r io.Reader
bucket *ratelimit.Bucket
}
func (r *limitedReader) Read(buf []byte) (int, error) {
n, err := r.r.Read(buf)
if r.bucket != nil {
r.bucket.Wait(int64(n))
}
return n, err
}

View File

@ -82,15 +82,16 @@ func init() {
} }
var ( var (
cfg config.Configuration cfg config.Configuration
myID protocol.NodeID myID protocol.NodeID
confDir string confDir string
logFlags int = log.Ltime logFlags int = log.Ltime
rateBucket *ratelimit.Bucket writeRateLimit *ratelimit.Bucket
stop = make(chan int) readRateLimit *ratelimit.Bucket
discoverer *discover.Discoverer stop = make(chan int)
externalPort int discoverer *discover.Discoverer
cert tls.Certificate externalPort int
cert tls.Certificate
) )
const ( const (
@ -381,11 +382,14 @@ func syncthingMain() {
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
} }
// If the write rate should be limited, set up a rate limiter for it. // If the read or write rate should be limited, set up a rate limiter for it.
// This will be used on connections created in the connect and listen routines. // This will be used on connections created in the connect and listen routines.
if cfg.Options.MaxSendKbps > 0 { if cfg.Options.MaxSendKbps > 0 {
rateBucket = ratelimit.NewBucketWithRate(float64(1000*cfg.Options.MaxSendKbps), int64(5*1000*cfg.Options.MaxSendKbps)) writeRateLimit = ratelimit.NewBucketWithRate(float64(1000*cfg.Options.MaxSendKbps), int64(5*1000*cfg.Options.MaxSendKbps))
}
if cfg.Options.MaxRecvKbps > 0 {
readRateLimit = ratelimit.NewBucketWithRate(float64(1000*cfg.Options.MaxRecvKbps), int64(5*1000*cfg.Options.MaxRecvKbps))
} }
// If this is the first time the user runs v0.9, archive the old indexes and config. // If this is the first time the user runs v0.9, archive the old indexes and config.
@ -790,15 +794,20 @@ next:
continue next continue next
} }
// If rate limiting is set, we wrap the write side of the // If rate limiting is set, we wrap the connection in a
// connection in a limiter. // limiter.
var wr io.Writer = conn var wr io.Writer = conn
if rateBucket != nil { if writeRateLimit != nil {
wr = &limitedWriter{conn, rateBucket} wr = &limitedWriter{conn, writeRateLimit}
}
var rd io.Reader = conn
if readRateLimit != nil {
rd = &limitedReader{conn, readRateLimit}
} }
name := fmt.Sprintf("%s-%s", conn.LocalAddr(), conn.RemoteAddr()) name := fmt.Sprintf("%s-%s", conn.LocalAddr(), conn.RemoteAddr())
protoConn := protocol.NewConnection(remoteID, conn, wr, m, name, nodeCfg.Compression) protoConn := protocol.NewConnection(remoteID, rd, wr, m, name, nodeCfg.Compression)
l.Infof("Established secure connection to %s at %s", remoteID, name) l.Infof("Established secure connection to %s at %s", remoteID, name)
if debugNet { if debugNet {

View File

@ -119,6 +119,7 @@ type OptionsConfiguration struct {
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"` LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
ParallelRequests int `xml:"parallelRequests" default:"16"` ParallelRequests int `xml:"parallelRequests" default:"16"`
MaxSendKbps int `xml:"maxSendKbps"` MaxSendKbps int `xml:"maxSendKbps"`
MaxRecvKbps int `xml:"maxRecvKbps"`
ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"` ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"`
StartBrowser bool `xml:"startBrowser" default:"true"` StartBrowser bool `xml:"startBrowser" default:"true"`
UPnPEnabled bool `xml:"upnpEnabled" default:"true"` UPnPEnabled bool `xml:"upnpEnabled" default:"true"`

View File

@ -31,6 +31,7 @@ func TestDefaultValues(t *testing.T) {
LocalAnnMCAddr: "[ff32::5222]:21026", LocalAnnMCAddr: "[ff32::5222]:21026",
ParallelRequests: 16, ParallelRequests: 16,
MaxSendKbps: 0, MaxSendKbps: 0,
MaxRecvKbps: 0,
ReconnectIntervalS: 60, ReconnectIntervalS: 60,
StartBrowser: true, StartBrowser: true,
UPnPEnabled: true, UPnPEnabled: true,
@ -121,6 +122,7 @@ func TestOverriddenValues(t *testing.T) {
LocalAnnMCAddr: "quux:3232", LocalAnnMCAddr: "quux:3232",
ParallelRequests: 32, ParallelRequests: 32,
MaxSendKbps: 1234, MaxSendKbps: 1234,
MaxRecvKbps: 2341,
ReconnectIntervalS: 6000, ReconnectIntervalS: 6000,
StartBrowser: false, StartBrowser: false,
UPnPEnabled: false, UPnPEnabled: false,

View File

@ -9,6 +9,7 @@
<localAnnounceMCAddr>quux:3232</localAnnounceMCAddr> <localAnnounceMCAddr>quux:3232</localAnnounceMCAddr>
<parallelRequests>32</parallelRequests> <parallelRequests>32</parallelRequests>
<maxSendKbps>1234</maxSendKbps> <maxSendKbps>1234</maxSendKbps>
<maxRecvKbps>2341</maxRecvKbps>
<reconnectionIntervalS>6000</reconnectionIntervalS> <reconnectionIntervalS>6000</reconnectionIntervalS>
<startBrowser>false</startBrowser> <startBrowser>false</startBrowser>
<upnpEnabled>false</upnpEnabled> <upnpEnabled>false</upnpEnabled>

View File

@ -538,6 +538,10 @@
<label translate for="ListenStr">Sync Protocol Listen Addresses</label> <label translate for="ListenStr">Sync Protocol Listen Addresses</label>
<input id="ListenStr" class="form-control" type="text" ng-model="tmpOptions.ListenStr"> <input id="ListenStr" class="form-control" type="text" ng-model="tmpOptions.ListenStr">
</div> </div>
<div class="form-group">
<label translate for="MaxRecvKbps">Incoming Rate Limit (KiB/s)</label>
<input id="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.MaxRecvKbps">
</div>
<div class="form-group"> <div class="form-group">
<label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label> <label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label>
<input id="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.MaxSendKbps"> <input id="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.MaxSendKbps">