return nil
}
-func (cl *Client) acceptConnections(l net.Listener) {
+func (cl *Client) acceptConnections(l Listener) {
for {
conn, err := l.Accept()
torrent.Add("client listener accepts", 1)
}
}
cl := t.cl
- cl.AddDHTNodes(spec.DhtNodes)
+ cl.AddDhtNodes(spec.DhtNodes)
cl.lock()
defer cl.unlock()
useTorrentSources(spec.Sources, t)
}
func (cl *Client) AddMagnet(uri string) (T *Torrent, err error) {
- spec, err := TorrentSpecFromMagnetURI(uri)
+ spec, err := TorrentSpecFromMagnetUri(uri)
if err != nil {
return
}
return cl.dhtServers
}
-func (cl *Client) AddDHTNodes(nodes []string) {
+func (cl *Client) AddDhtNodes(nodes []string) {
for _, n := range nodes {
hmp := missinggo.SplitHostMaybePort(n)
ip := net.ParseIP(hmp.Host)
}
}
-func (cl *Client) findListener(f func(net.Listener) bool) (ret net.Listener) {
+func (cl *Client) findListener(f func(Listener) bool) (ret Listener) {
cl.eachListener(func(l Listener) bool {
ret = l
return !f(l)
func (cl *Client) findListenerIp(f func(net.IP) bool) net.IP {
l := cl.findListener(
- func(l net.Listener) bool {
+ func(l Listener) bool {
return f(addrIpOrNil(l.Addr()))
},
)
func argSpec(arg string) (ts *torrent.TorrentSpec, err error) {
if strings.HasPrefix(arg, "magnet:") {
- return torrent.TorrentSpecFromMagnetURI(arg)
+ return torrent.TorrentSpecFromMagnetUri(arg)
}
mi, err := metainfo.LoadFromFile(arg)
if err != nil {
// OnQuery hook func
DHTOnQuery func(query *krpc.Msg, source net.Addr) (propagate bool)
- DefaultRequestStrategy RequestStrategyMaker
+ DefaultRequestStrategy requestStrategyMaker
Extensions PeerExtensionBits
return u.String()
}
-// ParseMagnetURI parses Magnet-formatted URIs into a Magnet instance
-func ParseMagnetURI(uri string) (m Magnet, err error) {
+// ParseMagnetUri parses Magnet-formatted URIs into a Magnet instance
+func ParseMagnetUri(uri string) (m Magnet, err error) {
u, err := url.Parse(uri)
if err != nil {
err = fmt.Errorf("error parsing uri: %w", err)
// Converting from our Magnet type to URL string.
func TestMagnetString(t *testing.T) {
- m, err := ParseMagnetURI(exampleMagnet.String())
+ m, err := ParseMagnetUri(exampleMagnet.String())
require.NoError(t, err)
assert.EqualValues(t, exampleMagnet, m)
}
// parsing the legit Magnet URI with btih-formatted xt should not return errors
uri = "magnet:?xt=urn:btih:ZOCMZQIPFFW7OLLMIC5HUB6BPCSDEOQU"
- _, err = ParseMagnetURI(uri)
+ _, err = ParseMagnetUri(uri)
if err != nil {
t.Errorf("Attempting parsing the proper Magnet btih URI:\"%v\" failed with err: %v", uri, err)
}
// Checking if the magnet instance struct is built correctly from parsing
- m, err = ParseMagnetURI(exampleMagnetURI)
+ m, err = ParseMagnetUri(exampleMagnetURI)
assert.EqualValues(t, exampleMagnet, m)
assert.NoError(t, err)
// empty string URI case
- _, err = ParseMagnetURI("")
+ _, err = ParseMagnetUri("")
if err == nil {
t.Errorf("Parsing empty string as URI should have returned an error but didn't")
}
// only BTIH (BitTorrent info hash)-formatted magnet links are currently supported
// must return error correctly when encountering other URN formats
uri = "magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C"
- _, err = ParseMagnetURI(uri)
+ _, err = ParseMagnetUri(uri)
if err == nil {
t.Errorf("Magnet URI with non-BTIH URNs (like \"%v\") are not supported and should return an error", uri)
}
// resilience to the broken hash
uri = "magnet:?xt=urn:btih:this hash is really broken"
- _, err = ParseMagnetURI(uri)
+ _, err = ParseMagnetUri(uri)
if err == nil {
t.Errorf("Failed to detect broken Magnet URI: %v", uri)
}
return
}
+// Tells the Client to refetch the completion status from storage, updating priority etc. if
+// necessary. Might be useful if you know the state of the piece data has changed externally.
func (p *Piece) UpdateCompletion() {
p.t.cl.lock()
defer p.t.cl.unlock()
"github.com/anacrolix/missinggo"
)
+// Accesses Torrent data via a Client. Reads block until the data is available. Seeks and readahead
+// also drive Client behaviour.
type Reader interface {
io.Reader
io.Seeker
io.Closer
missinggo.ReadContexter
+ // Configure the number of bytes ahead of a read that should also be prioritized in preparation
+ // for further reads.
SetReadahead(int64)
+ // Don't wait for pieces to complete and be verified. Read calls return as soon as they can when
+ // the underlying chunks become available.
SetResponsive()
}
begin, end pieceIndex
}
-// Accesses Torrent data via a Client. Reads block until the data is
-// available. Seeks and readahead also drive Client behaviour.
type reader struct {
t *Torrent
responsive bool
- // Adjust the read/seek window to handle Readers locked to File extents
- // and the like.
+ // Adjust the read/seek window to handle Readers locked to File extents and the like.
offset, length int64
- // Ensure operations that change the position are exclusive, like Read()
- // and Seek().
+ // Ensure operations that change the position are exclusive, like Read() and Seek().
opMu sync.Mutex
- // Required when modifying pos and readahead, or reading them without
- // opMu.
+ // Required when modifying pos and readahead, or reading them without opMu.
mu sync.Locker
pos int64
readahead int64
- // The cached piece range this reader wants downloaded. The zero value
- // corresponds to nothing. We cache this so that changes can be detected,
- // and bubbled up to the Torrent only as required.
+ // The cached piece range this reader wants downloaded. The zero value corresponds to nothing.
+ // We cache this so that changes can be detected, and bubbled up to the Torrent only as
+ // required.
pieces pieceRange
}
-var _ io.ReadCloser = &reader{}
+var _ io.ReadCloser = (*reader)(nil)
-// Don't wait for pieces to complete and be verified. Read calls return as
-// soon as they can when the underlying chunks become available.
func (r *reader) SetResponsive() {
r.responsive = true
r.t.cl.event.Broadcast()
r.t.cl.event.Broadcast()
}
-// Configure the number of bytes ahead of a read that should also be
-// prioritized in preparation for further reads.
func (r *reader) SetReadahead(readahead int64) {
r.mu.Lock()
r.readahead = readahead
}
func (r *reader) waitReadable(off int64) {
- // We may have been sent back here because we were told we could read but
- // it failed.
+ // We may have been sent back here because we were told we could read but it failed.
r.t.cl.event.Wait()
}
-// Calculates the pieces this reader wants downloaded, ignoring the cached
-// value at r.pieces.
+// Calculates the pieces this reader wants downloaded, ignoring the cached value at r.pieces.
func (r *reader) piecesUncached() (ret pieceRange) {
ra := r.readahead
if ra < 1 {
r.t.cl.unlock()
}()
}
- // Hmmm, if a Read gets stuck, this means you can't change position for
- // other purposes. That seems reasonable, but unusual.
+ // Hmmm, if a Read gets stuck, this means you can't change position for other purposes. That
+ // seems reasonable, but unusual.
r.opMu.Lock()
defer r.opMu.Unlock()
n, err = r.readOnceAt(b, r.pos, &ctxErr)
return
}
-// Wait until some data should be available to read. Tickles the client if it
-// isn't. Returns how much should be readable without blocking.
+// Wait until some data should be available to read. Tickles the client if it isn't. Returns how
+// much should be readable without blocking.
func (r *reader) waitAvailable(pos, wanted int64, ctxErr *error, wait bool) (avail int64, err error) {
r.t.cl.lock()
defer r.t.cl.unlock()
}
}
+// Hodor
func (r *reader) Close() error {
r.t.cl.lock()
defer r.t.cl.unlock()
requestStrategyDefaults
}
-func newRequestStrategyMaker(rs requestStrategy) RequestStrategyMaker {
+func newRequestStrategyMaker(rs requestStrategy) requestStrategyMaker {
return func(requestStrategyCallbacks, sync.Locker) requestStrategy {
return rs
}
}
-func RequestStrategyFastest() RequestStrategyMaker {
+func RequestStrategyFastest() requestStrategyMaker {
return newRequestStrategyMaker(requestStrategyFastest{})
}
-func RequestStrategyFuzzing() RequestStrategyMaker {
+func RequestStrategyFuzzing() requestStrategyMaker {
return newRequestStrategyMaker(requestStrategyFuzzing{})
}
timeoutLocker sync.Locker
}
-type RequestStrategyMaker func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy
+// Generates a request strategy instance for a given torrent. callbacks are probably specific to the torrent.
+type requestStrategyMaker func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy
-func RequestStrategyDuplicateRequestTimeout(duplicateRequestTimeout time.Duration) RequestStrategyMaker {
+func RequestStrategyDuplicateRequestTimeout(duplicateRequestTimeout time.Duration) requestStrategyMaker {
return func(callbacks requestStrategyCallbacks, clientLocker sync.Locker) requestStrategy {
return requestStrategyDuplicateRequestTimeout{
duplicateRequestTimeout: duplicateRequestTimeout,
)
type Listener interface {
- net.Listener
+ // Accept waits for and returns the next connection to the listener.
+ Accept() (net.Conn, error)
+
+ // Addr returns the listener's network address.
+ Addr() net.Addr
}
type socket interface {
Listener
Dialer
+ Close() error
}
func listen(n network, addr string, f firewallCallback) (socket, error) {
DisallowDataDownload bool
}
-func TorrentSpecFromMagnetURI(uri string) (spec *TorrentSpec, err error) {
- m, err := metainfo.ParseMagnetURI(uri)
+func TorrentSpecFromMagnetUri(uri string) (spec *TorrentSpec, err error) {
+ m, err := metainfo.ParseMagnetUri(uri)
if err != nil {
return
}
})
}
+// Enables uploading data, if it was disabled.
func (t *Torrent) AllowDataUpload() {
t.cl.lock()
defer t.cl.unlock()
}
}
+// Disables uploading data, if it was enabled.
func (t *Torrent) DisallowDataUpload() {
t.cl.lock()
defer t.cl.unlock()
}
}
+// Sets a handler that is called if there's an error writing a chunk to local storage. By default,
+// or if nil, a critical message is logged, and data download is disabled.
func (t *Torrent) SetOnWriteChunkError(f func(error)) {
t.cl.lock()
defer t.cl.unlock()
break
}
for _, uri := range uris {
- m, err := metainfo.ParseMagnetURI(uri)
+ m, err := metainfo.ParseMagnetUri(uri)
if err != nil {
log.Printf("error parsing %q in file %q: %s", uri, fullName, err)
continue