tracker/server/serve_announce.go

247 lines
5 KiB
Go
Raw Normal View History

// Copyright 2013 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.
2013-06-22 01:31:32 +02:00
package server
import (
2014-06-24 04:47:43 +02:00
"io"
"net"
2013-11-24 06:49:20 +01:00
"net/http"
"strconv"
2014-06-24 04:47:43 +02:00
log "github.com/golang/glog"
"github.com/chihaya/chihaya/bencode"
"github.com/chihaya/chihaya/drivers/tracker"
"github.com/chihaya/chihaya/models"
2013-06-22 01:31:32 +02:00
)
func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) {
2014-06-24 04:47:43 +02:00
announce, err := models.NewAnnounce(r, s.conf)
2013-11-24 06:49:20 +01:00
if err != nil {
fail(err, w, r)
return
}
2014-02-23 05:47:11 +01:00
conn, err := s.trackerPool.Get()
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
fail(err, w, r)
return
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
err = conn.ClientWhitelisted(announce.ClientID())
2013-11-24 06:49:20 +01:00
if err != nil {
fail(err, w, r)
return
}
2014-06-24 04:47:43 +02:00
var user *models.User
if s.conf.Private {
user, err = conn.FindUser(announce.Passkey)
if err != nil {
fail(err, w, r)
return
}
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
torrent, err := conn.FindTorrent(announce.Infohash)
if err != nil {
fail(err, w, r)
2013-11-24 06:49:20 +01:00
return
}
peer := models.NewPeer(announce, user, torrent)
2014-06-24 04:47:43 +02:00
created, err := updateTorrent(conn, announce, peer, torrent)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
fail(err, w, r)
2013-11-24 06:49:20 +01:00
return
}
snatched, err := handleEvent(conn, announce, peer, user, torrent)
2014-06-24 04:47:43 +02:00
if err != nil {
fail(err, w, r)
return
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
writeAnnounceResponse(w, announce, user, torrent)
2013-11-24 06:49:20 +01:00
delta := models.NewAnnounceDelta(announce, peer, user, torrent, created, snatched)
2014-06-24 04:47:43 +02:00
s.backendConn.RecordAnnounce(delta)
2013-11-24 06:49:20 +01:00
log.Infof("chihaya: handled announce from %s", announce.IP)
2014-06-24 04:47:43 +02:00
}
func updateTorrent(c tracker.Conn, a *models.Announce, p *models.Peer, t *models.Torrent) (created bool, err error) {
2014-06-24 04:47:43 +02:00
if !t.Active && a.Left == 0 {
err = c.MarkActive(t)
2014-06-24 04:47:43 +02:00
if err != nil {
return
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
}
2013-11-24 06:49:20 +01:00
2014-06-24 04:47:43 +02:00
switch {
case t.InSeederPool(p):
err = c.SetSeeder(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
case t.InLeecherPool(p):
err = c.SetLeecher(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
default:
2014-06-24 04:47:43 +02:00
if a.Left == 0 {
err = c.AddSeeder(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
} else {
err = c.AddLeecher(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
created = true
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
return
}
func handleEvent(c tracker.Conn, a *models.Announce, p *models.Peer, u *models.User, t *models.Torrent) (snatched bool, err error) {
2013-11-24 06:49:20 +01:00
switch {
2014-06-24 04:47:43 +02:00
case a.Event == "stopped" || a.Event == "paused":
if t.InSeederPool(p) {
err = c.RemoveSeeder(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
if t.InLeecherPool(p) {
err = c.RemoveLeecher(t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
case a.Event == "completed":
err = c.IncrementSnatches(t)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
snatched = true
if t.InLeecherPool(p) {
err = tracker.LeecherFinished(c, t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
case t.InLeecherPool(p) && a.Left == 0:
2013-11-24 06:49:20 +01:00
// A leecher completed but the event was never received
err = tracker.LeecherFinished(c, t, p)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-06-24 04:47:43 +02:00
return
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
return
}
2013-11-24 06:49:20 +01:00
2014-06-24 04:47:43 +02:00
func writeAnnounceResponse(w io.Writer, a *models.Announce, u *models.User, t *models.Torrent) {
bencoder := bencode.NewEncoder(w)
seedCount := len(t.Seeders)
leechCount := len(t.Leechers)
bencoder.Encode("d")
bencoder.Encode("complete")
bencoder.Encode(seedCount)
bencoder.Encode("incomplete")
bencoder.Encode(leechCount)
bencoder.Encode("interval")
bencoder.Encode(a.Config.Announce.Duration)
bencoder.Encode("min interval")
bencoder.Encode(a.Config.MinAnnounce.Duration)
if a.NumWant > 0 && a.Event != "stopped" && a.Event != "paused" {
bencoder.Encode("peers")
var peerCount int
if a.Compact {
if a.Left == 0 {
peerCount = minInt(a.NumWant, leechCount)
2013-11-24 06:49:20 +01:00
} else {
2014-06-24 04:47:43 +02:00
peerCount = minInt(a.NumWant, leechCount+seedCount-1)
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
// 6 is the number of bytes 1 compact peer takes up.
bencoder.Encode(strconv.Itoa(peerCount * 6))
bencoder.Encode(":")
2013-11-24 06:49:20 +01:00
} else {
2014-06-24 04:47:43 +02:00
bencoder.Encode("l")
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
var count int
if a.Left == 0 {
2013-11-24 06:49:20 +01:00
// If they're seeding, give them only leechers
2014-06-24 04:47:43 +02:00
count = writePeers(w, u, t.Leechers, a.NumWant, a.Compact)
2013-11-24 06:49:20 +01:00
} else {
// If they're leeching, prioritize giving them seeders
2014-06-24 04:47:43 +02:00
count += writePeers(w, u, t.Seeders, a.NumWant, a.Compact)
count += writePeers(w, u, t.Leechers, a.NumWant-count, a.Compact)
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
if a.Compact && peerCount != count {
log.Errorf("calculated peer count (%d) != real count (%d)", peerCount, count)
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
if !a.Compact {
bencoder.Encode("e")
2013-11-24 06:49:20 +01:00
}
}
2014-06-24 04:47:43 +02:00
bencoder.Encode("e")
2013-08-04 21:56:31 +02:00
}
2014-06-24 04:47:43 +02:00
func writePeers(w io.Writer, user *models.User, peers map[string]models.Peer, numWant int, compact bool) (count int) {
bencoder := bencode.NewEncoder(w)
for _, peer := range peers {
2013-11-24 06:49:20 +01:00
if count >= numWant {
break
}
2014-05-08 07:09:02 +02:00
if peer.UserID == user.ID {
continue
}
2014-05-08 07:09:02 +02:00
2013-11-24 06:49:20 +01:00
if compact {
2014-06-24 04:47:43 +02:00
if ip := net.ParseIP(peer.IP); ip != nil {
w.Write(ip)
w.Write([]byte{byte(peer.Port >> 8), byte(peer.Port & 0xff)})
}
2013-11-24 06:49:20 +01:00
} else {
2014-06-24 04:47:43 +02:00
bencoder.Encode("d")
bencoder.Encode("ip")
bencoder.Encode(peer.IP)
bencoder.Encode("peer id")
bencoder.Encode(peer.ID)
bencoder.Encode("port")
bencoder.Encode(peer.Port)
bencoder.Encode("e")
2013-11-24 06:49:20 +01:00
}
count++
}
2014-05-08 07:09:02 +02:00
2014-06-24 04:47:43 +02:00
return
2013-08-04 21:56:31 +02:00
}
2014-06-24 04:47:43 +02:00
func minInt(a, b int) int {
if a < b {
return a
2013-11-24 06:49:20 +01:00
}
2014-05-08 07:09:02 +02:00
2014-06-24 04:47:43 +02:00
return b
2013-07-12 06:36:24 +02:00
}