go fmt [ci skip]

This commit is contained in:
Jimmy Zelinskie 2013-11-24 00:49:20 -05:00
parent cf36519c5e
commit 6549f49e26
10 changed files with 1735 additions and 1735 deletions

View file

@ -6,83 +6,83 @@
package config package config
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"os" "os"
"time" "time"
) )
type Duration struct { type Duration struct {
time.Duration time.Duration
} }
func (d *Duration) MarshalJSON() ([]byte, error) { func (d *Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String()) return json.Marshal(d.String())
} }
func (d *Duration) UnmarshalJSON(b []byte) error { func (d *Duration) UnmarshalJSON(b []byte) error {
var str string var str string
err := json.Unmarshal(b, &str) err := json.Unmarshal(b, &str)
d.Duration, err = time.ParseDuration(str) d.Duration, err = time.ParseDuration(str)
return err return err
} }
// DataStore represents the configuration used to connect to a data store. // DataStore represents the configuration used to connect to a data store.
type DataStore struct { type DataStore struct {
Driver string `json:"driver"` Driver string `json:"driver"`
Network string `json:"network` Network string `json:"network`
Host string `json:"host"` Host string `json:"host"`
Port string `json:"port"` Port string `json:"port"`
Username string `json:"user"` Username string `json:"user"`
Password string `json:"pass"` Password string `json:"pass"`
Schema string `json:"schema,omitempty"` Schema string `json:"schema,omitempty"`
Encoding string `json:"encoding,omitempty"` Encoding string `json:"encoding,omitempty"`
Prefix string `json:"prefix,omitempty"` Prefix string `json:"prefix,omitempty"`
MaxIdleConns int `json:"max_idle_conns,omitempty"` MaxIdleConns int `json:"max_idle_conns,omitempty"`
IdleTimeout *Duration `json:"idle_timeout,omitempty"` IdleTimeout *Duration `json:"idle_timeout,omitempty"`
} }
// Config represents a configuration for a server.Server. // Config represents a configuration for a server.Server.
type Config struct { type Config struct {
Addr string `json:"addr"` Addr string `json:"addr"`
PubAddr string `json:"pub_addr"` PubAddr string `json:"pub_addr"`
Cache DataStore `json:"cache"` Cache DataStore `json:"cache"`
Storage DataStore `json:"storage"` Storage DataStore `json:"storage"`
Private bool `json:"private"` Private bool `json:"private"`
Freeleech bool `json:"freeleech"` Freeleech bool `json:"freeleech"`
Slots bool `json:"slots"` Slots bool `json:"slots"`
Announce Duration `json:"announce"` Announce Duration `json:"announce"`
MinAnnounce Duration `json:"min_announce"` MinAnnounce Duration `json:"min_announce"`
ReadTimeout Duration `json:"read_timeout"` ReadTimeout Duration `json:"read_timeout"`
DefaultNumWant int `json:"default_num_want"` DefaultNumWant int `json:"default_num_want"`
} }
// Open is a shortcut to open a file, read it, and generate a Config. // Open is a shortcut to open a file, read it, and generate a Config.
// It supports relative and absolute paths. // It supports relative and absolute paths.
func Open(path string) (*Config, error) { func Open(path string) (*Config, error) {
expandedPath := os.ExpandEnv(path) expandedPath := os.ExpandEnv(path)
f, err := os.Open(expandedPath) f, err := os.Open(expandedPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
conf, err := newConfig(f) conf, err := newConfig(f)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conf, nil return conf, nil
} }
// New decodes JSON from a Reader into a Config. // New decodes JSON from a Reader into a Config.
func newConfig(raw io.Reader) (*Config, error) { func newConfig(raw io.Reader) (*Config, error) {
conf := &Config{} conf := &Config{}
err := json.NewDecoder(raw).Decode(conf) err := json.NewDecoder(raw).Decode(conf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conf, nil return conf, nil
} }

View file

@ -5,351 +5,351 @@
package server package server
import ( import (
"errors" "errors"
"log" "log"
"net/http" "net/http"
"path" "path"
"strconv" "strconv"
"time" "time"
"github.com/pushrax/chihaya/storage" "github.com/pushrax/chihaya/storage"
) )
func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) {
// Parse the required parameters off of a query // Parse the required parameters off of a query
compact, numWant, infohash, peerID, event, ip, port, uploaded, downloaded, left, err := s.validateAnnounceQuery(r) compact, numWant, infohash, peerID, event, ip, port, uploaded, downloaded, left, err := s.validateAnnounceQuery(r)
if err != nil { if err != nil {
fail(err, w, r) fail(err, w, r)
return return
} }
// Get a connection to the tracker db // Get a connection to the tracker db
conn, err := s.dbConnPool.Get() conn, err := s.dbConnPool.Get()
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
// Validate the user's passkey // Validate the user's passkey
passkey, _ := path.Split(r.URL.Path) passkey, _ := path.Split(r.URL.Path)
user, err := validateUser(conn, passkey) user, err := validateUser(conn, passkey)
if err != nil { if err != nil {
fail(err, w, r) fail(err, w, r)
return return
} }
// Check if the user's client is whitelisted // Check if the user's client is whitelisted
whitelisted, err := conn.ClientWhitelisted(parsePeerID(peerID)) whitelisted, err := conn.ClientWhitelisted(parsePeerID(peerID))
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if !whitelisted { if !whitelisted {
fail(errors.New("Your client is not approved"), w, r) fail(errors.New("Your client is not approved"), w, r)
return return
} }
// Find the specified torrent // Find the specified torrent
torrent, exists, err := conn.FindTorrent(infohash) torrent, exists, err := conn.FindTorrent(infohash)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if !exists { if !exists {
fail(errors.New("This torrent does not exist"), w, r) fail(errors.New("This torrent does not exist"), w, r)
return return
} }
// If the torrent was pruned and the user is seeding, unprune it // If the torrent was pruned and the user is seeding, unprune it
if !torrent.Active && left == 0 { if !torrent.Active && left == 0 {
err := conn.MarkActive(torrent) err := conn.MarkActive(torrent)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
// Create a new peer object from the request // Create a new peer object from the request
peer := &storage.Peer{ peer := &storage.Peer{
ID: peerID, ID: peerID,
UserID: user.ID, UserID: user.ID,
TorrentID: torrent.ID, TorrentID: torrent.ID,
IP: ip, IP: ip,
Port: port, Port: port,
Uploaded: uploaded, Uploaded: uploaded,
Downloaded: downloaded, Downloaded: downloaded,
Left: left, Left: left,
LastAnnounce: time.Now().Unix(), LastAnnounce: time.Now().Unix(),
} }
// Look for the user in in the pool of seeders and leechers // Look for the user in in the pool of seeders and leechers
_, seeder := torrent.Seeders[storage.PeerMapKey(peer)] _, seeder := torrent.Seeders[storage.PeerMapKey(peer)]
_, leecher := torrent.Leechers[storage.PeerMapKey(peer)] _, leecher := torrent.Leechers[storage.PeerMapKey(peer)]
switch { switch {
// Guarantee that no user is in both pools // Guarantee that no user is in both pools
case seeder && leecher: case seeder && leecher:
if left == 0 { if left == 0 {
err := conn.RemoveLeecher(torrent, peer) err := conn.RemoveLeecher(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
leecher = false leecher = false
} else { } else {
err := conn.RemoveSeeder(torrent, peer) err := conn.RemoveSeeder(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
seeder = false seeder = false
} }
case seeder: case seeder:
// Update the peer with the stats from the request // Update the peer with the stats from the request
err := conn.SetSeeder(torrent, peer) err := conn.SetSeeder(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
case leecher: case leecher:
// Update the peer with the stats from the request // Update the peer with the stats from the request
err := conn.SetLeecher(torrent, peer) err := conn.SetLeecher(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
default: default:
// Check the user's slots to see if they're allowed to leech // Check the user's slots to see if they're allowed to leech
if s.conf.Slots && user.Slots != -1 && left != 0 { if s.conf.Slots && user.Slots != -1 && left != 0 {
if user.SlotsUsed >= user.Slots { if user.SlotsUsed >= user.Slots {
fail(errors.New("You've run out of download slots."), w, r) fail(errors.New("You've run out of download slots."), w, r)
return return
} }
} }
if left == 0 { if left == 0 {
// Save the peer as a new seeder // Save the peer as a new seeder
err := conn.AddSeeder(torrent, peer) err := conn.AddSeeder(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} else { } else {
// Save the peer as a new leecher and increment the user's slots // Save the peer as a new leecher and increment the user's slots
err := conn.IncrementSlots(user) err := conn.IncrementSlots(user)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
err = conn.AddLeecher(torrent, peer) err = conn.AddLeecher(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
} }
// Handle any events in the request // Handle any events in the request
switch { switch {
case event == "stopped" || event == "paused": case event == "stopped" || event == "paused":
if seeder { if seeder {
err := conn.RemoveSeeder(torrent, peer) err := conn.RemoveSeeder(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
if leecher { if leecher {
err := conn.RemoveLeecher(torrent, peer) err := conn.RemoveLeecher(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
err = conn.DecrementSlots(user) err = conn.DecrementSlots(user)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
case event == "completed": case event == "completed":
err := conn.RecordSnatch(user, torrent) err := conn.RecordSnatch(user, torrent)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if leecher { if leecher {
err := conn.LeecherFinished(torrent, peer) err := conn.LeecherFinished(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
case leecher && left == 0: case leecher && left == 0:
// A leecher completed but the event was never received // A leecher completed but the event was never received
err := conn.LeecherFinished(torrent, peer) err := conn.LeecherFinished(torrent, peer)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
} }
if ip != peer.IP || port != peer.Port { if ip != peer.IP || port != peer.Port {
peer.Port = port peer.Port = port
peer.IP = ip peer.IP = ip
} }
// Generate the response // Generate the response
seedCount := len(torrent.Seeders) seedCount := len(torrent.Seeders)
leechCount := len(torrent.Leechers) leechCount := len(torrent.Leechers)
writeBencoded(w, "d") writeBencoded(w, "d")
writeBencoded(w, "complete") writeBencoded(w, "complete")
writeBencoded(w, seedCount) writeBencoded(w, seedCount)
writeBencoded(w, "incomplete") writeBencoded(w, "incomplete")
writeBencoded(w, leechCount) writeBencoded(w, leechCount)
writeBencoded(w, "interval") writeBencoded(w, "interval")
writeBencoded(w, s.conf.Announce.Duration) writeBencoded(w, s.conf.Announce.Duration)
writeBencoded(w, "min interval") writeBencoded(w, "min interval")
writeBencoded(w, s.conf.MinAnnounce.Duration) writeBencoded(w, s.conf.MinAnnounce.Duration)
if numWant > 0 && event != "stopped" && event != "paused" { if numWant > 0 && event != "stopped" && event != "paused" {
writeBencoded(w, "peers") writeBencoded(w, "peers")
var peerCount, count int var peerCount, count int
if compact { if compact {
if left > 0 { if left > 0 {
peerCount = minInt(numWant, leechCount) peerCount = minInt(numWant, leechCount)
} else { } else {
peerCount = minInt(numWant, leechCount+seedCount-1) peerCount = minInt(numWant, leechCount+seedCount-1)
} }
writeBencoded(w, strconv.Itoa(peerCount*6)) writeBencoded(w, strconv.Itoa(peerCount*6))
writeBencoded(w, ":") writeBencoded(w, ":")
} else { } else {
writeBencoded(w, "l") writeBencoded(w, "l")
} }
if left > 0 { if left > 0 {
// If they're seeding, give them only leechers // If they're seeding, give them only leechers
writeLeechers(w, torrent, count, numWant, compact) writeLeechers(w, torrent, count, numWant, compact)
} else { } else {
// If they're leeching, prioritize giving them seeders // If they're leeching, prioritize giving them seeders
writeSeeders(w, torrent, count, numWant, compact) writeSeeders(w, torrent, count, numWant, compact)
writeLeechers(w, torrent, count, numWant, compact) writeLeechers(w, torrent, count, numWant, compact)
} }
if compact && peerCount != count { if compact && peerCount != count {
log.Panicf("Calculated peer count (%d) != real count (%d)", peerCount, count) log.Panicf("Calculated peer count (%d) != real count (%d)", peerCount, count)
} }
if !compact { if !compact {
writeBencoded(w, "e") writeBencoded(w, "e")
} }
} }
writeBencoded(w, "e") writeBencoded(w, "e")
} }
func (s Server) validateAnnounceQuery(r *http.Request) (compact bool, numWant int, infohash, peerID, event, ip string, port, uploaded, downloaded, left uint64, err error) { func (s Server) validateAnnounceQuery(r *http.Request) (compact bool, numWant int, infohash, peerID, event, ip string, port, uploaded, downloaded, left uint64, err error) {
pq, err := parseQuery(r.URL.RawQuery) pq, err := parseQuery(r.URL.RawQuery)
if err != nil { if err != nil {
return false, 0, "", "", "", "", 0, 0, 0, 0, err return false, 0, "", "", "", "", 0, 0, 0, 0, err
} }
compact = pq.Params["compact"] == "1" compact = pq.Params["compact"] == "1"
numWant = requestedPeerCount(s.conf.DefaultNumWant, pq) numWant = requestedPeerCount(s.conf.DefaultNumWant, pq)
infohash, _ = pq.Params["info_hash"] infohash, _ = pq.Params["info_hash"]
peerID, _ = pq.Params["peer_id"] peerID, _ = pq.Params["peer_id"]
event, _ = pq.Params["event"] event, _ = pq.Params["event"]
ip, _ = requestedIP(r, pq) ip, _ = requestedIP(r, pq)
port, portErr := pq.getUint64("port") port, portErr := pq.getUint64("port")
uploaded, uploadedErr := pq.getUint64("uploaded") uploaded, uploadedErr := pq.getUint64("uploaded")
downloaded, downloadedErr := pq.getUint64("downloaded") downloaded, downloadedErr := pq.getUint64("downloaded")
left, leftErr := pq.getUint64("left") left, leftErr := pq.getUint64("left")
if infohash == "" || if infohash == "" ||
peerID == "" || peerID == "" ||
ip == "" || ip == "" ||
portErr != nil || portErr != nil ||
uploadedErr != nil || uploadedErr != nil ||
downloadedErr != nil || downloadedErr != nil ||
leftErr != nil { leftErr != nil {
return false, 0, "", "", "", "", 0, 0, 0, 0, errors.New("Malformed request") return false, 0, "", "", "", "", 0, 0, 0, 0, errors.New("Malformed request")
} }
return return
} }
func requestedPeerCount(fallback int, pq *parsedQuery) int { func requestedPeerCount(fallback int, pq *parsedQuery) int {
if numWantStr, exists := pq.Params["numWant"]; exists { if numWantStr, exists := pq.Params["numWant"]; exists {
numWant, err := strconv.Atoi(numWantStr) numWant, err := strconv.Atoi(numWantStr)
if err != nil { if err != nil {
return fallback return fallback
} }
return numWant return numWant
} }
return fallback return fallback
} }
func requestedIP(r *http.Request, pq *parsedQuery) (string, error) { func requestedIP(r *http.Request, pq *parsedQuery) (string, error) {
ip, ok := pq.Params["ip"] ip, ok := pq.Params["ip"]
ipv4, okv4 := pq.Params["ipv4"] ipv4, okv4 := pq.Params["ipv4"]
xRealIPs, xRealOk := pq.Params["X-Real-Ip"] xRealIPs, xRealOk := pq.Params["X-Real-Ip"]
switch { switch {
case ok: case ok:
return ip, nil return ip, nil
case okv4: case okv4:
return ipv4, nil return ipv4, nil
case xRealOk && len(xRealIPs) > 0: case xRealOk && len(xRealIPs) > 0:
return string(xRealIPs[0]), nil return string(xRealIPs[0]), nil
default: default:
portIndex := len(r.RemoteAddr) - 1 portIndex := len(r.RemoteAddr) - 1
for ; portIndex >= 0; portIndex-- { for ; portIndex >= 0; portIndex-- {
if r.RemoteAddr[portIndex] == ':' { if r.RemoteAddr[portIndex] == ':' {
break break
} }
} }
if portIndex != -1 { if portIndex != -1 {
return r.RemoteAddr[0:portIndex], nil return r.RemoteAddr[0:portIndex], nil
} }
return "", errors.New("Failed to parse IP address") return "", errors.New("Failed to parse IP address")
} }
} }
func minInt(a, b int) int { func minInt(a, b int) int {
if a < b { if a < b {
return a return a
} }
return b return b
} }
func writeSeeders(w http.ResponseWriter, t *storage.Torrent, count, numWant int, compact bool) { func writeSeeders(w http.ResponseWriter, t *storage.Torrent, count, numWant int, compact bool) {
for _, seed := range t.Seeders { for _, seed := range t.Seeders {
if count >= numWant { if count >= numWant {
break break
} }
if compact { if compact {
// TODO writeBencoded(w, compactAddr) // TODO writeBencoded(w, compactAddr)
} else { } else {
writeBencoded(w, "d") writeBencoded(w, "d")
writeBencoded(w, "ip") writeBencoded(w, "ip")
writeBencoded(w, seed.IP) writeBencoded(w, seed.IP)
writeBencoded(w, "peer id") writeBencoded(w, "peer id")
writeBencoded(w, seed.ID) writeBencoded(w, seed.ID)
writeBencoded(w, "port") writeBencoded(w, "port")
writeBencoded(w, seed.Port) writeBencoded(w, seed.Port)
writeBencoded(w, "e") writeBencoded(w, "e")
} }
count++ count++
} }
} }
func writeLeechers(w http.ResponseWriter, t *storage.Torrent, count, numWant int, compact bool) { func writeLeechers(w http.ResponseWriter, t *storage.Torrent, count, numWant int, compact bool) {
for _, leech := range t.Leechers { for _, leech := range t.Leechers {
if count >= numWant { if count >= numWant {
break break
} }
if compact { if compact {
// TODO writeBencoded(w, compactAddr) // TODO writeBencoded(w, compactAddr)
} else { } else {
writeBencoded(w, "d") writeBencoded(w, "d")
writeBencoded(w, "ip") writeBencoded(w, "ip")
writeBencoded(w, leech.IP) writeBencoded(w, leech.IP)
writeBencoded(w, "peer id") writeBencoded(w, "peer id")
writeBencoded(w, leech.ID) writeBencoded(w, leech.ID)
writeBencoded(w, "port") writeBencoded(w, "port")
writeBencoded(w, leech.Port) writeBencoded(w, leech.Port)
writeBencoded(w, "e") writeBencoded(w, "e")
} }
count++ count++
} }
} }

View file

@ -5,72 +5,72 @@
package server package server
import ( import (
"errors" "errors"
"io" "io"
"log" "log"
"net/http" "net/http"
"path" "path"
"github.com/pushrax/chihaya/storage" "github.com/pushrax/chihaya/storage"
) )
func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) { func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) {
// Parse the query // Parse the query
pq, err := parseQuery(r.URL.RawQuery) pq, err := parseQuery(r.URL.RawQuery)
if err != nil { if err != nil {
fail(errors.New("Error parsing query"), w, r) fail(errors.New("Error parsing query"), w, r)
return return
} }
// Get a connection to the tracker db // Get a connection to the tracker db
conn, err := s.dbConnPool.Get() conn, err := s.dbConnPool.Get()
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// Find and validate the user // Find and validate the user
passkey, _ := path.Split(r.URL.Path) passkey, _ := path.Split(r.URL.Path)
_, err = validateUser(conn, passkey) _, err = validateUser(conn, passkey)
if err != nil { if err != nil {
fail(err, w, r) fail(err, w, r)
return return
} }
io.WriteString(w, "d") io.WriteString(w, "d")
writeBencoded(w, "files") writeBencoded(w, "files")
if pq.Infohashes != nil { if pq.Infohashes != nil {
for _, infohash := range pq.Infohashes { for _, infohash := range pq.Infohashes {
torrent, exists, err := conn.FindTorrent(infohash) torrent, exists, err := conn.FindTorrent(infohash)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if exists { if exists {
writeBencoded(w, infohash) writeBencoded(w, infohash)
writeScrapeInfo(w, torrent) writeScrapeInfo(w, torrent)
} }
} }
} else if infohash, exists := pq.Params["info_hash"]; exists { } else if infohash, exists := pq.Params["info_hash"]; exists {
torrent, exists, err := conn.FindTorrent(infohash) torrent, exists, err := conn.FindTorrent(infohash)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if exists { if exists {
writeBencoded(w, infohash) writeBencoded(w, infohash)
writeScrapeInfo(w, torrent) writeScrapeInfo(w, torrent)
} }
} }
io.WriteString(w, "e") io.WriteString(w, "e")
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} }
func writeScrapeInfo(w io.Writer, torrent *storage.Torrent) { func writeScrapeInfo(w io.Writer, torrent *storage.Torrent) {
io.WriteString(w, "d") io.WriteString(w, "d")
writeBencoded(w, "complete") writeBencoded(w, "complete")
writeBencoded(w, len(torrent.Seeders)) writeBencoded(w, len(torrent.Seeders))
writeBencoded(w, "downloaded") writeBencoded(w, "downloaded")
writeBencoded(w, torrent.Snatches) writeBencoded(w, torrent.Snatches)
writeBencoded(w, "incomplete") writeBencoded(w, "incomplete")
writeBencoded(w, len(torrent.Leechers)) writeBencoded(w, len(torrent.Leechers))
io.WriteString(w, "e") io.WriteString(w, "e")
} }

View file

@ -6,140 +6,140 @@
package server package server
import ( import (
"errors" "errors"
"io" "io"
"log" "log"
"net" "net"
"net/http" "net/http"
"path" "path"
"strconv" "strconv"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/etix/stoppableListener" "github.com/etix/stoppableListener"
"github.com/pushrax/chihaya/config" "github.com/pushrax/chihaya/config"
"github.com/pushrax/chihaya/storage" "github.com/pushrax/chihaya/storage"
"github.com/pushrax/chihaya/storage/tracker" "github.com/pushrax/chihaya/storage/tracker"
) )
type Server struct { type Server struct {
conf *config.Config conf *config.Config
listener *stoppableListener.StoppableListener listener *stoppableListener.StoppableListener
dbConnPool tracker.Pool dbConnPool tracker.Pool
startTime time.Time startTime time.Time
deltaRequests int64 deltaRequests int64
rpm int64 rpm int64
http.Server http.Server
} }
func New(conf *config.Config) (*Server, error) { func New(conf *config.Config) (*Server, error) {
pool, err := tracker.Open(&conf.Cache) pool, err := tracker.Open(&conf.Cache)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s := &Server{ s := &Server{
conf: conf, conf: conf,
dbConnPool: pool, dbConnPool: pool,
Server: http.Server{ Server: http.Server{
Addr: conf.Addr, Addr: conf.Addr,
ReadTimeout: conf.ReadTimeout.Duration, ReadTimeout: conf.ReadTimeout.Duration,
}, },
} }
s.Server.Handler = s s.Server.Handler = s
return s, nil return s, nil
} }
func (s *Server) ListenAndServe() error { func (s *Server) ListenAndServe() error {
l, err := net.Listen("tcp", s.Addr) l, err := net.Listen("tcp", s.Addr)
if err != nil { if err != nil {
return err return err
} }
sl := stoppableListener.Handle(l) sl := stoppableListener.Handle(l)
s.listener = sl s.listener = sl
s.startTime = time.Now() s.startTime = time.Now()
go s.updateStats() go s.updateStats()
s.Serve(s.listener) s.Serve(s.listener)
return nil return nil
} }
func (s *Server) Stop() error { func (s *Server) Stop() error {
s.listener.Stop <- true s.listener.Stop <- true
err := s.dbConnPool.Close() err := s.dbConnPool.Close()
if err != nil { if err != nil {
return err return err
} }
return s.listener.Close() return s.listener.Close()
} }
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer atomic.AddInt64(&s.deltaRequests, 1) defer atomic.AddInt64(&s.deltaRequests, 1)
r.Close = true r.Close = true
switch r.URL.Path { switch r.URL.Path {
case "/stats": case "/stats":
s.serveStats(w, r) s.serveStats(w, r)
return return
case "/add": case "/add":
s.serveAdd(w, r) s.serveAdd(w, r)
return return
case "/remove": case "/remove":
s.serveRemove(w, r) s.serveRemove(w, r)
return return
} }
_, action := path.Split(r.URL.Path) _, action := path.Split(r.URL.Path)
switch action { switch action {
case "announce": case "announce":
s.serveAnnounce(w, r) s.serveAnnounce(w, r)
return return
case "scrape": case "scrape":
s.serveScrape(w, r) s.serveScrape(w, r)
return return
default: default:
fail(errors.New("Unknown action"), w, r) fail(errors.New("Unknown action"), w, r)
return return
} }
} }
func fail(err error, w http.ResponseWriter, r *http.Request) { func fail(err error, w http.ResponseWriter, r *http.Request) {
errmsg := err.Error() errmsg := err.Error()
message := "d14:failure reason" + strconv.Itoa(len(errmsg)) + ":" + errmsg + "e" message := "d14:failure reason" + strconv.Itoa(len(errmsg)) + ":" + errmsg + "e"
length, _ := io.WriteString(w, message) length, _ := io.WriteString(w, message)
w.Header().Add("Content-Length", string(length)) w.Header().Add("Content-Length", string(length))
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} }
func validateUser(conn tracker.Conn, dir string) (*storage.User, error) { func validateUser(conn tracker.Conn, dir string) (*storage.User, error) {
if len(dir) != 34 { if len(dir) != 34 {
return nil, errors.New("Passkey is invalid") return nil, errors.New("Passkey is invalid")
} }
passkey := dir[1:33] passkey := dir[1:33]
user, exists, err := conn.FindUser(passkey) user, exists, err := conn.FindUser(passkey)
if err != nil { if err != nil {
log.Panicf("server: %s", err) log.Panicf("server: %s", err)
} }
if !exists { if !exists {
return nil, errors.New("User not found") return nil, errors.New("User not found")
} }
return user, nil return user, nil
} }
// Takes a peer_id and returns a ClientID // Takes a peer_id and returns a ClientID
func parsePeerID(peerID string) (clientID string) { func parsePeerID(peerID string) (clientID string) {
if peerID[0] == '-' { if peerID[0] == '-' {
clientID = peerID[1:7] clientID = peerID[1:7]
} else { } else {
clientID = peerID[0:6] clientID = peerID[0:6]
} }
return return
} }

View file

@ -5,35 +5,35 @@
package server package server
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/pushrax/chihaya/config" "github.com/pushrax/chihaya/config"
) )
type stats struct { type stats struct {
Uptime config.Duration `json:"uptime"` Uptime config.Duration `json:"uptime"`
RPM int64 `json:"req_per_min"` RPM int64 `json:"req_per_min"`
} }
func (s *Server) serveStats(w http.ResponseWriter, r *http.Request) { func (s *Server) serveStats(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
stats, _ := json.Marshal(&stats{ stats, _ := json.Marshal(&stats{
config.Duration{time.Now().Sub(s.startTime)}, config.Duration{time.Now().Sub(s.startTime)},
s.rpm, s.rpm,
}) })
length, _ := w.Write(stats) length, _ := w.Write(stats)
w.Header().Set("Content-Length", string(length)) w.Header().Set("Content-Length", string(length))
w.(http.Flusher).Flush() w.(http.Flusher).Flush()
} }
func (s *Server) updateStats() { func (s *Server) updateStats() {
for _ = range time.NewTicker(time.Minute).C { for _ = range time.NewTicker(time.Minute).C {
s.rpm = s.deltaRequests s.rpm = s.deltaRequests
atomic.StoreInt64(&s.deltaRequests, 0) atomic.StoreInt64(&s.deltaRequests, 0)
} }
} }

View file

@ -5,51 +5,51 @@
package server package server
import ( import (
"errors" "errors"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"testing" "testing"
"github.com/pushrax/chihaya/config" "github.com/pushrax/chihaya/config"
_ "github.com/pushrax/chihaya/storage/backend/batter" _ "github.com/pushrax/chihaya/storage/backend/batter"
_ "github.com/pushrax/chihaya/storage/tracker/redis" _ "github.com/pushrax/chihaya/storage/tracker/redis"
) )
func newTestServer() (*Server, error) { func newTestServer() (*Server, error) {
testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH")) testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
s, err := New(testConfig) s, err := New(testConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s, nil return s, nil
} }
func TestStats(t *testing.T) { func TestStats(t *testing.T) {
s, err := newTestServer() s, err := newTestServer()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
r, err := http.NewRequest("GET", "127.0.0.1:80/stats", nil) r, err := http.NewRequest("GET", "127.0.0.1:80/stats", nil)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
w := httptest.NewRecorder() w := httptest.NewRecorder()
s.serveStats(w, r) s.serveStats(w, r)
if w.Code != 200 { if w.Code != 200 {
t.Error(errors.New("/stats did not return HTTP 200")) t.Error(errors.New("/stats did not return HTTP 200"))
} }
if w.Header()["Content-Type"][0] != "application/json" { if w.Header()["Content-Type"][0] != "application/json" {
t.Error(errors.New("/stats did not return the proper Content-Type header")) t.Error(errors.New("/stats did not return the proper Content-Type header"))
} }
} }

View file

@ -5,559 +5,559 @@
package redis package redis
import ( import (
"math/rand" "math/rand"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"time" "time"
"github.com/pushrax/chihaya/config" "github.com/pushrax/chihaya/config"
"github.com/pushrax/chihaya/storage" "github.com/pushrax/chihaya/storage"
"github.com/pushrax/chihaya/storage/tracker" "github.com/pushrax/chihaya/storage/tracker"
) )
func createTestConn() tracker.Conn { func createTestConn() tracker.Conn {
testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH")) testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH"))
panicOnErr(err) panicOnErr(err)
conf := &testConfig.Cache conf := &testConfig.Cache
testPool, err := tracker.Open(conf) testPool, err := tracker.Open(conf)
panicOnErr(err) panicOnErr(err)
newConn, err := testPool.Get() newConn, err := testPool.Get()
panicOnErr(err) panicOnErr(err)
return newConn return newConn
} }
func TestFindUserSuccess(t *testing.T) { func TestFindUserSuccess(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
foundUser, found, err := conn.FindUser(testUser.Passkey) foundUser, found, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
t.Error("user not found", testUser) t.Error("user not found", testUser)
} }
if *foundUser != *testUser { if *foundUser != *testUser {
t.Error("found user mismatch", *foundUser, testUser) t.Error("found user mismatch", *foundUser, testUser)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
} }
func TestFindUserFail(t *testing.T) { func TestFindUserFail(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
foundUser, found, err := conn.FindUser(testUser.Passkey) foundUser, found, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("user found", foundUser) t.Error("user found", foundUser)
} }
} }
func TestRemoveUser(t *testing.T) { func TestRemoveUser(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
err := conn.RemoveUser(testUser) err := conn.RemoveUser(testUser)
panicOnErr(err) panicOnErr(err)
foundUser, found, err := conn.FindUser(testUser.Passkey) foundUser, found, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("removed user found", foundUser) t.Error("removed user found", foundUser)
} }
} }
func TestFindTorrentSuccess(t *testing.T) { func TestFindTorrentSuccess(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
t.Error("torrent not found", testTorrent) t.Error("torrent not found", testTorrent)
} }
if !reflect.DeepEqual(foundTorrent, testTorrent) { if !reflect.DeepEqual(foundTorrent, testTorrent) {
t.Error("found torrent mismatch", foundTorrent, testTorrent) t.Error("found torrent mismatch", foundTorrent, testTorrent)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestFindTorrentFail(t *testing.T) { func TestFindTorrentFail(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("torrent found", foundTorrent) t.Error("torrent found", foundTorrent)
} }
} }
func TestRemoveTorrent(t *testing.T) { func TestRemoveTorrent(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("removed torrent found", foundTorrent) t.Error("removed torrent found", foundTorrent)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestClientWhitelistSuccess(t *testing.T) { func TestClientWhitelistSuccess(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testPeerID := "-lt0D30-" testPeerID := "-lt0D30-"
panicOnErr(conn.WhitelistClient(testPeerID)) panicOnErr(conn.WhitelistClient(testPeerID))
found, err := conn.ClientWhitelisted(testPeerID) found, err := conn.ClientWhitelisted(testPeerID)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
t.Error("peerID not found", testPeerID) t.Error("peerID not found", testPeerID)
} }
// Cleanup // Cleanup
panicOnErr(conn.UnWhitelistClient(testPeerID)) panicOnErr(conn.UnWhitelistClient(testPeerID))
} }
func TestClientWhitelistFail(t *testing.T) { func TestClientWhitelistFail(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testPeerID2 := "TIX0192" testPeerID2 := "TIX0192"
found, err := conn.ClientWhitelisted(testPeerID2) found, err := conn.ClientWhitelisted(testPeerID2)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("peerID found", testPeerID2) t.Error("peerID found", testPeerID2)
} }
} }
func TestRecordSnatch(t *testing.T) { func TestRecordSnatch(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
userSnatches := testUser.Snatches userSnatches := testUser.Snatches
torrentSnatches := testTorrent.Snatches torrentSnatches := testTorrent.Snatches
panicOnErr(conn.RecordSnatch(testUser, testTorrent)) panicOnErr(conn.RecordSnatch(testUser, testTorrent))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundUser, _, err := conn.FindUser(testUser.Passkey) foundUser, _, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if testUser.Snatches != userSnatches+1 { if testUser.Snatches != userSnatches+1 {
t.Error("snatch not recorded to local user", testUser.Snatches, userSnatches+1) t.Error("snatch not recorded to local user", testUser.Snatches, userSnatches+1)
} }
if testTorrent.Snatches != torrentSnatches+1 { if testTorrent.Snatches != torrentSnatches+1 {
t.Error("snatch not recorded to local torrent") t.Error("snatch not recorded to local torrent")
} }
if foundUser.Snatches != userSnatches+1 { if foundUser.Snatches != userSnatches+1 {
t.Error("snatch not recorded to cached user", foundUser.Snatches, userSnatches+1) t.Error("snatch not recorded to cached user", foundUser.Snatches, userSnatches+1)
} }
if foundTorrent.Snatches != torrentSnatches+1 { if foundTorrent.Snatches != torrentSnatches+1 {
t.Error("snatch not recorded to cached torrent") t.Error("snatch not recorded to cached torrent")
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
} }
func TestMarkActive(t *testing.T) { func TestMarkActive(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
testTorrent.Active = false testTorrent.Active = false
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
panicOnErr(conn.MarkActive(testTorrent)) panicOnErr(conn.MarkActive(testTorrent))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if foundTorrent.Active != true { if foundTorrent.Active != true {
t.Error("cached torrent not activated") t.Error("cached torrent not activated")
} }
if testTorrent.Active != true { if testTorrent.Active != true {
t.Error("cached torrent not activated") t.Error("cached torrent not activated")
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestClientWhitelistRemove(t *testing.T) { func TestClientWhitelistRemove(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testPeerID := "-lt0D30-" testPeerID := "-lt0D30-"
panicOnErr(conn.WhitelistClient(testPeerID)) panicOnErr(conn.WhitelistClient(testPeerID))
panicOnErr(conn.UnWhitelistClient(testPeerID)) panicOnErr(conn.UnWhitelistClient(testPeerID))
found, err := conn.ClientWhitelisted(testPeerID) found, err := conn.ClientWhitelisted(testPeerID)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("removed peerID found", testPeerID) t.Error("removed peerID found", testPeerID)
} }
} }
func TestAddSeeder(t *testing.T) { func TestAddSeeder(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundSeeder, found := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, found := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if found && foundSeeder != *testSeeder { if found && foundSeeder != *testSeeder {
t.Error("seeder not added to cache", testSeeder) t.Error("seeder not added to cache", testSeeder)
} }
foundSeeder, found = testTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, found = testTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if found && foundSeeder != *testSeeder { if found && foundSeeder != *testSeeder {
t.Error("seeder not added to local", testSeeder) t.Error("seeder not added to local", testSeeder)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestAddLeecher(t *testing.T) { func TestAddLeecher(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found && foundLeecher != *testLeecher { if found && foundLeecher != *testLeecher {
t.Error("leecher not added to cache", testLeecher) t.Error("leecher not added to cache", testLeecher)
} }
foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found && foundLeecher != *testLeecher { if found && foundLeecher != *testLeecher {
t.Error("leecher not added to local", testLeecher) t.Error("leecher not added to local", testLeecher)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestRemoveSeeder(t *testing.T) { func TestRemoveSeeder(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder)) panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder))
foundSeeder, found := testTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, found := testTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if found || foundSeeder == *testSeeder { if found || foundSeeder == *testSeeder {
t.Error("seeder not removed from local", foundSeeder) t.Error("seeder not removed from local", foundSeeder)
} }
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundSeeder, found = foundTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, found = foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if found || foundSeeder == *testSeeder { if found || foundSeeder == *testSeeder {
t.Error("seeder not removed from cache", foundSeeder, *testSeeder) t.Error("seeder not removed from cache", foundSeeder, *testSeeder)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestRemoveLeecher(t *testing.T) { func TestRemoveLeecher(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
panicOnErr(conn.RemoveLeecher(testTorrent, testLeecher)) panicOnErr(conn.RemoveLeecher(testTorrent, testLeecher))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found || foundLeecher == *testLeecher { if found || foundLeecher == *testLeecher {
t.Error("leecher not removed from cache", foundLeecher, *testLeecher) t.Error("leecher not removed from cache", foundLeecher, *testLeecher)
} }
foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found || foundLeecher == *testLeecher { if found || foundLeecher == *testLeecher {
t.Error("leecher not removed from local", foundLeecher, *testLeecher) t.Error("leecher not removed from local", foundLeecher, *testLeecher)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestSetSeeder(t *testing.T) { func TestSetSeeder(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
testSeeder.Uploaded += uint64(r.Int63()) testSeeder.Uploaded += uint64(r.Int63())
panicOnErr(conn.SetSeeder(testTorrent, testSeeder)) panicOnErr(conn.SetSeeder(testTorrent, testSeeder))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if foundSeeder != *testSeeder { if foundSeeder != *testSeeder {
t.Error("seeder not updated in cache", foundSeeder, *testSeeder) t.Error("seeder not updated in cache", foundSeeder, *testSeeder)
} }
foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if foundSeeder != *testSeeder { if foundSeeder != *testSeeder {
t.Error("seeder not updated in local", foundSeeder, *testSeeder) t.Error("seeder not updated in local", foundSeeder, *testSeeder)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestSetLeecher(t *testing.T) { func TestSetLeecher(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
testLeecher.Uploaded += uint64(r.Int63()) testLeecher.Uploaded += uint64(r.Int63())
panicOnErr(conn.SetLeecher(testTorrent, testLeecher)) panicOnErr(conn.SetLeecher(testTorrent, testLeecher))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundLeecher, _ := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, _ := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if foundLeecher != *testLeecher { if foundLeecher != *testLeecher {
t.Error("leecher not updated in cache", testLeecher) t.Error("leecher not updated in cache", testLeecher)
} }
foundLeecher, _ = testTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, _ = testTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if foundLeecher != *testLeecher { if foundLeecher != *testLeecher {
t.Error("leecher not updated in local", testLeecher) t.Error("leecher not updated in local", testLeecher)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestIncrementSlots(t *testing.T) { func TestIncrementSlots(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
numSlots := testUser.Slots numSlots := testUser.Slots
panicOnErr(conn.IncrementSlots(testUser)) panicOnErr(conn.IncrementSlots(testUser))
foundUser, _, err := conn.FindUser(testUser.Passkey) foundUser, _, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if foundUser.Slots != numSlots+1 { if foundUser.Slots != numSlots+1 {
t.Error("cached slots not incremented") t.Error("cached slots not incremented")
} }
if testUser.Slots != numSlots+1 { if testUser.Slots != numSlots+1 {
t.Error("local slots not incremented") t.Error("local slots not incremented")
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
} }
func TestDecrementSlots(t *testing.T) { func TestDecrementSlots(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
numSlots := testUser.Slots numSlots := testUser.Slots
panicOnErr(conn.DecrementSlots(testUser)) panicOnErr(conn.DecrementSlots(testUser))
foundUser, _, err := conn.FindUser(testUser.Passkey) foundUser, _, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if foundUser.Slots != numSlots-1 { if foundUser.Slots != numSlots-1 {
t.Error("cached slots not incremented") t.Error("cached slots not incremented")
} }
if testUser.Slots != numSlots-1 { if testUser.Slots != numSlots-1 {
t.Error("local slots not incremented") t.Error("local slots not incremented")
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
} }
func TestLeecherFinished(t *testing.T) { func TestLeecherFinished(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
testLeecher.Left = 0 testLeecher.Left = 0
panicOnErr(conn.LeecherFinished(testTorrent, testLeecher)) panicOnErr(conn.LeecherFinished(testTorrent, testLeecher))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testLeecher)] foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testLeecher)]
if foundSeeder != *testLeecher { if foundSeeder != *testLeecher {
t.Error("seeder not added to cache", foundSeeder, *testLeecher) t.Error("seeder not added to cache", foundSeeder, *testLeecher)
} }
foundSeeder, _ = foundTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundSeeder, _ = foundTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if foundSeeder == *testLeecher { if foundSeeder == *testLeecher {
t.Error("leecher not removed from cache", testLeecher) t.Error("leecher not removed from cache", testLeecher)
} }
foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testLeecher)] foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testLeecher)]
if foundSeeder != *testLeecher { if foundSeeder != *testLeecher {
t.Error("seeder not added to local", testLeecher) t.Error("seeder not added to local", testLeecher)
} }
foundSeeder, _ = testTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundSeeder, _ = testTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if foundSeeder == *testLeecher { if foundSeeder == *testLeecher {
t.Error("leecher not removed from local", testLeecher) t.Error("leecher not removed from local", testLeecher)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
// Add, update, verify remove // Add, update, verify remove
func TestUpdatePeer(t *testing.T) { func TestUpdatePeer(t *testing.T) {
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
// Update a seeder, set it, then check to make sure it updated // Update a seeder, set it, then check to make sure it updated
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
testSeeder.Uploaded += uint64(r.Int63()) testSeeder.Uploaded += uint64(r.Int63())
panicOnErr(conn.SetSeeder(testTorrent, testSeeder)) panicOnErr(conn.SetSeeder(testTorrent, testSeeder))
panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder)) panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if seeder, exists := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]; exists { if seeder, exists := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]; exists {
t.Error("seeder not removed from cache", seeder) t.Error("seeder not removed from cache", seeder)
} }
if seeder, exists := testTorrent.Seeders[storage.PeerMapKey(testSeeder)]; exists { if seeder, exists := testTorrent.Seeders[storage.PeerMapKey(testSeeder)]; exists {
t.Error("seeder not removed from local", seeder) t.Error("seeder not removed from local", seeder)
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestParallelFindUser(t *testing.T) { func TestParallelFindUser(t *testing.T) {
t.Parallel() t.Parallel()
if testing.Short() { if testing.Short() {
t.Skip() t.Skip()
} }
conn := createTestConn() conn := createTestConn()
testUserSuccess := createTestUser() testUserSuccess := createTestUser()
testUserFail := createTestUser() testUserFail := createTestUser()
panicOnErr(conn.AddUser(testUserSuccess)) panicOnErr(conn.AddUser(testUserSuccess))
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
foundUser, found, err := conn.FindUser(testUserFail.Passkey) foundUser, found, err := conn.FindUser(testUserFail.Passkey)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("user found", foundUser) t.Error("user found", foundUser)
} }
foundUser, found, err = conn.FindUser(testUserSuccess.Passkey) foundUser, found, err = conn.FindUser(testUserSuccess.Passkey)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
t.Error("user not found", testUserSuccess) t.Error("user not found", testUserSuccess)
} }
if *foundUser != *testUserSuccess { if *foundUser != *testUserSuccess {
t.Error("found user mismatch", *foundUser, testUserSuccess) t.Error("found user mismatch", *foundUser, testUserSuccess)
} }
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveUser(testUserSuccess)) panicOnErr(conn.RemoveUser(testUserSuccess))
} }
func TestParallelFindTorrent(t *testing.T) { func TestParallelFindTorrent(t *testing.T) {
t.Parallel() t.Parallel()
if testing.Short() { if testing.Short() {
t.Skip() t.Skip()
} }
conn := createTestConn() conn := createTestConn()
testTorrentSuccess := createTestTorrent() testTorrentSuccess := createTestTorrent()
testTorrentFail := createTestTorrent() testTorrentFail := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrentSuccess)) panicOnErr(conn.AddTorrent(testTorrentSuccess))
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
foundTorrent, found, err := conn.FindTorrent(testTorrentSuccess.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrentSuccess.Infohash)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
t.Error("torrent not found", testTorrentSuccess) t.Error("torrent not found", testTorrentSuccess)
} }
if !reflect.DeepEqual(foundTorrent, testTorrentSuccess) { if !reflect.DeepEqual(foundTorrent, testTorrentSuccess) {
t.Error("found torrent mismatch", foundTorrent, testTorrentSuccess) t.Error("found torrent mismatch", foundTorrent, testTorrentSuccess)
} }
foundTorrent, found, err = conn.FindTorrent(testTorrentFail.Infohash) foundTorrent, found, err = conn.FindTorrent(testTorrentFail.Infohash)
panicOnErr(err) panicOnErr(err)
if found { if found {
t.Error("torrent found", foundTorrent) t.Error("torrent found", foundTorrent)
} }
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrentSuccess)) panicOnErr(conn.RemoveTorrent(testTorrentSuccess))
} }
func TestParallelSetSeeder(t *testing.T) { func TestParallelSetSeeder(t *testing.T) {
t.Parallel() t.Parallel()
if testing.Short() { if testing.Short() {
t.Skip() t.Skip()
} }
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
testSeeder.Uploaded += uint64(r.Int63()) testSeeder.Uploaded += uint64(r.Int63())
panicOnErr(conn.SetSeeder(testTorrent, testSeeder)) panicOnErr(conn.SetSeeder(testTorrent, testSeeder))
foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, _, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, _ := foundTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if foundSeeder != *testSeeder { if foundSeeder != *testSeeder {
t.Error("seeder not updated in cache", foundSeeder, *testSeeder) t.Error("seeder not updated in cache", foundSeeder, *testSeeder)
} }
foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testSeeder)] foundSeeder, _ = testTorrent.Seeders[storage.PeerMapKey(testSeeder)]
if foundSeeder != *testSeeder { if foundSeeder != *testSeeder {
t.Error("seeder not updated in local", foundSeeder, *testSeeder) t.Error("seeder not updated in local", foundSeeder, *testSeeder)
} }
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }
func TestParallelAddLeecher(t *testing.T) { func TestParallelAddLeecher(t *testing.T) {
t.Parallel() t.Parallel()
if testing.Short() { if testing.Short() {
t.Skip() t.Skip()
} }
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found := foundTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found && foundLeecher != *testLeecher { if found && foundLeecher != *testLeecher {
t.Error("leecher not added to cache", testLeecher) t.Error("leecher not added to cache", testLeecher)
} }
foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)] foundLeecher, found = testTorrent.Leechers[storage.PeerMapKey(testLeecher)]
if found && foundLeecher != *testLeecher { if found && foundLeecher != *testLeecher {
t.Error("leecher not added to local", testLeecher) t.Error("leecher not added to local", testLeecher)
} }
} }
// Cleanup // Cleanup
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
} }

File diff suppressed because it is too large Load diff

View file

@ -5,284 +5,284 @@
package redis package redis
import ( import (
"math/rand" "math/rand"
"testing" "testing"
"time" "time"
) )
func BenchmarkSuccessfulFindUser(b *testing.B) { func BenchmarkSuccessfulFindUser(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
foundUser, found, err := conn.FindUser(testUser.Passkey) foundUser, found, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
b.Error("user not found", testUser) b.Error("user not found", testUser)
} }
if *foundUser != *testUser { if *foundUser != *testUser {
b.Error("found user mismatch", *foundUser, testUser) b.Error("found user mismatch", *foundUser, testUser)
} }
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
b.StartTimer() b.StartTimer()
} }
func BenchmarkFailedFindUser(b *testing.B) { func BenchmarkFailedFindUser(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
_, found, err := conn.FindUser(testUser.Passkey) _, found, err := conn.FindUser(testUser.Passkey)
panicOnErr(err) panicOnErr(err)
if found { if found {
b.Error("user not found", testUser) b.Error("user not found", testUser)
} }
} }
} }
func BenchmarkSuccessfulFindTorrent(b *testing.B) { func BenchmarkSuccessfulFindTorrent(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
b.Error("torrent not found", testTorrent) b.Error("torrent not found", testTorrent)
} }
// Incomplete comparison as maps make struct not nativly comparable // Incomplete comparison as maps make struct not nativly comparable
if foundTorrent.Infohash != testTorrent.Infohash { if foundTorrent.Infohash != testTorrent.Infohash {
b.Error("found torrent mismatch", foundTorrent, testTorrent) b.Error("found torrent mismatch", foundTorrent, testTorrent)
} }
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
func BenchmarkFailFindTorrent(b *testing.B) { func BenchmarkFailFindTorrent(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash) foundTorrent, found, err := conn.FindTorrent(testTorrent.Infohash)
panicOnErr(err) panicOnErr(err)
if found { if found {
b.Error("torrent found", foundTorrent) b.Error("torrent found", foundTorrent)
} }
} }
} }
func BenchmarkSuccessfulClientWhitelisted(b *testing.B) { func BenchmarkSuccessfulClientWhitelisted(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testPeerID := "-lt0D30-" testPeerID := "-lt0D30-"
panicOnErr(conn.WhitelistClient(testPeerID)) panicOnErr(conn.WhitelistClient(testPeerID))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
found, err := conn.ClientWhitelisted(testPeerID) found, err := conn.ClientWhitelisted(testPeerID)
panicOnErr(err) panicOnErr(err)
if !found { if !found {
b.Error("peerID not found", testPeerID) b.Error("peerID not found", testPeerID)
} }
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.UnWhitelistClient(testPeerID)) panicOnErr(conn.UnWhitelistClient(testPeerID))
b.StartTimer() b.StartTimer()
} }
func BenchmarkFailClientWhitelisted(b *testing.B) { func BenchmarkFailClientWhitelisted(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testPeerID2 := "TIX0192" testPeerID2 := "TIX0192"
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
found, err := conn.ClientWhitelisted(testPeerID2) found, err := conn.ClientWhitelisted(testPeerID2)
panicOnErr(err) panicOnErr(err)
if found { if found {
b.Error("peerID found", testPeerID2) b.Error("peerID found", testPeerID2)
} }
} }
} }
func BenchmarkRecordSnatch(b *testing.B) { func BenchmarkRecordSnatch(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
panicOnErr(conn.RecordSnatch(testUser, testTorrent)) panicOnErr(conn.RecordSnatch(testUser, testTorrent))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
b.StartTimer() b.StartTimer()
} }
func BenchmarkMarkActive(b *testing.B) { func BenchmarkMarkActive(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
testTorrent.Active = false testTorrent.Active = false
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
panicOnErr(conn.MarkActive(testTorrent)) panicOnErr(conn.MarkActive(testTorrent))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
func BenchmarkAddSeeder(b *testing.B) { func BenchmarkAddSeeder(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
b.StopTimer() b.StopTimer()
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
b.StartTimer() b.StartTimer()
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
func BenchmarkRemoveSeeder(b *testing.B) { func BenchmarkRemoveSeeder(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
b.StopTimer() b.StopTimer()
conn.AddSeeder(testTorrent, testSeeder) conn.AddSeeder(testTorrent, testSeeder)
b.StartTimer() b.StartTimer()
panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder)) panicOnErr(conn.RemoveSeeder(testTorrent, testSeeder))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
func BenchmarkSetSeeder(b *testing.B) { func BenchmarkSetSeeder(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
testSeeder := createTestPeer(createTestUserID(), testTorrent.ID) testSeeder := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddSeeder(testTorrent, testSeeder)) panicOnErr(conn.AddSeeder(testTorrent, testSeeder))
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
b.StopTimer() b.StopTimer()
testSeeder.Uploaded += uint64(r.Int63()) testSeeder.Uploaded += uint64(r.Int63())
b.StartTimer() b.StartTimer()
conn.SetSeeder(testTorrent, testSeeder) conn.SetSeeder(testTorrent, testSeeder)
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
func BenchmarkIncrementSlots(b *testing.B) { func BenchmarkIncrementSlots(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testUser := createTestUser() testUser := createTestUser()
panicOnErr(conn.AddUser(testUser)) panicOnErr(conn.AddUser(testUser))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
panicOnErr(conn.IncrementSlots(testUser)) panicOnErr(conn.IncrementSlots(testUser))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveUser(testUser)) panicOnErr(conn.RemoveUser(testUser))
b.StartTimer() b.StartTimer()
} }
func BenchmarkLeecherFinished(b *testing.B) { func BenchmarkLeecherFinished(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
b.StopTimer() b.StopTimer()
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
testLeecher.Left = 0 testLeecher.Left = 0
b.StartTimer() b.StartTimer()
panicOnErr(conn.LeecherFinished(testTorrent, testLeecher)) panicOnErr(conn.LeecherFinished(testTorrent, testLeecher))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
panicOnErr(conn.RemoveTorrent(testTorrent)) panicOnErr(conn.RemoveTorrent(testTorrent))
b.StartTimer() b.StartTimer()
} }
// This is a comparision to the Leecher finished function // This is a comparision to the Leecher finished function
func BenchmarkRemoveLeecherAddSeeder(b *testing.B) { func BenchmarkRemoveLeecherAddSeeder(b *testing.B) {
b.StopTimer() b.StopTimer()
conn := createTestConn() conn := createTestConn()
testTorrent := createTestTorrent() testTorrent := createTestTorrent()
panicOnErr(conn.AddTorrent(testTorrent)) panicOnErr(conn.AddTorrent(testTorrent))
b.StartTimer() b.StartTimer()
for bCount := 0; bCount < b.N; bCount++ { for bCount := 0; bCount < b.N; bCount++ {
b.StopTimer() b.StopTimer()
testLeecher := createTestPeer(createTestUserID(), testTorrent.ID) testLeecher := createTestPeer(createTestUserID(), testTorrent.ID)
panicOnErr(conn.AddLeecher(testTorrent, testLeecher)) panicOnErr(conn.AddLeecher(testTorrent, testLeecher))
testLeecher.Left = 0 testLeecher.Left = 0
b.StartTimer() b.StartTimer()
panicOnErr(conn.RemoveLeecher(testTorrent, testLeecher)) panicOnErr(conn.RemoveLeecher(testTorrent, testLeecher))
panicOnErr(conn.AddSeeder(testTorrent, testLeecher)) panicOnErr(conn.AddSeeder(testTorrent, testLeecher))
} }
// Cleanup // Cleanup
b.StopTimer() b.StopTimer()
conn.RemoveTorrent(testTorrent) conn.RemoveTorrent(testTorrent)
b.StartTimer() b.StartTimer()
} }

View file

@ -5,180 +5,180 @@
package redis package redis
import ( import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"io" "io"
"os" "os"
"strconv" "strconv"
"testing" "testing"
"github.com/garyburd/redigo/redis" "github.com/garyburd/redigo/redis"
"github.com/pushrax/chihaya/config" "github.com/pushrax/chihaya/config"
"github.com/pushrax/chihaya/storage" "github.com/pushrax/chihaya/storage"
) )
var ( var (
testTorrentIDChannel chan uint64 testTorrentIDChannel chan uint64
testUserIDChannel chan uint64 testUserIDChannel chan uint64
testPeerIDChannel chan int testPeerIDChannel chan int
) )
func init() { func init() {
testTorrentIDChannel = make(chan uint64, 100) testTorrentIDChannel = make(chan uint64, 100)
testUserIDChannel = make(chan uint64, 100) testUserIDChannel = make(chan uint64, 100)
testPeerIDChannel = make(chan int, 100) testPeerIDChannel = make(chan int, 100)
// Sync access to ID counter with buffered global channels // Sync access to ID counter with buffered global channels
go func() { go func() {
for i := 0; ; i++ { for i := 0; ; i++ {
testTorrentIDChannel <- uint64(i) testTorrentIDChannel <- uint64(i)
} }
}() }()
go func() { go func() {
for i := 0; ; i++ { for i := 0; ; i++ {
testUserIDChannel <- uint64(i) testUserIDChannel <- uint64(i)
} }
}() }()
go func() { go func() {
for i := 0; ; i++ { for i := 0; ; i++ {
testPeerIDChannel <- i testPeerIDChannel <- i
} }
}() }()
} }
func createTestTorrentID() uint64 { func createTestTorrentID() uint64 {
return <-testTorrentIDChannel return <-testTorrentIDChannel
} }
func createTestUserID() uint64 { func createTestUserID() uint64 {
return <-testUserIDChannel return <-testUserIDChannel
} }
func createTestPeerID() string { func createTestPeerID() string {
return "-testPeerID-" + strconv.Itoa(<-testPeerIDChannel) return "-testPeerID-" + strconv.Itoa(<-testPeerIDChannel)
} }
func createTestInfohash() string { func createTestInfohash() string {
uuid := make([]byte, 40) uuid := make([]byte, 40)
n, err := io.ReadFull(rand.Reader, uuid) n, err := io.ReadFull(rand.Reader, uuid)
if n != len(uuid) || err != nil { if n != len(uuid) || err != nil {
panic(err) panic(err)
} }
return string(uuid) return string(uuid)
} }
func createTestPasskey() string { func createTestPasskey() string {
uuid := make([]byte, 40) uuid := make([]byte, 40)
n, err := io.ReadFull(rand.Reader, uuid) n, err := io.ReadFull(rand.Reader, uuid)
if n != len(uuid) || err != nil { if n != len(uuid) || err != nil {
panic(err) panic(err)
} }
return string(uuid) return string(uuid)
} }
func panicOnErr(err error) { func panicOnErr(err error) {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
panic(err) panic(err)
} }
} }
func createTestRedisConn() *Conn { func createTestRedisConn() *Conn {
testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH")) testConfig, err := config.Open(os.Getenv("TESTCONFIGPATH"))
conf := &testConfig.Cache conf := &testConfig.Cache
panicOnErr(err) panicOnErr(err)
testPool := &Pool{ testPool := &Pool{
conf: conf, conf: conf,
pool: redis.Pool{ pool: redis.Pool{
MaxIdle: conf.MaxIdleConns, MaxIdle: conf.MaxIdleConns,
IdleTimeout: conf.IdleTimeout.Duration, IdleTimeout: conf.IdleTimeout.Duration,
Dial: makeDialFunc(conf), Dial: makeDialFunc(conf),
TestOnBorrow: testOnBorrow, TestOnBorrow: testOnBorrow,
}, },
} }
newConn := &Conn{ newConn := &Conn{
conf: testPool.conf, conf: testPool.conf,
done: false, done: false,
Conn: testPool.pool.Get(), Conn: testPool.pool.Get(),
} }
panicOnErr(err) panicOnErr(err)
// Test connection before returning // Test connection before returning
_, err = newConn.Do("PING") _, err = newConn.Do("PING")
panicOnErr(err) panicOnErr(err)
return newConn return newConn
} }
func createTestUser() *storage.User { func createTestUser() *storage.User {
return &storage.User{ID: createTestUserID(), Passkey: createTestPasskey(), return &storage.User{ID: createTestUserID(), Passkey: createTestPasskey(),
UpMultiplier: 1.01, DownMultiplier: 1.0, Slots: 4, SlotsUsed: 2, Snatches: 7} UpMultiplier: 1.01, DownMultiplier: 1.0, Slots: 4, SlotsUsed: 2, Snatches: 7}
} }
func createTestPeer(userID uint64, torrentID uint64) *storage.Peer { func createTestPeer(userID uint64, torrentID uint64) *storage.Peer {
return &storage.Peer{ID: createTestPeerID(), UserID: userID, TorrentID: torrentID, return &storage.Peer{ID: createTestPeerID(), UserID: userID, TorrentID: torrentID,
IP: "127.0.0.1", Port: 6889, Uploaded: 1024, Downloaded: 3000, Left: 4200, LastAnnounce: 11} IP: "127.0.0.1", Port: 6889, Uploaded: 1024, Downloaded: 3000, Left: 4200, LastAnnounce: 11}
} }
func createTestPeers(torrentID uint64, num int) map[string]storage.Peer { func createTestPeers(torrentID uint64, num int) map[string]storage.Peer {
testPeers := make(map[string]storage.Peer) testPeers := make(map[string]storage.Peer)
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
tempPeer := createTestPeer(createTestUserID(), torrentID) tempPeer := createTestPeer(createTestUserID(), torrentID)
testPeers[storage.PeerMapKey(tempPeer)] = *tempPeer testPeers[storage.PeerMapKey(tempPeer)] = *tempPeer
} }
return testPeers return testPeers
} }
func createTestTorrent() *storage.Torrent { func createTestTorrent() *storage.Torrent {
torrentInfohash := createTestInfohash() torrentInfohash := createTestInfohash()
torrentID := createTestTorrentID() torrentID := createTestTorrentID()
testSeeders := createTestPeers(torrentID, 4) testSeeders := createTestPeers(torrentID, 4)
testLeechers := createTestPeers(torrentID, 2) testLeechers := createTestPeers(torrentID, 2)
testTorrent := storage.Torrent{ID: torrentID, Infohash: torrentInfohash, Active: true, testTorrent := storage.Torrent{ID: torrentID, Infohash: torrentInfohash, Active: true,
Seeders: testSeeders, Leechers: testLeechers, Snatches: 11, UpMultiplier: 1.0, DownMultiplier: 1.0, LastAction: 0} Seeders: testSeeders, Leechers: testLeechers, Snatches: 11, UpMultiplier: 1.0, DownMultiplier: 1.0, LastAction: 0}
return &testTorrent return &testTorrent
} }
func TestValidPeers(t *testing.T) { func TestValidPeers(t *testing.T) {
testConn := createTestRedisConn() testConn := createTestRedisConn()
testTorrentID := createTestTorrentID() testTorrentID := createTestTorrentID()
testPeers := createTestPeers(testTorrentID, 3) testPeers := createTestPeers(testTorrentID, 3)
panicOnErr(testConn.addPeers(testPeers, "test:")) panicOnErr(testConn.addPeers(testPeers, "test:"))
peerMap, err := testConn.getPeers(testTorrentID, "test:") peerMap, err := testConn.getPeers(testTorrentID, "test:")
panicOnErr(err) panicOnErr(err)
if len(peerMap) != len(testPeers) { if len(peerMap) != len(testPeers) {
t.Error("Num Peers not equal ", len(peerMap), len(testPeers)) t.Error("Num Peers not equal ", len(peerMap), len(testPeers))
} }
panicOnErr(testConn.removePeers(testTorrentID, testPeers, "test:")) panicOnErr(testConn.removePeers(testTorrentID, testPeers, "test:"))
} }
func TestInvalidPeers(t *testing.T) { func TestInvalidPeers(t *testing.T) {
testConn := createTestRedisConn() testConn := createTestRedisConn()
testTorrentID := createTestTorrentID() testTorrentID := createTestTorrentID()
testPeers := createTestPeers(testTorrentID, 3) testPeers := createTestPeers(testTorrentID, 3)
tempPeer := createTestPeer(createTestUserID(), testTorrentID) tempPeer := createTestPeer(createTestUserID(), testTorrentID)
testPeers[storage.PeerMapKey(tempPeer)] = *tempPeer testPeers[storage.PeerMapKey(tempPeer)] = *tempPeer
panicOnErr(testConn.addPeers(testPeers, "test:")) panicOnErr(testConn.addPeers(testPeers, "test:"))
// Imitate a peer being removed during get // Imitate a peer being removed during get
hashKey := testConn.conf.Prefix + getPeerHashKey(tempPeer) hashKey := testConn.conf.Prefix + getPeerHashKey(tempPeer)
_, err := testConn.Do("DEL", hashKey) _, err := testConn.Do("DEL", hashKey)
panicOnErr(err) panicOnErr(err)
peerMap, err := testConn.getPeers(testTorrentID, "test:") peerMap, err := testConn.getPeers(testTorrentID, "test:")
panicOnErr(err) panicOnErr(err)
// Expect 1 less peer due to delete // Expect 1 less peer due to delete
if len(peerMap) != len(testPeers)-1 { if len(peerMap) != len(testPeers)-1 {
t.Error("Num Peers not equal ", len(peerMap), len(testPeers)-1) t.Error("Num Peers not equal ", len(peerMap), len(testPeers)-1)
} }
panicOnErr(testConn.removePeers(testTorrentID, testPeers, "test:")) panicOnErr(testConn.removePeers(testTorrentID, testPeers, "test:"))
if len(testPeers) != 0 { if len(testPeers) != 0 {
t.Errorf("All peers not removed, %d peers remain!", len(testPeers)) t.Errorf("All peers not removed, %d peers remain!", len(testPeers))
} }
} }