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(localPeerId, remotePeerId int, outgoing, utp, ipv6 bool) *PeerConn {
262 pc.outgoing = outgoing
267 pc.RemoteAddr = &net.TCPAddr{IP: net.ParseIP(fmt.Sprintf("::420"))}
269 pc.RemoteAddr = &net.TCPAddr{IP: net.IPv4(1, 2, 3, 4)}
271 binary.BigEndian.PutUint64(pc.PeerID[:], uint64(remotePeerId))
273 binary.BigEndian.PutUint64(cl.peerID[:], uint64(localPeerId))
274 pc.t = &Torrent{cl: &cl}
278 func TestPreferredNetworkDirection(t *testing.T) {
279 pc := peerConnForPreferredNetworkDirection
281 // Prefer outgoing to higher peer ID
282 c.Assert(pc(1, 2, true, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)), qt.IsTrue)
283 c.Assert(pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, true, false, false)), qt.IsFalse)
284 c.Assert(pc(2, 1, false, false, false).hasPreferredNetworkOver(pc(2, 1, true, false, false)), qt.IsTrue)
286 c.Assert(pc(1, 2, false, true, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)), qt.IsFalse)
288 c.Assert(pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, true)), qt.IsFalse)
290 c.Assert(pc(1, 2, false, false, false).hasPreferredNetworkOver(pc(1, 2, false, false, false)), qt.IsFalse)
293 func TestReceiveLargeRequest(t *testing.T) {
295 cl := newTestingClient(t)
296 pc := cl.newConnection(nil, newConnectionOpts{network: "test"})
297 tor := cl.newTorrentForTesting()
298 tor.info = &metainfo.Info{PieceLength: 3 << 20}
300 tor._completedPieces.Add(0)
301 pc.PeerExtensionBytes.SetBit(pp.ExtensionBitFast, true)
303 pc.initMessageWriter()
305 req.Length = defaultChunkSize
306 c.Assert(pc.fastEnabled(), qt.IsTrue)
307 c.Check(pc.onReadRequest(req, false), qt.IsNil)
308 c.Check(pc.peerRequests, qt.HasLen, 1)
310 c.Check(pc.onReadRequest(req, false), qt.IsNil)
311 c.Check(pc.peerRequests, qt.HasLen, 2)
312 pc.peerRequests = nil
313 pc.t.cl.config.UploadRateLimiter = rate.NewLimiter(1, defaultChunkSize)
314 req.Length = defaultChunkSize
315 c.Check(pc.onReadRequest(req, false), qt.IsNil)
316 c.Check(pc.peerRequests, qt.HasLen, 1)
318 c.Check(pc.onReadRequest(req, false), qt.IsNil)
319 c.Check(pc.messageWriter.writeBuffer.Len(), qt.Equals, 17)
322 func TestChunkOverflowsPiece(t *testing.T) {
324 check := func(begin, length, limit pp.Integer, expected bool) {
325 c.Check(chunkOverflowsPiece(ChunkSpec{begin, length}, limit), qt.Equals, expected)
328 check(2, pp.IntegerMax, 1, true)
329 check(2, pp.IntegerMax, 3, true)
330 check(2, pp.IntegerMax, pp.IntegerMax, true)
331 check(2, pp.IntegerMax-2, pp.IntegerMax, false)