mirror of
https://github.com/meehow/privtracker.git
synced 2026-05-06 23:34:37 +08:00
use constant size arrays for storage functions
This commit is contained in:
3
Makefile
3
Makefile
@@ -3,3 +3,6 @@ build:
|
||||
|
||||
deploy: build
|
||||
rsync -avzL --exclude '*.fiber.gz' docs privtracker privtracker:web/
|
||||
|
||||
test:
|
||||
go test -bench . -benchmem
|
||||
|
||||
27
announce.go
27
announce.go
@@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jackpal/bencode-go"
|
||||
@@ -40,29 +42,32 @@ func announce(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
room := c.Params("room")
|
||||
ip := net.ParseIP(c.IP())
|
||||
if ip == nil {
|
||||
ip = c.Context().RemoteIP()
|
||||
}
|
||||
if req.Numwant == 0 {
|
||||
if req.Numwant < 1 {
|
||||
req.Numwant = 30
|
||||
}
|
||||
swarmHash := sha1.Sum([]byte(c.Params("room") + req.InfoHash))
|
||||
peer := NewPeer(ip, req.Port)
|
||||
switch req.Event {
|
||||
case "stopped":
|
||||
DeletePeer(room, req.InfoHash, peer)
|
||||
DeletePeer(swarmHash, peer)
|
||||
case "completed":
|
||||
GraduateLeecher(room, req.InfoHash, peer)
|
||||
GraduateLeecher(swarmHash, peer)
|
||||
default:
|
||||
PutPeer(room, req.InfoHash, peer, req.IsSeeding())
|
||||
PutPeer(swarmHash, peer, req.IsSeeding())
|
||||
}
|
||||
peersIPv4, peersIPv6, numSeeders, numLeechers := GetPeers(room, req.InfoHash, peer, req.IsSeeding(), req.Numwant)
|
||||
interval := 120
|
||||
if numSeeders == 0 {
|
||||
interval /= 2
|
||||
} else if numLeechers == 0 {
|
||||
interval *= 4
|
||||
peersIPv4, peersIPv6, numSeeders, numLeechers := GetPeers(swarmHash, peer, req.IsSeeding(), req.Numwant)
|
||||
interval := int(time.Now().Unix()+int64(swarmHash[0]))%256 + 60
|
||||
switch {
|
||||
// case numSeeders == 0:
|
||||
// interval -= 30
|
||||
case numLeechers == 0:
|
||||
interval += 240
|
||||
case numSeeders+numLeechers > 10:
|
||||
interval += 480
|
||||
}
|
||||
resp := AnnounceResponse{
|
||||
Interval: interval,
|
||||
|
||||
19
main.go
19
main.go
@@ -16,20 +16,21 @@ import (
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
var port = os.Getenv("PORT")
|
||||
|
||||
func main() {
|
||||
port := os.Getenv("PORT")
|
||||
tlsEnabled := port == "443"
|
||||
if port == "" {
|
||||
port = "1337"
|
||||
}
|
||||
config := fiber.Config{
|
||||
AppName: "PrivTracker",
|
||||
ServerHeader: "PrivTracker",
|
||||
ReadTimeout: time.Second * 245,
|
||||
WriteTimeout: time.Second * 30,
|
||||
Network: fiber.NetworkTCP,
|
||||
GETOnly: true,
|
||||
AppName: "PrivTracker",
|
||||
ServerHeader: "PrivTracker",
|
||||
ReadTimeout: time.Second * 245,
|
||||
WriteTimeout: time.Second * 30,
|
||||
Network: fiber.NetworkTCP,
|
||||
GETOnly: true,
|
||||
DisableKeepalive: true,
|
||||
Immutable: true,
|
||||
}
|
||||
// if you disable TLS, then I guess you want to use existing proxy
|
||||
if !tlsEnabled {
|
||||
@@ -38,7 +39,7 @@ func main() {
|
||||
config.ProxyHeader = fiber.HeaderXForwardedFor
|
||||
}
|
||||
|
||||
go Cleanup(time.Second * 600)
|
||||
go Cleanup(time.Minute * 16)
|
||||
|
||||
app := fiber.New(config)
|
||||
app.Use(recover.New())
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jackpal/bencode-go"
|
||||
)
|
||||
@@ -25,7 +27,8 @@ func scrape(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
numSeeders, numLeechers := GetStats(c.Params("room"), req.InfoHash)
|
||||
swarmHash := sha1.Sum([]byte(c.Params("room") + req.InfoHash))
|
||||
numSeeders, numLeechers := GetStats(swarmHash)
|
||||
resp := ScrapeResponse{
|
||||
Files: map[string]Stat{
|
||||
req.InfoHash: {
|
||||
|
||||
59
storage.go
59
storage.go
@@ -5,24 +5,18 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Hash [20]byte // we use sha1 and we are not affraid of hash collisions
|
||||
type Peer [18]byte // 16 bytes for IP and 2 bytes for port number
|
||||
type Hash [sha1.Size]byte // we use sha1 and we are not affraid of hash collisions
|
||||
type Peer [18]byte // 16 bytes for IP and 2 bytes for port number
|
||||
|
||||
func NewPeer(ip net.IP, port uint16) (peer Peer) {
|
||||
copy(peer[:], ip)
|
||||
peer[16] = byte(port >> 8)
|
||||
peer[17] = byte(port)
|
||||
return
|
||||
}
|
||||
|
||||
func (peer Peer) String() string {
|
||||
return fmt.Sprintf("%s:%d", net.IP(peer[:16]), binary.BigEndian.Uint16(peer[16:]))
|
||||
}
|
||||
var shards = NewShards(256)
|
||||
var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
|
||||
|
||||
type shard struct {
|
||||
swarms map[Hash]Swarm
|
||||
@@ -41,10 +35,19 @@ func NewSwarm() Swarm {
|
||||
}
|
||||
}
|
||||
|
||||
var shards = NewShards(512)
|
||||
var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
|
||||
func NewPeer(ip net.IP, port uint16) (peer Peer) {
|
||||
copy(peer[:16], ip)
|
||||
binary.BigEndian.PutUint16(peer[16:18], port)
|
||||
return
|
||||
}
|
||||
|
||||
func shardIndex(hash [20]byte) int {
|
||||
func (peer Peer) String() string {
|
||||
ip := net.IP(peer[:16])
|
||||
port := binary.BigEndian.Uint16(peer[16:18])
|
||||
return fmt.Sprintf("%s:%d", ip, port)
|
||||
}
|
||||
|
||||
func shardIndex(hash Hash) int {
|
||||
return int(binary.BigEndian.Uint16(hash[:2])) % len(shards)
|
||||
}
|
||||
|
||||
@@ -58,8 +61,7 @@ func NewShards(size int) []*shard {
|
||||
return shards
|
||||
}
|
||||
|
||||
func PutPeer(room, infoHash string, peer Peer, seeding bool) {
|
||||
h := sha1.Sum([]byte(room + infoHash))
|
||||
func PutPeer(h Hash, peer Peer, seeding bool) {
|
||||
shard := shards[shardIndex(h)]
|
||||
shard.Lock()
|
||||
if _, ok := shard.swarms[h]; !ok {
|
||||
@@ -73,8 +75,7 @@ func PutPeer(room, infoHash string, peer Peer, seeding bool) {
|
||||
shard.Unlock()
|
||||
}
|
||||
|
||||
func DeletePeer(room, infoHash string, peer Peer) {
|
||||
h := sha1.Sum([]byte(room + infoHash))
|
||||
func DeletePeer(h Hash, peer Peer) {
|
||||
shard := shards[shardIndex(h)]
|
||||
shard.Lock()
|
||||
if _, ok := shard.swarms[h]; !ok {
|
||||
@@ -85,8 +86,7 @@ func DeletePeer(room, infoHash string, peer Peer) {
|
||||
shard.Unlock()
|
||||
}
|
||||
|
||||
func GraduateLeecher(room, infoHash string, peer Peer) {
|
||||
h := sha1.Sum([]byte(room + infoHash))
|
||||
func GraduateLeecher(h Hash, peer Peer) {
|
||||
shard := shards[shardIndex(h)]
|
||||
shard.Lock()
|
||||
if _, ok := shard.swarms[h]; !ok {
|
||||
@@ -97,10 +97,10 @@ func GraduateLeecher(room, infoHash string, peer Peer) {
|
||||
shard.Unlock()
|
||||
}
|
||||
|
||||
func GetPeers(room, infoHash string, client Peer, seeding bool, numWant uint) (peersIPv4, peersIPv6 []byte, numSeeders, numLeechers int) {
|
||||
h := sha1.Sum([]byte(room + infoHash))
|
||||
func GetPeers(h Hash, client Peer, seeding bool, numWant uint) (peersIPv4, peersIPv6 []byte, numSeeders, numLeechers int) {
|
||||
shard := shards[shardIndex(h)]
|
||||
shard.RLock()
|
||||
|
||||
// seeders don't need other seeders
|
||||
if !seeding {
|
||||
for peer := range shard.swarms[h].seeders {
|
||||
@@ -135,8 +135,7 @@ func GetPeers(room, infoHash string, client Peer, seeding bool, numWant uint) (p
|
||||
return
|
||||
}
|
||||
|
||||
func GetStats(room, infoHash string) (numSeeders, numLeechers int) {
|
||||
h := sha1.Sum([]byte(room + infoHash))
|
||||
func GetStats(h Hash) (numSeeders, numLeechers int) {
|
||||
shard := shards[shardIndex(h)]
|
||||
shard.RLock()
|
||||
numSeeders = len(shard.swarms[h].seeders)
|
||||
@@ -148,25 +147,35 @@ func GetStats(room, infoHash string) (numSeeders, numLeechers int) {
|
||||
func Cleanup(duration time.Duration) {
|
||||
ticker := time.NewTicker(duration)
|
||||
for range ticker.C {
|
||||
var seeders, leechers, swarms, seedersDeleted, leechersDeleted, swarmsDeleted int
|
||||
expiration := time.Now().Unix() - int64(duration.Seconds())
|
||||
for _, shard := range shards {
|
||||
shard.Lock()
|
||||
swarms += len(shard.swarms)
|
||||
for h, swarm := range shard.swarms {
|
||||
seeders += len(swarm.seeders)
|
||||
leechers += len(swarm.leechers)
|
||||
for peer, lastSeen := range swarm.seeders {
|
||||
if lastSeen < expiration {
|
||||
seedersDeleted++
|
||||
delete(swarm.seeders, peer)
|
||||
}
|
||||
}
|
||||
for peer, lastSeen := range swarm.leechers {
|
||||
if lastSeen < expiration {
|
||||
leechersDeleted++
|
||||
delete(swarm.leechers, peer)
|
||||
}
|
||||
}
|
||||
if len(swarm.leechers) == 0 && len(swarm.seeders) == 0 {
|
||||
swarmsDeleted++
|
||||
delete(shard.swarms, h)
|
||||
}
|
||||
}
|
||||
shard.Unlock()
|
||||
}
|
||||
log.Printf("seeders: %d (%d deleted), leechers: %d (%d deleted), swarms: %d (%d deleted)",
|
||||
seeders, seedersDeleted, leechers, leechersDeleted, swarms, swarmsDeleted)
|
||||
runtime.GC()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkPutPeerGetPeers(b *testing.B) {
|
||||
peer := NewPeer(net.ParseIP("127.0.0.1"), 6881)
|
||||
var swarmHash Hash
|
||||
for i := 0; i < b.N; i++ {
|
||||
PutPeer("room", "infoHash", peer, true)
|
||||
GetPeers("room", "infoHash", peer, true, 99)
|
||||
for j := 0; j < 100; j++ {
|
||||
rand.Read(swarmHash[:])
|
||||
for k := 0; k < 10; k++ {
|
||||
peer := NewPeer(net.ParseIP("127.0.0.1"), uint16(mrand.Uint32()))
|
||||
|
||||
PutPeer(swarmHash, peer, true)
|
||||
GetPeers(swarmHash, peer, true, 99)
|
||||
|
||||
GraduateLeecher(swarmHash, peer)
|
||||
GetPeers(swarmHash, peer, true, 99)
|
||||
|
||||
DeletePeer(swarmHash, peer)
|
||||
GetPeers(swarmHash, peer, true, 99)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user