"github.com/anacrolix/sync"
"github.com/anacrolix/utp"
"github.com/dustin/go-humanize"
+ "golang.org/x/time/rate"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/dht"
// Our BitTorrent protocol extension bytes, sent in our BT handshakes.
extensionBytes peerExtensionBytes
// The net.Addr.String part that should be common to all active listeners.
- listenAddr string
+ listenAddr string
+ uploadLimit *rate.Limiter
// Set of addresses that have our client ID. This intentionally will
// include ourselves if we end up trying to connect to our own address
dopplegangerAddrs: make(map[string]struct{}),
torrents: make(map[metainfo.Hash]*Torrent),
}
+ if cfg.UploadRateLimiter == nil {
+ cl.uploadLimit = rate.NewLimiter(rate.Inf, 0)
+ } else {
+ cl.uploadLimit = cfg.UploadRateLimiter
+ }
missinggo.CopyExact(&cl.extensionBytes, defaultExtensionBytes)
cl.event.L = &cl.mu
storageImpl := cfg.DefaultStorage
// We want to upload to the peer.
c.Unchoke()
for r := range c.PeerRequests {
+ res := cl.uploadLimit.ReserveN(time.Now(), int(r.Length))
+ delay := res.Delay()
+ if delay > 0 {
+ res.Cancel()
+ go func() {
+ time.Sleep(delay)
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+ cl.upload(t, c)
+ }()
+ return
+ }
err := cl.sendChunk(t, c, r)
if err != nil {
i := int(r.Index)
"github.com/bradfitz/iter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "golang.org/x/time/rate"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/dht"
})
}
+func TestClientTransferRateLimited(t *testing.T) {
+ started := time.Now()
+ testClientTransfer(t, testClientTransferParams{
+ // We are uploading 13 bytes (the length of the greeting torrent). The
+ // chunks are 2 bytes in length. Then the smallest burst we can run
+ // with is 2. Time taken is (13-burst)/rate.
+ SeederUploadRateLimiter: rate.NewLimiter(11, 2),
+ })
+ require.True(t, time.Since(started) > time.Second)
+}
+
func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
return storage.NewResourcePieces(fc.AsResourceProvider())
}
}
type testClientTransferParams struct {
- Responsive bool
- Readahead int64
- SetReadahead bool
- ExportClientStatus bool
- LeecherStorage func(string) storage.ClientImpl
- SeederStorage func(string) storage.ClientImpl
+ Responsive bool
+ Readahead int64
+ SetReadahead bool
+ ExportClientStatus bool
+ LeecherStorage func(string) storage.ClientImpl
+ SeederStorage func(string) storage.ClientImpl
+ SeederUploadRateLimiter *rate.Limiter
}
// Creates a seeder and a leecher, and ensures the data transfers when a read
// Create seeder and a Torrent.
cfg := TestingConfig
cfg.Seed = true
+ cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
// cfg.ListenAddr = "localhost:4000"
if ps.SeederStorage != nil {
cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
leecherDataDir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(leecherDataDir)
- cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
+ if ps.LeecherStorage == nil {
+ cfg.DataDir = leecherDataDir
+ } else {
+ cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
+ }
// cfg.ListenAddr = "localhost:4001"
leecher, err := NewClient(&cfg)
require.NoError(t, err)
package torrent
import (
+ "golang.org/x/time/rate"
+
"github.com/anacrolix/torrent/dht"
"github.com/anacrolix/torrent/iplist"
"github.com/anacrolix/torrent/storage"
// not altruistic, we'll upload slightly more than we download from each
// peer.
Seed bool `long:"seed"`
+ // If non-zero, is the maximum bytes per second of data pieces we'll
+ // upload to peers.
+ UploadRateLimiter *rate.Limiter
+
// User-provided Client peer ID. If not present, one is generated automatically.
PeerID string
// For the bittorrent protocol.