This is a very breaking change.
"github.com/davecgh/go-spew/spew"
"github.com/dustin/go-humanize"
"github.com/google/btree"
- "golang.org/x/time/rate"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/iplist"
event sync.Cond
closed missinggo.Event
- config Config
+ config *ClientConfig
logger *log.Logger
halfOpenLimit int
ipBlockList iplist.Ranger
// Our BitTorrent protocol extension bytes, sent in our BT handshakes.
extensionBytes peerExtensionBytes
- uploadLimit *rate.Limiter
- downloadLimit *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
return int32(binary.BigEndian.Uint32(cl.peerID[16:20]))
}
-func NewClient(cfg *Config) (cl *Client, err error) {
+func NewClient(cfg *ClientConfig) (cl *Client, err error) {
if cfg == nil {
- cfg = &Config{}
+ cfg = NewDefaultClientConfig()
}
- cfg.setDefaults()
-
defer func() {
if err != nil {
cl = nil
}()
cl = &Client{
halfOpenLimit: cfg.HalfOpenConnsPerTorrent,
- config: *cfg,
+ config: cfg,
dopplegangerAddrs: make(map[string]struct{}),
torrents: make(map[metainfo.Hash]*Torrent),
}
}
cl.Close()
}()
- if cfg.UploadRateLimiter == nil {
- cl.uploadLimit = unlimited
- } else {
- cl.uploadLimit = cfg.UploadRateLimiter
- }
- if cfg.DownloadRateLimiter == nil {
- cl.downloadLimit = unlimited
- } else {
- cl.downloadLimit = cfg.DownloadRateLimiter
- }
cl.extensionBytes = defaultPeerExtensionBytes()
cl.event.L = &cl.mu
storageImpl := cfg.DefaultStorage
var allPeerNetworks = []string{"tcp4", "tcp6", "udp4", "udp6"}
-func peerNetworkEnabled(network string, cfg Config) bool {
+func peerNetworkEnabled(network string, cfg *ClientConfig) bool {
c := func(s string) bool {
return strings.Contains(network, s)
}
c.writerCond.L = &cl.mu
c.setRW(connStatsReadWriter{nc, c})
c.r = &rateLimitedReader{
- l: cl.downloadLimit,
+ l: cl.config.DownloadRateLimiter,
r: c.r,
}
return
"github.com/anacrolix/torrent/storage"
)
-func TestingConfig() *Config {
- return &Config{
- ListenHost: LoopbackListenHost,
- NoDHT: true,
- DataDir: tempDir(),
- DisableTrackers: true,
- NoDefaultPortForwarding: true,
- // Debug: true,
- }
+func TestingConfig() *ClientConfig {
+ cfg := NewDefaultClientConfig()
+ cfg.ListenHost = LoopbackListenHost
+ cfg.NoDHT = true
+ cfg.DataDir = tempDir()
+ cfg.DisableTrackers = true
+ cfg.NoDefaultPortForwarding = true
+ return cfg
}
func TestClientDefault(t *testing.T) {
func TestTorrentInitialState(t *testing.T) {
dir, mi := testutil.GreetingTestTorrent()
defer os.RemoveAll(dir)
- cl := &Client{}
+ cl := &Client{
+ config: &ClientConfig{},
+ }
cl.initLogger()
tor := cl.newTorrent(
mi.HashInfoBytes(),
}
func TestReducedDialTimeout(t *testing.T) {
- cfg := &Config{}
- cfg.setDefaults()
+ cfg := NewDefaultClientConfig()
for _, _case := range []struct {
Max time.Duration
HalfOpenLimit int
// Create seeder and a Torrent.
cfg := TestingConfig()
cfg.Seed = true
- cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
+ if ps.SeederUploadRateLimiter != nil {
+ 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 = TestingConfig()
if ps.LeecherStorage == nil {
cfg.DataDir = leecherDataDir
} else {
cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
}
- cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
+ if ps.LeecherDownloadRateLimiter != nil {
+ cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
+ }
cfg.Seed = false
leecher, err := NewClient(cfg)
require.NoError(t, err)
func TestSeedAfterDownloading(t *testing.T) {
greetingTempDir, mi := testutil.GreetingTestTorrent()
defer os.RemoveAll(greetingTempDir)
+
cfg := TestingConfig()
cfg.Seed = true
cfg.DataDir = greetingTempDir
require.NoError(t, err)
assert.True(t, ok)
seederTorrent.VerifyData()
+
+ cfg = TestingConfig()
+ cfg.Seed = true
cfg.DataDir, err = ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(cfg.DataDir)
require.NoError(t, err)
defer leecher.Close()
testutil.ExportStatusWriter(leecher, "l")
+
+ cfg = TestingConfig()
cfg.Seed = false
cfg.DataDir, err = ioutil.TempDir("", "")
require.NoError(t, err)
defer ss.Close()
var tts []*Torrent
ih := testutil.GreetingMetaInfo().HashInfoBytes()
+ cfg := TestingConfig()
+ cfg.DisableAcceptRateLimiting = true
+ cfg.dropDuplicatePeerIds = true
for i := range iter.N(3) {
- cl, err := NewClient(TestingConfig())
+ cl, err := NewClient(cfg)
require.NoError(t, err)
defer cl.Close()
tt, _ := cl.AddTorrentInfoHash(ih)
"github.com/anacrolix/torrent/storage"
)
-var DefaultHTTPClient = &http.Client{
- Timeout: time.Second * 15,
- Transport: &http.Transport{
- Dial: (&net.Dialer{
- Timeout: 15 * time.Second,
- }).Dial,
- TLSHandshakeTimeout: 15 * time.Second,
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- },
-}
var DefaultHTTPUserAgent = "Go-Torrent/1.0"
-// Override Client defaults.
-type Config struct {
+// Probably not safe to modify this after it's given to a Client.
+type ClientConfig struct {
// Store torrent file data in this directory unless .DefaultStorage is
// specified.
DataDir string `long:"data-dir" description:"directory to store downloaded torrent data"`
PublicIp6 net.IP
}
-func (cfg *Config) SetListenAddr(addr string) *Config {
+func (cfg *ClientConfig) SetListenAddr(addr string) *ClientConfig {
host, port, err := missinggo.ParseHostPort(addr)
expect.Nil(err)
cfg.ListenHost = func(string) string { return host }
return cfg
}
-func (cfg *Config) setDefaults() {
- if cfg.HTTP == nil {
- cfg.HTTP = DefaultHTTPClient
- if cfg.ProxyURL != "" {
- cfg.setProxyURL()
- }
- }
- if cfg.HTTPUserAgent == "" {
- cfg.HTTPUserAgent = DefaultHTTPUserAgent
- }
- if cfg.ExtendedHandshakeClientVersion == "" {
- cfg.ExtendedHandshakeClientVersion = "go.torrent dev 20150624"
- }
- if cfg.Bep20 == "" {
- cfg.Bep20 = "-GT0001-"
- }
- if cfg.NominalDialTimeout == 0 {
- cfg.NominalDialTimeout = 30 * time.Second
- }
- if cfg.MinDialTimeout == 0 {
- cfg.MinDialTimeout = 5 * time.Second
- }
- if cfg.EstablishedConnsPerTorrent == 0 {
- cfg.EstablishedConnsPerTorrent = 50
- }
- if cfg.HalfOpenConnsPerTorrent == 0 {
- cfg.HalfOpenConnsPerTorrent = (cfg.EstablishedConnsPerTorrent + 1) / 2
- }
- if cfg.TorrentPeersHighWater == 0 {
- // Memory and freshness are the concern here.
- cfg.TorrentPeersHighWater = 500
- }
- if cfg.TorrentPeersLowWater == 0 {
- cfg.TorrentPeersLowWater = 2 * cfg.HalfOpenConnsPerTorrent
- }
- if cfg.HandshakesTimeout == 0 {
- cfg.HandshakesTimeout = 20 * time.Second
- }
- if cfg.DhtStartingNodes == nil {
- cfg.DhtStartingNodes = dht.GlobalBootstrapAddrs
- }
- if cfg.ListenHost == nil {
- cfg.ListenHost = func(string) string { return "" }
+func NewDefaultClientConfig() *ClientConfig {
+ return &ClientConfig{
+ HTTP: &http.Client{
+ Timeout: time.Second * 15,
+ Transport: &http.Transport{
+ Dial: (&net.Dialer{
+ Timeout: 15 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 15 * time.Second,
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+ }},
+ HTTPUserAgent: DefaultHTTPUserAgent,
+ ExtendedHandshakeClientVersion: "go.torrent dev 20150624",
+ Bep20: "-GT0001-",
+ NominalDialTimeout: 30 * time.Second,
+ MinDialTimeout: 5 * time.Second,
+ EstablishedConnsPerTorrent: 50,
+ HalfOpenConnsPerTorrent: 25,
+ TorrentPeersHighWater: 500,
+ TorrentPeersLowWater: 50,
+ HandshakesTimeout: 20 * time.Second,
+ DhtStartingNodes: dht.GlobalBootstrapAddrs,
+ ListenHost: func(string) string { return "" },
+ UploadRateLimiter: unlimited,
+ DownloadRateLimiter: unlimited,
}
}
-func (cfg *Config) setProxyURL() {
+func (cfg *ClientConfig) setProxyURL() {
fixedURL, err := url.Parse(cfg.ProxyURL)
if err != nil {
return
return false
}
for r := range c.PeerRequests {
- res := c.t.cl.uploadLimit.ReserveN(time.Now(), int(r.Length))
+ res := c.t.cl.config.UploadRateLimiter.ReserveN(time.Now(), int(r.Length))
if !res.OK() {
panic(fmt.Sprintf("upload rate limiter burst size < %d", r.Length))
}
// Have that would potentially alter it.
func TestSendBitfieldThenHave(t *testing.T) {
r, w := io.Pipe()
- var cl Client
+ cl := Client{
+ config: &ClientConfig{DownloadRateLimiter: unlimited},
+ }
cl.initLogger()
c := cl.newConnection(nil, false)
c.setTorrent(cl.newTorrent(metainfo.Hash{}, nil))
func BenchmarkConnectionMainReadLoop(b *testing.B) {
cl := &Client{
- downloadLimit: unlimited,
+ config: &ClientConfig{
+ DownloadRateLimiter: unlimited,
+ },
}
ts := &torrentStorage{}
t := &Torrent{