11 "github.com/anacrolix/log"
12 pp "github.com/anacrolix/torrent/peer_protocol"
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
17 "github.com/anacrolix/torrent"
18 "github.com/anacrolix/torrent/internal/testutil"
19 "github.com/anacrolix/torrent/metainfo"
20 "github.com/anacrolix/torrent/storage"
23 func justOneNetwork(cc *torrent.ClientConfig) {
28 func TestReceiveChunkStorageFailureSeederFastExtensionDisabled(t *testing.T) {
29 testReceiveChunkStorageFailure(t, false)
32 func TestReceiveChunkStorageFailure(t *testing.T) {
33 testReceiveChunkStorageFailure(t, true)
36 func testReceiveChunkStorageFailure(t *testing.T, seederFast bool) {
37 seederDataDir, metainfo := testutil.GreetingTestTorrent()
38 defer os.RemoveAll(seederDataDir)
39 seederClientConfig := torrent.TestingConfig(t)
40 seederClientConfig.Debug = true
41 justOneNetwork(seederClientConfig)
42 seederClientStorage := storage.NewMMap(seederDataDir)
43 defer seederClientStorage.Close()
44 seederClientConfig.DefaultStorage = seederClientStorage
45 seederClientConfig.Seed = true
46 seederClientConfig.Debug = true
47 seederClientConfig.Extensions.SetBit(pp.ExtensionBitFast, seederFast)
48 seederClient, err := torrent.NewClient(seederClientConfig)
49 require.NoError(t, err)
50 defer seederClient.Close()
51 defer testutil.ExportStatusWriter(seederClient, "s", t)()
52 leecherClientConfig := torrent.TestingConfig(t)
53 leecherClientConfig.Debug = true
54 // Don't require fast extension, whether the seeder will provide it or not (so we can test mixed
56 leecherClientConfig.MinPeerExtensions.SetBit(pp.ExtensionBitFast, false)
57 justOneNetwork(leecherClientConfig)
58 leecherClient, err := torrent.NewClient(leecherClientConfig)
59 require.NoError(t, err)
60 defer leecherClient.Close()
61 defer testutil.ExportStatusWriter(leecherClient, "l", t)()
62 info, err := metainfo.UnmarshalInfo()
63 require.NoError(t, err)
64 leecherStorage := diskFullStorage{
65 pieces: make([]pieceState, info.NumPieces()),
66 data: make([]byte, info.TotalLength()),
68 defer leecherStorage.Close()
69 leecherTorrent, new, err := leecherClient.AddTorrentSpec(&torrent.TorrentSpec{
70 InfoHash: metainfo.HashInfoBytes(),
71 Storage: &leecherStorage,
73 leecherStorage.t = leecherTorrent
74 require.NoError(t, err)
76 seederTorrent, err := seederClient.AddTorrent(metainfo)
77 require.NoError(t, err)
78 // Tell the seeder to find the leecher. Is it guaranteed seeders will always try to do this?
79 seederTorrent.AddClientPeer(leecherClient)
80 <-leecherTorrent.GotInfo()
81 r := leecherTorrent.Files()[0].NewReader()
83 // We can't use assertReadAllGreeting here, because the default storage write error handler
84 // disables data downloads, which now causes Readers to error when they're blocked.
86 assertReadAllGreeting(t, leecherTorrent.NewReader())
89 // We don't seem to need to seek, but that's probably just because the storage failure is
90 // happening on the first read.
91 r.Seek(0, io.SeekStart)
92 if err := iotest.TestReader(r, []byte(testutil.GreetingFileContents)); err != nil {
93 t.Logf("got error while reading: %v", err)
100 // TODO: Check that PeerConns fastEnabled matches seederFast?
104 type pieceState struct {
108 type diskFullStorage struct {
111 defaultHandledWriteChunkError bool
118 func (me *diskFullStorage) Piece(p metainfo.Piece) storage.PieceImpl {
125 func (me *diskFullStorage) Close() error {
129 func (d *diskFullStorage) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (storage.TorrentImpl, error) {
130 return storage.TorrentImpl{Piece: d.Piece, Close: d.Close}, nil
133 type pieceImpl struct {
138 func (me pieceImpl) state() *pieceState {
139 return &me.diskFullStorage.pieces[me.mip.Index()]
142 func (me pieceImpl) ReadAt(p []byte, off int64) (n int, err error) {
143 off += me.mip.Offset()
144 return copy(p, me.data[off:]), nil
147 func (me pieceImpl) WriteAt(p []byte, off int64) (int, error) {
148 off += me.mip.Offset()
149 if !me.defaultHandledWriteChunkError {
151 me.t.SetOnWriteChunkError(func(err error) {
152 log.Printf("got write chunk error to custom handler: %v", err)
154 me.diskNotFull = true
156 me.t.AllowDataDownload()
158 me.t.AllowDataDownload()
160 me.defaultHandledWriteChunkError = true
165 return copy(me.data[off:], p), nil
167 return copy(me.data[off:], p[:1]), errors.New("disk full")
170 func (me pieceImpl) MarkComplete() error {
171 me.state().complete = true
175 func (me pieceImpl) MarkNotComplete() error {
176 panic("implement me")
179 func (me pieceImpl) Completion() storage.Completion {
180 return storage.Completion{
181 Complete: me.state().complete,