torrent/ut-holepunching_test.go

136 lines
3.5 KiB
Go

package torrent
import (
"os"
"sync"
"testing"
"testing/iotest"
"github.com/anacrolix/log"
"github.com/anacrolix/torrent/internal/testutil"
qt "github.com/frankban/quicktest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Check that after completing leeching, a leecher transitions to a seeding
// correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
func TestHolepunchConnect(t *testing.T) {
greetingTempDir, mi := testutil.GreetingTestTorrent()
defer os.RemoveAll(greetingTempDir)
cfg := TestingConfig(t)
cfg.Seed = true
cfg.MaxAllocPeerRequestDataPerConn = 4
cfg.DataDir = greetingTempDir
cfg.DisablePEX = true
cfg.Debug = true
cfg.AcceptPeerConnections = false
//cfg.DisableUTP = true
seeder, err := NewClient(cfg)
require.NoError(t, err)
defer seeder.Close()
defer testutil.ExportStatusWriter(seeder, "s", t)()
seederTorrent, ok, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
require.NoError(t, err)
assert.True(t, ok)
seederTorrent.VerifyData()
cfg = TestingConfig(t)
cfg.Seed = true
cfg.DataDir = t.TempDir()
cfg.AlwaysWantConns = true
// This way the leecher leecher will still try to use this peer as a relay, but won't be told
// about the seeder via PEX.
//cfg.DisablePEX = true
//cfg.Debug = true
leecher, err := NewClient(cfg)
require.NoError(t, err)
defer leecher.Close()
defer testutil.ExportStatusWriter(leecher, "l", t)()
cfg = TestingConfig(t)
cfg.Seed = false
cfg.DataDir = t.TempDir()
cfg.MaxAllocPeerRequestDataPerConn = 4
cfg.Debug = true
cfg.NominalDialTimeout = time.Second
//cfg.DisableUTP = true
leecherLeecher, _ := NewClient(cfg)
require.NoError(t, err)
defer leecherLeecher.Close()
defer testutil.ExportStatusWriter(leecherLeecher, "ll", t)()
leecherGreeting, ok, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
ret = TorrentSpecFromMetaInfo(mi)
ret.ChunkSize = 2
return
}())
_ = leecherGreeting
require.NoError(t, err)
assert.True(t, ok)
llg, ok, err := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
ret = TorrentSpecFromMetaInfo(mi)
ret.ChunkSize = 3
return
}())
require.NoError(t, err)
assert.True(t, ok)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
r := llg.NewReader()
defer r.Close()
qt.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), qt.IsNil)
}()
go seederTorrent.AddClientPeer(leecher)
waitForConns(seederTorrent)
go llg.AddClientPeer(leecher)
waitForConns(llg)
time.Sleep(time.Second)
llg.cl.lock()
targetAddr := seeder.ListenAddrs()[0]
log.Printf("trying to initiate to %v", targetAddr)
initiateConn(outgoingConnOpts{
peerInfo: PeerInfo{
Addr: targetAddr,
},
t: llg,
requireRendezvous: true,
skipHolepunchRendezvous: false,
HeaderObfuscationPolicy: llg.cl.config.HeaderObfuscationPolicy,
}, true)
llg.cl.unlock()
wg.Wait()
llClientStats := leecherLeecher.Stats()
c.Check(llClientStats.NumPeersDialableOnlyAfterHolepunch, qt.Not(qt.Equals), 0)
c.Check(
llClientStats.NumPeersDialableOnlyAfterHolepunch,
qt.Equals,
llClientStats.NumPeersUndialableWithoutHolepunchDialedAfterHolepunchConnect,
)
}
func waitForConns(t *Torrent) {
t.cl.lock()
defer t.cl.unlock()
for {
for range t.conns {
return
}
t.cl.event.Wait()
}
}
func TestDialTcpNotAccepting(t *testing.T) {
l, err := net.Listen("tcp", "localhost:0")
c := qt.New(t)
c.Check(err, qt.IsNil)
defer l.Close()
_, err = net.Dial("tcp", l.Addr().String())
c.Assert(err, qt.IsNotNil)
}