12 "golang.org/x/time/rate"
14 "github.com/frankban/quicktest"
15 qt "github.com/frankban/quicktest"
16 "github.com/stretchr/testify/require"
18 "github.com/anacrolix/torrent/metainfo"
19 pp "github.com/anacrolix/torrent/peer_protocol"
20 "github.com/anacrolix/torrent/storage"
23 // Ensure that no race exists between sending a bitfield, and a subsequent
24 // Have that would potentially alter it.
25 func TestSendBitfieldThenHave(t *testing.T) {
27 cl.init(TestingConfig(t))
29 c := cl.newConnection(nil, newConnectionOpts{network: "io.Pipe"})
30 c.setTorrent(cl.newTorrent(metainfo.Hash{}, nil))
31 if err := c.t.setInfo(&metainfo.Info{Pieces: make([]byte, metainfo.HashSize*3)}); err != nil {
37 c.startMessageWriter()
39 c.t._completedPieces.Add(1)
40 c.postBitfield( /*[]bool{false, true, false}*/ )
46 n, err := io.ReadFull(r, b)
48 // This will cause connection.writer to terminate.
51 require.NoError(t, err)
52 require.EqualValues(t, 15, n)
53 // Here we see that the bitfield doesn't have piece 2 set, as that should
54 // arrive in the following Have message.
55 require.EqualValues(t, "\x00\x00\x00\x02\x05@\x00\x00\x00\x05\x04\x00\x00\x00\x02", string(b))
58 type torrentStorage struct {
62 func (me *torrentStorage) Close() error { return nil }
64 func (me *torrentStorage) Piece(mp metainfo.Piece) storage.PieceImpl {
68 func (me *torrentStorage) Completion() storage.Completion {
69 return storage.Completion{}
72 func (me *torrentStorage) MarkComplete() error {
76 func (me *torrentStorage) MarkNotComplete() error {
80 func (me *torrentStorage) ReadAt([]byte, int64) (int, error) {
81 panic("shouldn't be called")
84 func (me *torrentStorage) WriteAt(b []byte, _ int64) (int, error) {
85 if len(b) != defaultChunkSize {
92 func BenchmarkConnectionMainReadLoop(b *testing.B) {
95 cl.init(&ClientConfig{
96 DownloadRateLimiter: unlimited,
99 ts := &torrentStorage{}
100 t := cl.newTorrent(metainfo.Hash{}, nil)
101 t.initialPieceCheckDisabled = true
102 require.NoError(b, t.setInfo(&metainfo.Info{
103 Pieces: make([]byte, 20),
105 PieceLength: 1 << 20,
107 t.storage = &storage.Torrent{TorrentImpl: storage.TorrentImpl{Piece: ts.Piece, Close: ts.Close}}
109 t._pendingPieces.Add(0)
111 cn := cl.newConnection(r, newConnectionOpts{
113 remoteAddr: r.RemoteAddr(),
114 network: r.RemoteAddr().Network(),
115 connString: regularNetConnPeerConnConnString(r),
118 mrlErrChan := make(chan error)
121 Piece: make([]byte, defaultChunkSize),
125 err := cn.mainReadLoop()
131 wb := msg.MustMarshalBinary()
132 b.SetBytes(int64(len(msg.Piece)))
135 for i := 0; i < b.N; i += 1 {
137 // The chunk must be written to storage everytime, to ensure the
138 // writeSem is unlocked.
139 t.pendAllChunkSpecs(0)
140 cn.validReceiveChunks = map[RequestIndex]int{
141 t.requestIndexFromRequest(newRequestFromMessage(&msg)): 1,
144 n, err := w.Write(wb)
145 require.NoError(b, err)
146 require.EqualValues(b, len(wb), n)
149 if err := w.Close(); err != nil {
153 mrlErr := <-mrlErrChan
154 if mrlErr != nil && !errors.Is(mrlErr, io.EOF) {
157 c.Assert(cn._stats.ChunksReadUseful.Int64(), quicktest.Equals, int64(b.N))
160 func TestConnPexPeerFlags(t *testing.T) {
162 tcpAddr = &net.TCPAddr{IP: net.IPv6loopback, Port: 4848}
163 udpAddr = &net.UDPAddr{IP: net.IPv6loopback, Port: 4848}
165 testcases := []struct {
169 {&PeerConn{Peer: Peer{outgoing: false, PeerPrefersEncryption: false}}, 0},
170 {&PeerConn{Peer: Peer{outgoing: false, PeerPrefersEncryption: true}}, pp.PexPrefersEncryption},
171 {&PeerConn{Peer: Peer{outgoing: true, PeerPrefersEncryption: false}}, pp.PexOutgoingConn},
172 {&PeerConn{Peer: Peer{outgoing: true, PeerPrefersEncryption: true}}, pp.PexOutgoingConn | pp.PexPrefersEncryption},
173 {&PeerConn{Peer: Peer{RemoteAddr: udpAddr, Network: udpAddr.Network()}}, pp.PexSupportsUtp},
174 {&PeerConn{Peer: Peer{RemoteAddr: udpAddr, Network: udpAddr.Network(), outgoing: true}}, pp.PexOutgoingConn | pp.PexSupportsUtp},
175 {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr, Network: tcpAddr.Network(), outgoing: true}}, pp.PexOutgoingConn},
176 {&PeerConn{Peer: Peer{RemoteAddr: tcpAddr, Network: tcpAddr.Network()}}, 0},
178 for i, tc := range testcases {
179 f := tc.conn.pexPeerFlags()
180 require.EqualValues(t, tc.f, f, i)
184 func TestConnPexEvent(t *testing.T) {
186 udpAddr = &net.UDPAddr{IP: net.IPv6loopback, Port: 4848}
187 tcpAddr = &net.TCPAddr{IP: net.IPv6loopback, Port: 4848}
188 dialTcpAddr = &net.TCPAddr{IP: net.IPv6loopback, Port: 4747}
189 dialUdpAddr = &net.UDPAddr{IP: net.IPv6loopback, Port: 4747}
191 testcases := []struct {
198 &PeerConn{Peer: Peer{RemoteAddr: udpAddr, Network: udpAddr.Network()}},
199 pexEvent{pexAdd, udpAddr, pp.PexSupportsUtp, nil},
203 &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, Network: tcpAddr.Network(), outgoing: true}, PeerListenPort: dialTcpAddr.Port},
204 pexEvent{pexDrop, tcpAddr, pp.PexOutgoingConn, nil},
208 &PeerConn{Peer: Peer{RemoteAddr: tcpAddr, Network: tcpAddr.Network()}, PeerListenPort: dialTcpAddr.Port},
209 pexEvent{pexAdd, dialTcpAddr, 0, nil},
213 &PeerConn{Peer: Peer{RemoteAddr: udpAddr, Network: udpAddr.Network()}, PeerListenPort: dialUdpAddr.Port},
214 pexEvent{pexDrop, dialUdpAddr, pp.PexSupportsUtp, nil},
217 for i, tc := range testcases {
218 e := tc.c.pexEvent(tc.t)
219 require.EqualValues(t, tc.e, e, i)
223 func TestHaveAllThenBitfield(t *testing.T) {
225 cl := newTestingClient(t)
226 tt := cl.newTorrentForTesting()
227 // cl.newConnection()
231 pc.initRequestState()
233 tt.conns[&pc] = struct{}{}
234 c.Assert(pc.onPeerSentHaveAll(), qt.IsNil)
235 c.Check(pc.t.connsWithAllPieces, qt.DeepEquals, map[*Peer]struct{}{&pc.Peer: {}})
236 pc.peerSentBitfield([]bool{false, false, true, false, true, true, false, false})
237 c.Check(pc.peerMinPieces, qt.Equals, 6)
238 c.Check(pc.t.connsWithAllPieces, qt.HasLen, 0)
239 c.Assert(pc.t.setInfo(&metainfo.Info{
241 Pieces: make([]byte, pieceHash.Size()*7),
244 c.Check(tt.numPieces(), qt.Equals, 7)
245 c.Check(tt.pieceAvailabilityRuns(), qt.DeepEquals, []pieceAvailabilityRun{
246 // The last element of the bitfield is irrelevant, as the Torrent actually only has 7
248 {2, 0}, {1, 1}, {1, 0}, {2, 1}, {1, 0},
252 func TestApplyRequestStateWriteBufferConstraints(t *testing.T) {
254 c.Check(interestedMsgLen, qt.Equals, 5)
255 c.Check(requestMsgLen, qt.Equals, 17)
256 c.Check(maxLocalToRemoteRequests >= 8, qt.IsTrue)
257 c.Logf("max local to remote requests: %v", maxLocalToRemoteRequests)
260 func peerConnForPreferredNetworkDirection(
261 localPeerId, remotePeerId int,
262 outgoing, utp, ipv6 bool,
265 pc.outgoing = outgoing
270 pc.RemoteAddr = &net.TCPAddr{IP: net.ParseIP(fmt.Sprintf("::420"))}
272 pc.RemoteAddr = &net.TCPAddr{IP: net.IPv4(1, 2, 3, 4)}
274 binary.BigEndian.PutUint64(pc.PeerID[:], uint64(remotePeerId))
276 binary.BigEndian.PutUint64(cl.peerID[:], uint64(localPeerId))
277 pc.t = &Torrent{cl: &cl}
281 func TestPreferredNetworkDirection(t *testing.T) {
282 pc := peerConnForPreferredNetworkDirection
285 // Prefer outgoing to lower peer ID
288 pc(1, 2, true, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)),
292 pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, true, false, false)),
296 pc(2, 1, false, false, false).hasPreferredNetworkOver(pc(2, 1, true, false, false)),
302 pc(1, 2, false, true, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)),
307 pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, true)),
312 pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)),
317 func TestReceiveLargeRequest(t *testing.T) {
319 cl := newTestingClient(t)
320 pc := cl.newConnection(nil, newConnectionOpts{network: "test"})
321 tor := cl.newTorrentForTesting()
322 tor.info = &metainfo.Info{PieceLength: 3 << 20}
324 tor._completedPieces.Add(0)
325 pc.PeerExtensionBytes.SetBit(pp.ExtensionBitFast, true)
327 pc.initMessageWriter()
329 req.Length = defaultChunkSize
330 c.Assert(pc.fastEnabled(), qt.IsTrue)
331 c.Check(pc.onReadRequest(req, false), qt.IsNil)
332 c.Check(pc.peerRequests, qt.HasLen, 1)
334 c.Check(pc.onReadRequest(req, false), qt.IsNil)
335 c.Check(pc.peerRequests, qt.HasLen, 2)
336 pc.peerRequests = nil
337 pc.t.cl.config.UploadRateLimiter = rate.NewLimiter(1, defaultChunkSize)
338 req.Length = defaultChunkSize
339 c.Check(pc.onReadRequest(req, false), qt.IsNil)
340 c.Check(pc.peerRequests, qt.HasLen, 1)
342 c.Check(pc.onReadRequest(req, false), qt.IsNil)
343 c.Check(pc.messageWriter.writeBuffer.Len(), qt.Equals, 17)
346 func TestChunkOverflowsPiece(t *testing.T) {
348 check := func(begin, length, limit pp.Integer, expected bool) {
349 c.Check(chunkOverflowsPiece(ChunkSpec{begin, length}, limit), qt.Equals, expected)
352 check(2, pp.IntegerMax, 1, true)
353 check(2, pp.IntegerMax, 3, true)
354 check(2, pp.IntegerMax, pp.IntegerMax, true)
355 check(2, pp.IntegerMax-2, pp.IntegerMax, false)