func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory { return func(dataDir string) storage.Client { fc, err := filecache.NewCache(dataDir) if err != nil { panic(err) } if ps.SetCapacity { fc.SetCapacity(ps.Capacity) } return ps.Wrapper(fc) } }
func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.Client) { fileCacheDir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(fileCacheDir) fileCache, err := filecache.NewCache(fileCacheDir) require.NoError(t, err) greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingDataTempDir) filePieceStore := csf(fileCache) greetingData, err := filePieceStore.OpenTorrent(&greetingMetainfo.Info) require.NoError(t, err) writeTorrentData(greetingData, &greetingMetainfo.Info, []byte(testutil.GreetingFileContents)) // require.Equal(t, len(testutil.GreetingFileContents), written) // require.NoError(t, err) for i := 0; i < greetingMetainfo.Info.NumPieces(); i++ { p := greetingMetainfo.Info.Piece(i) if alreadyCompleted { err := greetingData.Piece(p).MarkComplete() assert.NoError(t, err) } } cfg := TestingConfig // TODO: Disable network option? cfg.DisableTCP = true cfg.DisableUTP = true cfg.DefaultStorage = filePieceStore cl, err := NewClient(&cfg) require.NoError(t, err) defer cl.Close() tt, err := cl.AddTorrent(greetingMetainfo) require.NoError(t, err) psrs := tt.PieceStateRuns() assert.Len(t, psrs, 1) assert.EqualValues(t, 3, psrs[0].Length) assert.Equal(t, alreadyCompleted, psrs[0].Complete) if alreadyCompleted { r := tt.NewReader() b, err := ioutil.ReadAll(r) assert.NoError(t, err) assert.EqualValues(t, testutil.GreetingFileContents, b) } }
func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) require.NoError(t, err) defer seeder.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(seeder, "s") } seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(leecherDataDir) fc, err := filecache.NewCache(leecherDataDir) require.NoError(t, err) if ps.SetLeecherStorageCapacity { fc.SetCapacity(ps.LeecherStorageCapacity) } cfg.DefaultStorage = storage.NewFileStorePieces(fc.AsFileStore()) cfg.DataDir = leecherDataDir leecher, _ := NewClient(&cfg) defer leecher.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(leecher, "l") } leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) require.NoError(t, err) assert.True(t, new) psc := leecherGreeting.SubscribePieceStateChanges() defer psc.Close() leecherGreeting.DownloadAll() if ps.Cancel { leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces()) } leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) completes := make(map[int]bool, 3) values: for { // started := time.Now() select { case _v := <-psc.Values: // log.Print(time.Since(started)) v := _v.(PieceStateChange) completes[v.Index] = v.Complete case <-time.After(100 * time.Millisecond): break values } } if ps.Cancel { assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes) } else { assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes) } }
func testClientTransfer(t *testing.T, ps testClientTransferParams) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true if ps.SeederStorage != nil { cfg.DefaultStorage = ps.SeederStorage(greetingTempDir) } else { cfg.DataDir = greetingTempDir } seeder, err := NewClient(&cfg) require.NoError(t, err) defer seeder.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(seeder, "s") } _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) require.NoError(t, err) assert.True(t, new) leecherDataDir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(leecherDataDir) fc, err := filecache.NewCache(leecherDataDir) require.NoError(t, err) if ps.SetLeecherStorageCapacity { fc.SetCapacity(ps.LeecherStorageCapacity) } cfg.DefaultStorage = ps.LeecherFileCachePieceStorageFactory(fc) leecher, err := NewClient(&cfg) require.NoError(t, err) defer leecher.Close() if ps.ExportClientStatus { testutil.ExportStatusWriter(leecher, "l") } leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 ret.Storage = storage.NewFile(leecherDataDir) return }()) require.NoError(t, err) assert.True(t, new) leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) r := leecherGreeting.NewReader() defer r.Close() if ps.Responsive { r.SetResponsive() } if ps.SetReadahead { r.SetReadahead(ps.Readahead) } for range iter.N(2) { pos, err := r.Seek(0, os.SEEK_SET) assert.NoError(t, err) assert.EqualValues(t, 0, pos) _greeting, err := ioutil.ReadAll(r) assert.NoError(t, err) assert.EqualValues(t, testutil.GreetingFileContents, _greeting) } }
func TestClientTransfer(t *testing.T) { greetingTempDir, mi := testutil.GreetingTestTorrent() defer os.RemoveAll(greetingTempDir) cfg := TestingConfig cfg.Seed = true cfg.DataDir = greetingTempDir seeder, err := NewClient(&cfg) if err != nil { t.Fatal(err) } defer seeder.Close() seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) leecherDataDir, err := ioutil.TempDir("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(leecherDataDir) // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) { // return blob.TorrentData(info, leecherDataDir), nil // } // blobStore := blob.NewStore(leecherDataDir) // cfg.TorrentDataOpener = func(info *metainfo.Info) Data { // return blobStore.OpenTorrent(info) // } cfg.TorrentDataOpener = func() TorrentDataOpener { fc, err := filecache.NewCache(leecherDataDir) require.NoError(t, err) store := pieceStore.New(fileCacheDataBackend.New(fc)) return func(mi *metainfo.Info) Data { return store.OpenTorrentData(mi) } }() leecher, _ := NewClient(&cfg) defer leecher.Close() leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 return }()) // TODO: The piece state publishing is kinda jammed in here until I have a // more thorough test. go func() { s := leecherGreeting.pieceStateChanges.Subscribe() defer s.Close() for i := range s.Values { log.Print(i) } log.Print("finished") }() leecherGreeting.AddPeers([]Peer{ Peer{ IP: missinggo.AddrIP(seeder.ListenAddr()), Port: missinggo.AddrPort(seeder.ListenAddr()), }, }) r := leecherGreeting.NewReader() defer r.Close() _greeting, err := ioutil.ReadAll(r) if err != nil { t.Fatalf("%q %s", string(_greeting), err) } greeting := string(_greeting) if greeting != testutil.GreetingFileContents { t.Fatal(":(") } }
func main() { log.SetFlags(log.Flags() | log.Lshortfile) args := struct { Capacity tagflag.Bytes `short:"c"` Addr string }{ Capacity: -1, Addr: "localhost:2076", } tagflag.Parse(&args) root, err := os.Getwd() if err != nil { log.Fatal(err) } log.Printf("cache root at %q", root) c, err = filecache.NewCache(root) if err != nil { log.Fatalf("error creating cache: %s", err) } if args.Capacity < 0 { log.Printf("no capacity set, no evictions will occur") } else { c.SetCapacity(args.Capacity.Int64()) log.Printf("setting capacity to %s bytes", humanize.Comma(args.Capacity.Int64())) } http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { p := r.URL.Path[1:] switch r.Method { case "DELETE": log.Printf("%s %s", r.Method, r.RequestURI) handleDelete(w, p) return case "PUT", "PATCH", "POST": contentRange := r.Header.Get("Content-Range") firstByte := parseContentRangeFirstByte(contentRange) log.Printf("%s (%d-) %s", r.Method, firstByte, r.RequestURI) handleNewData(w, p, firstByte, r.Body) return } log.Printf("%s %s %s", r.Method, r.Header.Get("Range"), r.RequestURI) f, err := c.OpenFile(p, os.O_RDONLY) if os.IsNotExist(err) { http.NotFound(w, r) return } if err != nil { log.Printf("couldn't open requested file: %s", err) http.Error(w, "couldn't open file", http.StatusInternalServerError) return } defer func() { go f.Close() }() info, _ := f.Stat() w.Header().Set("Content-Range", fmt.Sprintf("*/%d", info.Size())) http.ServeContent(w, r, p, info.ModTime(), f) }) http.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { info := c.Info() fmt.Fprintf(w, "Capacity: %d\n", info.Capacity) fmt.Fprintf(w, "Current Size: %d\n", info.Filled) fmt.Fprintf(w, "Item Count: %d\n", info.NumItems) }) http.HandleFunc("/lru", func(w http.ResponseWriter, r *http.Request) { c.WalkItems(func(item filecache.ItemInfo) { fmt.Fprintf(w, "%s\t%d\t%s\n", item.Accessed, item.Size, item.Path) }) }) cert, err := missinggo.NewSelfSignedCertificate() if err != nil { log.Fatal(err) } srv := http.Server{ Addr: args.Addr, TLSConfig: &tls.Config{ Certificates: []tls.Certificate{cert}, }, } log.Fatal(srv.ListenAndServeTLS("", "")) }