From 6e8272f78f84ac5268f2a5a797763ad4586a8a4e Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Mon, 8 Sep 2014 17:25:55 +0200 Subject: [PATCH] Implement incoming rate limit (fixes #613) --- auto/gui.files.go | 2 +- cmd/syncthing/limitedreader.go | 24 +++++++++++++++++ cmd/syncthing/main.go | 41 ++++++++++++++++++----------- config/config.go | 1 + config/config_test.go | 2 ++ config/testdata/overridenvalues.xml | 1 + gui/index.html | 4 +++ 7 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 cmd/syncthing/limitedreader.go diff --git a/auto/gui.files.go b/auto/gui.files.go index 4365856a6..85701ab79 100644 --- a/auto/gui.files.go +++ b/auto/gui.files.go @@ -92,7 +92,7 @@ func Assets() map[string][]byte { bs, _ = ioutil.ReadAll(gr) assets["img/logo-text-64.png"] = bs - bs, _ = base64.StdEncoding.DecodeString("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") + bs, _ = base64.StdEncoding.DecodeString("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") gr, _ = gzip.NewReader(bytes.NewBuffer(bs)) bs, _ = ioutil.ReadAll(gr) assets["index.html"] = bs diff --git a/cmd/syncthing/limitedreader.go b/cmd/syncthing/limitedreader.go new file mode 100644 index 000000000..a625440b6 --- /dev/null +++ b/cmd/syncthing/limitedreader.go @@ -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 +} diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go index ccb720ebe..1bd77e84e 100644 --- a/cmd/syncthing/main.go +++ b/cmd/syncthing/main.go @@ -82,15 +82,16 @@ func init() { } var ( - cfg config.Configuration - myID protocol.NodeID - confDir string - logFlags int = log.Ltime - rateBucket *ratelimit.Bucket - stop = make(chan int) - discoverer *discover.Discoverer - externalPort int - cert tls.Certificate + cfg config.Configuration + myID protocol.NodeID + confDir string + logFlags int = log.Ltime + writeRateLimit *ratelimit.Bucket + readRateLimit *ratelimit.Bucket + stop = make(chan int) + discoverer *discover.Discoverer + externalPort int + cert tls.Certificate ) const ( @@ -381,11 +382,14 @@ func syncthingMain() { 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. 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. @@ -790,15 +794,20 @@ next: continue next } - // If rate limiting is set, we wrap the write side of the - // connection in a limiter. + // If rate limiting is set, we wrap the connection in a + // limiter. var wr io.Writer = conn - if rateBucket != nil { - wr = &limitedWriter{conn, rateBucket} + if writeRateLimit != nil { + 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()) - 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) if debugNet { diff --git a/config/config.go b/config/config.go index 4dac5c13f..d0aa0b719 100644 --- a/config/config.go +++ b/config/config.go @@ -119,6 +119,7 @@ type OptionsConfiguration struct { LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"` ParallelRequests int `xml:"parallelRequests" default:"16"` MaxSendKbps int `xml:"maxSendKbps"` + MaxRecvKbps int `xml:"maxRecvKbps"` ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"` StartBrowser bool `xml:"startBrowser" default:"true"` UPnPEnabled bool `xml:"upnpEnabled" default:"true"` diff --git a/config/config_test.go b/config/config_test.go index 6a6f89564..fabc13243 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -31,6 +31,7 @@ func TestDefaultValues(t *testing.T) { LocalAnnMCAddr: "[ff32::5222]:21026", ParallelRequests: 16, MaxSendKbps: 0, + MaxRecvKbps: 0, ReconnectIntervalS: 60, StartBrowser: true, UPnPEnabled: true, @@ -121,6 +122,7 @@ func TestOverriddenValues(t *testing.T) { LocalAnnMCAddr: "quux:3232", ParallelRequests: 32, MaxSendKbps: 1234, + MaxRecvKbps: 2341, ReconnectIntervalS: 6000, StartBrowser: false, UPnPEnabled: false, diff --git a/config/testdata/overridenvalues.xml b/config/testdata/overridenvalues.xml index 034a662b5..967100bee 100755 --- a/config/testdata/overridenvalues.xml +++ b/config/testdata/overridenvalues.xml @@ -9,6 +9,7 @@ quux:3232 32 1234 + 2341 6000 false false diff --git a/gui/index.html b/gui/index.html index b9e634c45..a5a72dbcc 100644 --- a/gui/index.html +++ b/gui/index.html @@ -538,6 +538,10 @@ +
+ + +