tracker/http/announce.go

316 lines
6.8 KiB
Go
Raw Normal View History

// Copyright 2014 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.
2014-07-02 03:40:29 +02:00
package http
2013-06-22 01:31:32 +02:00
import (
"bytes"
2013-11-24 06:49:20 +01:00
"net/http"
2014-07-02 03:40:29 +02:00
"github.com/julienschmidt/httprouter"
2014-06-24 04:47:43 +02:00
"github.com/chihaya/bencode"
2014-06-24 04:47:43 +02:00
"github.com/chihaya/chihaya/drivers/tracker"
"github.com/chihaya/chihaya/models"
2013-06-22 01:31:32 +02:00
)
2014-07-09 06:53:57 +02:00
func (t *Tracker) ServeAnnounce(w http.ResponseWriter, r *http.Request, p httprouter.Params) (int, error) {
2014-07-02 03:40:29 +02:00
ann, err := models.NewAnnounce(t.cfg, r, p)
if err == models.ErrMalformedRequest {
2014-07-02 03:40:29 +02:00
fail(w, r, err)
2014-07-09 06:53:57 +02:00
return http.StatusOK, nil
2014-07-07 05:35:37 +02:00
} else if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
}
2013-11-24 06:49:20 +01:00
conn, err := t.pool.Get()
2013-11-24 06:49:20 +01:00
if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2013-11-24 06:49:20 +01:00
}
2014-07-02 03:40:29 +02:00
if t.cfg.Whitelist {
2014-07-16 19:03:59 +02:00
err = conn.FindClient(ann.ClientID())
if err == tracker.ErrClientUnapproved {
2014-07-02 03:40:29 +02:00
fail(w, r, err)
2014-07-09 06:53:57 +02:00
return http.StatusOK, nil
} else if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2014-07-02 03:40:29 +02:00
}
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
var user *models.User
2014-07-02 03:40:29 +02:00
if t.cfg.Private {
2014-07-16 19:03:59 +02:00
user, err = conn.FindUser(ann.Passkey)
if err == tracker.ErrUserDNE {
2014-07-02 03:40:29 +02:00
fail(w, r, err)
2014-07-09 06:53:57 +02:00
return http.StatusOK, nil
} else if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2014-06-24 04:47:43 +02:00
}
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
var torrent *models.Torrent
torrent, err = conn.FindTorrent(ann.Infohash)
switch {
2014-07-12 02:44:28 +02:00
case !t.cfg.Private && err == tracker.ErrTorrentDNE:
torrent = &models.Torrent{
Infohash: ann.Infohash,
Seeders: make(map[string]models.Peer),
Leechers: make(map[string]models.Peer),
}
err = conn.PutTorrent(torrent)
if err != nil {
return http.StatusInternalServerError, err
}
2014-07-12 02:44:28 +02:00
case t.cfg.Private && err == tracker.ErrTorrentDNE:
2014-07-02 03:40:29 +02:00
fail(w, r, err)
2014-07-09 06:53:57 +02:00
return http.StatusOK, nil
case err != nil:
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2013-11-24 06:49:20 +01:00
}
2014-07-02 03:40:29 +02:00
peer := models.NewPeer(ann, user, torrent)
2014-06-24 04:47:43 +02:00
2014-07-02 03:40:29 +02:00
created, err := updateTorrent(conn, ann, peer, torrent)
2013-11-24 06:49:20 +01:00
if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2013-11-24 06:49:20 +01:00
}
2014-07-02 03:40:29 +02:00
snatched, err := handleEvent(conn, ann, peer, user, torrent)
2014-06-24 04:47:43 +02:00
if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2013-11-24 06:49:20 +01:00
}
2014-07-02 03:40:29 +02:00
if t.cfg.Private {
delta := models.NewAnnounceDelta(ann, peer, user, torrent, created, snatched)
err = t.backend.RecordAnnounce(delta)
2014-07-02 03:40:29 +02:00
if err != nil {
2014-07-09 06:53:57 +02:00
return http.StatusInternalServerError, err
2014-07-02 03:40:29 +02:00
}
2014-06-26 20:26:58 +02:00
}
2013-11-24 06:49:20 +01:00
resp := newAnnounceResponse(ann, user, torrent)
bencoder := bencode.NewEncoder(w)
err = bencoder.Encode(resp)
if err != nil {
return http.StatusInternalServerError, err
}
2014-06-26 23:10:39 +02:00
2014-07-09 06:53:57 +02:00
return http.StatusOK, nil
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 {
2014-07-08 11:58:00 +02:00
err = c.MarkActive(t.Infohash)
2014-06-24 04:47:43 +02:00
if err != nil {
return
2013-11-24 06:49:20 +01:00
}
2014-07-08 11:58:00 +02:00
t.Active = true
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):
2014-07-08 11:58:00 +02:00
err = c.PutSeeder(t.Infohash, 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-07-08 11:58:00 +02:00
t.Seeders[p.Key()] = *p
2013-11-24 06:49:20 +01:00
2014-06-24 04:47:43 +02:00
case t.InLeecherPool(p):
2014-07-08 11:58:00 +02:00
err = c.PutLeecher(t.Infohash, 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-07-08 11:58:00 +02:00
t.Leechers[p.Key()] = *p
2013-11-24 06:49:20 +01:00
default:
2014-06-24 04:47:43 +02:00
if a.Left == 0 {
2014-07-08 11:58:00 +02:00
err = c.PutSeeder(t.Infohash, 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-07-08 11:58:00 +02:00
t.Seeders[p.Key()] = *p
2013-11-24 06:49:20 +01:00
} else {
2014-07-08 11:58:00 +02:00
err = c.PutLeecher(t.Infohash, 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-07-08 11:58:00 +02:00
t.Leechers[p.Key()] = *p
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) {
2014-07-08 11:58:00 +02:00
err = c.DeleteSeeder(t.Infohash, p.Key())
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-07-08 11:58:00 +02:00
delete(t.Seeders, p.Key())
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
if t.InLeecherPool(p) {
2014-07-08 11:58:00 +02:00
err = c.DeleteLeecher(t.Infohash, p.Key())
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-07-08 11:58:00 +02:00
delete(t.Leechers, p.Key())
2013-11-24 06:49:20 +01:00
}
2014-06-24 04:47:43 +02:00
case a.Event == "completed":
2014-07-08 11:58:00 +02:00
err = c.IncrementSnatches(t.Infohash)
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
2014-07-08 11:58:00 +02:00
t.Snatches++
2014-06-24 04:47:43 +02:00
if t.InLeecherPool(p) {
2014-07-08 11:58:00 +02:00
err = tracker.LeecherFinished(c, t.Infohash, 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:
2014-07-07 05:35:37 +02:00
// A leecher completed but the event was never received.
2014-07-08 11:58:00 +02:00
err = tracker.LeecherFinished(c, t.Infohash, 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
func newAnnounceResponse(a *models.Announce, u *models.User, t *models.Torrent) bencode.Dict {
2014-06-24 04:47:43 +02:00
seedCount := len(t.Seeders)
leechCount := len(t.Leechers)
var peerCount int
if a.Left == 0 {
peerCount = minInt(a.NumWant, leechCount)
} else {
peerCount = minInt(a.NumWant, leechCount+seedCount-1)
}
resp := bencode.NewDict()
resp["complete"] = seedCount
resp["incomplete"] = leechCount
resp["interval"] = a.Config.Announce.Duration
resp["min interval"] = a.Config.MinAnnounce.Duration
2014-06-24 04:47:43 +02:00
if a.NumWant > 0 && a.Event != "stopped" && a.Event != "paused" {
ipv4s, ipv6s := getPeers(a, u, t, peerCount)
if a.Compact {
resp["peers"] = compactPeers("ipv4", ipv4s)
resp["peers6"] = compactPeers("ipv6", ipv6s)
} else {
resp["peers"] = peersList(ipv4s, ipv6s)
}
}
return resp
}
func compactPeers(ipv string, peers []models.Peer) []byte {
var compactPeers bytes.Buffer
2014-06-30 20:59:31 +02:00
switch ipv {
case "ipv4":
for _, peer := range peers {
if ip := peer.IP.To4(); ip != nil {
compactPeers.Write(ip)
compactPeers.Write([]byte{byte(peer.Port >> 8), byte(peer.Port & 0xff)})
}
}
2014-06-24 04:47:43 +02:00
case "ipv6":
for _, peer := range peers {
if ip := peer.IP.To16(); ip != nil {
compactPeers.Write(ip)
compactPeers.Write([]byte{byte(peer.Port >> 8), byte(peer.Port & 0xff)})
}
2014-06-26 23:10:39 +02:00
}
}
return compactPeers.Bytes()
}
2014-06-26 23:10:39 +02:00
2014-07-02 03:40:29 +02:00
func getPeers(a *models.Announce, u *models.User, t *models.Torrent, peerCount int) (ipv4s, ipv6s []models.Peer) {
if a.Left == 0 {
// If they're seeding, give them only leechers.
2014-06-30 20:59:31 +02:00
splitPeers(&ipv4s, &ipv6s, a, u, t.Leechers, peerCount)
} else {
// If they're leeching, prioritize giving them seeders.
2014-06-30 20:59:31 +02:00
count := splitPeers(&ipv4s, &ipv6s, a, u, t.Seeders, peerCount)
splitPeers(&ipv4s, &ipv6s, a, u, t.Leechers, peerCount-count)
}
return
}
2014-07-02 03:40:29 +02:00
func splitPeers(ipv4s, ipv6s *[]models.Peer, a *models.Announce, u *models.User, peers map[string]models.Peer, peerCount int) (count int) {
for _, peer := range peers {
if count >= peerCount {
break
2013-11-24 06:49:20 +01:00
}
2014-06-30 20:59:31 +02:00
if a.Config.Private && peer.UserID == u.ID {
continue
2013-11-24 06:49:20 +01:00
}
2014-06-30 20:59:31 +02:00
if ip := peer.IP.To4(); len(ip) == 4 {
2014-07-02 03:40:29 +02:00
*ipv4s = append(*ipv4s, peer)
2014-06-30 20:59:31 +02:00
} else if ip := peer.IP.To16(); len(ip) == 16 {
2014-07-02 03:40:29 +02:00
*ipv6s = append(*ipv6s, peer)
2013-11-24 06:49:20 +01:00
}
count++
}
return
}
func peersList(ipv4s, ipv6s []models.Peer) []bencode.Dict {
var peers []bencode.Dict
2014-06-30 20:59:31 +02:00
for _, peer := range ipv4s {
pd := peerDict(&peer)
peers = append(peers, pd)
2014-06-30 20:59:31 +02:00
}
2014-06-30 20:59:31 +02:00
for _, peer := range ipv6s {
pd := peerDict(&peer)
peers = append(peers, pd)
2013-11-24 06:49:20 +01:00
}
return peers
2013-08-04 21:56:31 +02:00
}
func peerDict(peer *models.Peer) bencode.Dict {
pd := bencode.NewDict()
pd["ip"] = peer.IP.String()
pd["peer id"] = peer.ID
pd["port"] = peer.Port
return pd
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
}