diff --git a/config/config.go b/config/config.go index fcd37a4..bbbb333 100644 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ // Use of this source code is governed by the BSD 2-Clause license, // which can be found in the LICENSE file. +// Package config implements the configuration for a BitTorrent tracker package config import ( diff --git a/server/announce.go b/server/announce.go index e01d1af..9afb9fa 100644 --- a/server/announce.go +++ b/server/announce.go @@ -14,32 +14,32 @@ import ( func (s *Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { passkey, _ := path.Split(r.URL.Path) - user, err := validatePasskey(passkey, s.storage) + _, err := validatePasskey(passkey, s.storage) if err != nil { - fail(err, w) + fail(err, w, r) return } pq, err := parseQuery(r.URL.RawQuery) if err != nil { - fail(errors.New("Error parsing query"), w) + fail(errors.New("Error parsing query"), w, r) return } - ip, err := pq.determineIP(r) + _, err = pq.determineIP(r) if err != nil { - fail(err, w) + fail(err, w, r) return } err = pq.validate() if err != nil { - fail(errors.New("Malformed request"), w) + fail(errors.New("Malformed request"), w, r) return } if !s.conf.Whitelisted(pq.params["peerId"]) { - fail(errors.New("Your client is not approved"), w) + fail(errors.New("Your client is not approved"), w, r) return } @@ -48,7 +48,7 @@ func (s *Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { log.Panicf("server: %s", err) } if !exists { - fail(errors.New("This torrent does not exist"), w) + fail(errors.New("This torrent does not exist"), w, r) return } @@ -66,6 +66,7 @@ func (s *Server) serveAnnounce(w http.ResponseWriter, r *http.Request) { left, ), w, + r, ) return } diff --git a/server/query.go b/server/query.go index 61fc5bf..5429dc9 100644 --- a/server/query.go +++ b/server/query.go @@ -97,27 +97,27 @@ func parseQuery(query string) (*parsedQuery, error) { } func (pq *parsedQuery) validate() error { - infohash, ok := pq.params["info_hash"] + infohash, _ := pq.params["info_hash"] if infohash == "" { return errors.New("infohash does not exist") } - peerId, ok := pq.params["peer_id"] + peerId, _ := pq.params["peer_id"] if peerId == "" { return errors.New("peerId does not exist") } - port, ok := pq.getUint64("port") + _, ok := pq.getUint64("port") if ok == false { return errors.New("port does not exist") } - uploaded, ok := pq.getUint64("uploaded") + _, ok = pq.getUint64("uploaded") if ok == false { return errors.New("uploaded does not exist") } - downloaded, ok := pq.getUint64("downloaded") + _, ok = pq.getUint64("downloaded") if ok == false { return errors.New("downloaded does not exist") } - left, ok := pq.getUint64("left") + _, ok = pq.getUint64("left") if ok == false { return errors.New("left does not exist") } diff --git a/server/scrape.go b/server/scrape.go index 280483a..2312c87 100644 --- a/server/scrape.go +++ b/server/scrape.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "log" "net/http" "path" "strconv" @@ -20,13 +21,13 @@ func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) { passkey, _ := path.Split(r.URL.Path) _, err := validatePasskey(passkey, s.storage) if err != nil { - fail(err, w) + fail(err, w, r) return } pq, err := parseQuery(r.URL.RawQuery) if err != nil { - fail(errors.New("Error parsing query"), w) + fail(errors.New("Error parsing query"), w, r) return } @@ -36,7 +37,7 @@ func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) { for _, infohash := range pq.infohashes { torrent, exists, err := s.storage.FindTorrent(infohash) if err != nil { - panic("server: failed to find torrent") + log.Panicf("server: %s", err) } if exists { bencode(w, infohash) @@ -46,7 +47,7 @@ func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) { } else if infohash, exists := pq.params["info_hash"]; exists { torrent, exists, err := s.storage.FindTorrent(infohash) if err != nil { - panic("server: failed to find torrent") + log.Panicf("server: %s", err) } if exists { bencode(w, infohash) @@ -54,7 +55,11 @@ func (s *Server) serveScrape(w http.ResponseWriter, r *http.Request) { } } io.WriteString(w, "e") - finalizeResponse(w, r) + + r.Close = true + w.Header().Add("Content-Type", "text/plain") + w.Header().Add("Connection", "close") + w.(http.Flusher).Flush() } func writeScrapeInfo(w io.Writer, torrent *storage.Torrent) { @@ -69,6 +74,7 @@ func writeScrapeInfo(w io.Writer, torrent *storage.Torrent) { } func bencode(w io.Writer, data interface{}) { + // A massive switch is faster than reflection switch v := data.(type) { case string: str := fmt.Sprintf("%s:%s", strconv.Itoa(len(v)), v) diff --git a/server/server.go b/server/server.go index d93f1c1..457bf6a 100644 --- a/server/server.go +++ b/server/server.go @@ -2,6 +2,7 @@ // Use of this source code is governed by the BSD 2-Clause license, // which can be found in the LICENSE file. +// Package server implements a BitTorrent tracker package server import ( @@ -90,7 +91,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.waitgroup.Add(1) defer s.waitgroup.Done() defer atomic.AddInt64(&s.deltaRequests, 1) - defer finalizeResponse(w, r) if r.URL.Path == "/stats" { s.serveStats(w, r) @@ -106,19 +106,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.serveScrape(w, r) return default: - fail(errors.New("Unknown action"), w) + fail(errors.New("Unknown action"), w, r) return } } -func finalizeResponse(w http.ResponseWriter, r *http.Request) { - r.Close = true - w.Header().Add("Content-Type", "text/plain") - w.Header().Add("Connection", "close") - w.(http.Flusher).Flush() -} - -func fail(err error, w http.ResponseWriter) { +func fail(err error, w http.ResponseWriter, r *http.Request) { errmsg := err.Error() message := fmt.Sprintf( "%s%s%s%s%s", @@ -128,7 +121,12 @@ func fail(err error, w http.ResponseWriter) { errmsg, "e", ) - io.WriteString(w, message) + length, _ := io.WriteString(w, message) + r.Close = true + w.Header().Add("Content-Type", "text/plain") + w.Header().Add("Content-Length", string(length)) + w.Header().Add("Connection", "close") + w.(http.Flusher).Flush() } func validatePasskey(dir string, s storage.Storage) (*storage.User, error) { diff --git a/server/stats.go b/server/stats.go index 56f5f70..0bcd8a7 100644 --- a/server/stats.go +++ b/server/stats.go @@ -23,7 +23,12 @@ func (s *Server) serveStats(w http.ResponseWriter, r *http.Request) { config.Duration{time.Now().Sub(s.startTime)}, s.rpm, }) - w.Write(stats) + + length, _ := w.Write(stats) + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Length", string(length)) + w.Header().Set("Connection", "close") + w.(http.Flusher).Flush() } func (s *Server) updateRPM() { diff --git a/storage/storage.go b/storage/storage.go index 76ff92c..600d591 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -2,6 +2,8 @@ // Use of this source code is governed by the BSD 2-Clause license, // which can be found in the LICENSE file. +// Package storage provides a generic interface for manipulating a +// BitTorrent tracker's data store. package storage import (