mirror of
https://github.com/sot-tech/mochi.git
synced 2026-07-20 23:18:10 -07:00
Separate tracker logic from the http package, step 1
This commit is contained in:
@@ -11,7 +11,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/chihaya/chihaya/config"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
"github.com/chihaya/chihaya/tracker/models"
|
||||
)
|
||||
|
||||
var drivers = make(map[string]Driver)
|
||||
@@ -35,7 +35,7 @@ func Register(name string, driver Driver) {
|
||||
drivers[name] = driver
|
||||
}
|
||||
|
||||
// Open creates a connection specified by a models configuration.
|
||||
// Open creates a connection specified by a configuration.
|
||||
func Open(cfg *config.DriverConfig) (Conn, error) {
|
||||
driver, ok := drivers[cfg.Name]
|
||||
if !ok {
|
||||
|
||||
@@ -9,7 +9,7 @@ package noop
|
||||
import (
|
||||
"github.com/chihaya/chihaya/config"
|
||||
"github.com/chihaya/chihaya/drivers/backend"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
"github.com/chihaya/chihaya/tracker/models"
|
||||
)
|
||||
|
||||
type driver struct{}
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
// Copyright 2014 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package memory
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/chihaya/chihaya/drivers/tracker"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
)
|
||||
|
||||
// Conn implements a connection to a memory-based tracker data store.
|
||||
type Conn struct {
|
||||
*Pool
|
||||
}
|
||||
|
||||
func (c *Conn) FindUser(passkey string) (*models.User, error) {
|
||||
c.usersM.RLock()
|
||||
defer c.usersM.RUnlock()
|
||||
|
||||
user, exists := c.users[passkey]
|
||||
if !exists {
|
||||
return nil, tracker.ErrUserDNE
|
||||
}
|
||||
return &*user, nil
|
||||
}
|
||||
|
||||
func (c *Conn) FindTorrent(infohash string) (*models.Torrent, error) {
|
||||
c.torrentsM.RLock()
|
||||
defer c.torrentsM.RUnlock()
|
||||
|
||||
torrent, exists := c.torrents[infohash]
|
||||
if !exists {
|
||||
return nil, tracker.ErrTorrentDNE
|
||||
}
|
||||
return &*torrent, nil
|
||||
}
|
||||
|
||||
func (c *Conn) FindClient(peerID string) error {
|
||||
c.whitelistM.RLock()
|
||||
defer c.whitelistM.RUnlock()
|
||||
|
||||
_, ok := c.whitelist[peerID]
|
||||
if !ok {
|
||||
return tracker.ErrClientUnapproved
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) IncrementSnatches(infohash string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.Snatches++
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) TouchTorrent(infohash string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.LastAction = time.Now().Unix()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) AddLeecher(infohash string, p *models.Peer) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.Leechers[p.Key()] = *p
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) AddSeeder(infohash string, p *models.Peer) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.Seeders[p.Key()] = *p
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) DeleteLeecher(infohash, peerkey string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
delete(t.Leechers, peerkey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) DeleteSeeder(infohash, peerkey string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
delete(t.Seeders, peerkey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PutLeecher(infohash string, p *models.Peer) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.Leechers[p.Key()] = *p
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PutSeeder(infohash string, p *models.Peer) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
t, ok := c.torrents[infohash]
|
||||
if !ok {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
t.Seeders[p.Key()] = *p
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PutTorrent(t *models.Torrent) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
c.torrents[t.Infohash] = &*t
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) DeleteTorrent(infohash string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
delete(c.torrents, infohash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PurgeInactiveTorrent(infohash string) error {
|
||||
c.torrentsM.Lock()
|
||||
defer c.torrentsM.Unlock()
|
||||
|
||||
torrent, exists := c.torrents[infohash]
|
||||
if !exists {
|
||||
return tracker.ErrTorrentDNE
|
||||
}
|
||||
|
||||
if torrent.PeerCount() == 0 {
|
||||
delete(c.torrents, infohash)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PutUser(u *models.User) error {
|
||||
c.usersM.Lock()
|
||||
defer c.usersM.Unlock()
|
||||
|
||||
c.users[u.Passkey] = &*u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) DeleteUser(passkey string) error {
|
||||
c.usersM.Lock()
|
||||
defer c.usersM.Unlock()
|
||||
|
||||
delete(c.users, passkey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PutClient(peerID string) error {
|
||||
c.whitelistM.Lock()
|
||||
defer c.whitelistM.Unlock()
|
||||
|
||||
c.whitelist[peerID] = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) DeleteClient(peerID string) error {
|
||||
c.whitelistM.Lock()
|
||||
defer c.whitelistM.Unlock()
|
||||
|
||||
delete(c.whitelist, peerID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) PurgeInactivePeers(purgeEmptyTorrents bool, before time.Time) error {
|
||||
unixtime := before.Unix()
|
||||
|
||||
// Build array of map keys to operate on.
|
||||
c.torrentsM.RLock()
|
||||
index := 0
|
||||
keys := make([]string, len(c.torrents))
|
||||
|
||||
for infohash, _ := range c.torrents {
|
||||
keys[index] = infohash
|
||||
index++
|
||||
}
|
||||
|
||||
c.torrentsM.RUnlock()
|
||||
|
||||
// Process keys.
|
||||
for _, infohash := range keys {
|
||||
runtime.Gosched() // Let other goroutines run, since this is low priority.
|
||||
|
||||
c.torrentsM.Lock()
|
||||
torrent := c.torrents[infohash]
|
||||
|
||||
if torrent == nil {
|
||||
continue // Torrent deleted since keys were computed.
|
||||
}
|
||||
|
||||
for key, peer := range torrent.Seeders {
|
||||
if peer.LastAnnounce <= unixtime {
|
||||
delete(torrent.Seeders, key)
|
||||
}
|
||||
}
|
||||
|
||||
for key, peer := range torrent.Leechers {
|
||||
if peer.LastAnnounce <= unixtime {
|
||||
delete(torrent.Leechers, key)
|
||||
}
|
||||
}
|
||||
|
||||
peers := torrent.PeerCount()
|
||||
c.torrentsM.Unlock()
|
||||
|
||||
if purgeEmptyTorrents && peers == 0 {
|
||||
c.PurgeInactiveTorrent(infohash)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2014 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
// Package memory implements a Chihaya tracker storage driver within memory.
|
||||
// Stored values will not persist if the tracker is restarted.
|
||||
package memory
|
||||
|
||||
import (
|
||||
"github.com/chihaya/chihaya/config"
|
||||
"github.com/chihaya/chihaya/drivers/tracker"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
)
|
||||
|
||||
type driver struct{}
|
||||
|
||||
func (d *driver) New(conf *config.DriverConfig) tracker.Pool {
|
||||
return &Pool{
|
||||
users: make(map[string]*models.User),
|
||||
torrents: make(map[string]*models.Torrent),
|
||||
whitelist: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
tracker.Register("memory", &driver{})
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2014 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package memory
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/chihaya/chihaya/drivers/tracker"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
)
|
||||
|
||||
type Pool struct {
|
||||
users map[string]*models.User
|
||||
usersM sync.RWMutex
|
||||
|
||||
torrents map[string]*models.Torrent
|
||||
torrentsM sync.RWMutex
|
||||
|
||||
whitelist map[string]bool
|
||||
whitelistM sync.RWMutex
|
||||
}
|
||||
|
||||
func (p *Pool) Get() (tracker.Conn, error) {
|
||||
return &Conn{
|
||||
Pool: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Pool) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2014 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func PurgeInactivePeers(p Pool, purgeEmptyTorrents bool, threshold, interval time.Duration) {
|
||||
for _ = range time.NewTicker(interval).C {
|
||||
before := time.Now().Add(-threshold)
|
||||
glog.V(0).Infof("Purging peers with no announces since %s", before)
|
||||
|
||||
conn, err := p.Get()
|
||||
|
||||
if err != nil {
|
||||
glog.Error("Unable to get connection for a routine")
|
||||
continue
|
||||
}
|
||||
|
||||
err = conn.PurgeInactivePeers(purgeEmptyTorrents, before)
|
||||
if err != nil {
|
||||
glog.Errorf("Error purging torrents: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2014 The Chihaya Authors. All rights reserved.
|
||||
// Use of this source code is governed by the BSD 2-Clause license,
|
||||
// which can be found in the LICENSE file.
|
||||
|
||||
// Package tracker provides a generic interface for manipulating a
|
||||
// BitTorrent tracker's fast-moving data.
|
||||
package tracker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/chihaya/chihaya/config"
|
||||
"github.com/chihaya/chihaya/models"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrUserDNE is returned when a user does not exist.
|
||||
ErrUserDNE = errors.New("user does not exist")
|
||||
// ErrTorrentDNE is returned when a torrent does not exist.
|
||||
ErrTorrentDNE = errors.New("torrent does not exist")
|
||||
// ErrClientUnapproved is returned when a clientID is not in the whitelist.
|
||||
ErrClientUnapproved = errors.New("client is not approved")
|
||||
// ErrInvalidPasskey is returned when a passkey is not properly formatted.
|
||||
ErrInvalidPasskey = errors.New("passkey is invalid")
|
||||
|
||||
drivers = make(map[string]Driver)
|
||||
)
|
||||
|
||||
// Driver represents an interface to pool of connections to models used for
|
||||
// the tracker.
|
||||
type Driver interface {
|
||||
New(*config.DriverConfig) Pool
|
||||
}
|
||||
|
||||
// Register makes a database driver available by the provided name.
|
||||
// If Register is called twice with the same name or if driver is nil,
|
||||
// it panics.
|
||||
func Register(name string, driver Driver) {
|
||||
if driver == nil {
|
||||
panic("tracker: Register driver is nil")
|
||||
}
|
||||
if _, dup := drivers[name]; dup {
|
||||
panic("tracker: Register called twice for driver " + name)
|
||||
}
|
||||
drivers[name] = driver
|
||||
}
|
||||
|
||||
// Open creates a pool of data store connections specified by a models configuration.
|
||||
func Open(cfg *config.DriverConfig) (Pool, error) {
|
||||
driver, ok := drivers[cfg.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"unknown driver %q (forgotten import?)",
|
||||
cfg.Name,
|
||||
)
|
||||
}
|
||||
pool := driver.New(cfg)
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// Pool represents a thread-safe pool of connections to the data store
|
||||
// that can be used to safely within concurrent goroutines.
|
||||
type Pool interface {
|
||||
Close() error
|
||||
Get() (Conn, error)
|
||||
}
|
||||
|
||||
// Conn represents a connection to the data store that can be used
|
||||
// to make reads/writes.
|
||||
type Conn interface {
|
||||
// Torrent interactions
|
||||
FindTorrent(infohash string) (*models.Torrent, error)
|
||||
PutTorrent(t *models.Torrent) error
|
||||
DeleteTorrent(infohash string) error
|
||||
IncrementSnatches(infohash string) error
|
||||
TouchTorrent(infohash string) error
|
||||
PutLeecher(infohash string, p *models.Peer) error
|
||||
DeleteLeecher(infohash, peerkey string) error
|
||||
PutSeeder(infohash string, p *models.Peer) error
|
||||
DeleteSeeder(infohash, peerkey string) error
|
||||
|
||||
PurgeInactiveTorrent(infohash string) error
|
||||
PurgeInactivePeers(purgeEmptyTorrents bool, before time.Time) error
|
||||
|
||||
// User interactions
|
||||
FindUser(passkey string) (*models.User, error)
|
||||
PutUser(u *models.User) error
|
||||
DeleteUser(passkey string) error
|
||||
|
||||
// Whitelist interactions
|
||||
FindClient(clientID string) error
|
||||
PutClient(clientID string) error
|
||||
DeleteClient(clientID string) error
|
||||
}
|
||||
|
||||
// LeecherFinished moves a peer from the leeching pool to the seeder pool.
|
||||
func LeecherFinished(c Conn, infohash string, p *models.Peer) error {
|
||||
err := c.DeleteLeecher(infohash, p.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.PutSeeder(infohash, p)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user