// makeHandler wraps our ResponseHandlers while timing requests, collecting, // stats, logging, and handling errors. func makeHandler(handler ResponseHandler) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { start := time.Now() httpCode, err := handler(w, r, p) duration := time.Since(start) var msg string if err != nil { msg = err.Error() } else if httpCode != http.StatusOK { msg = http.StatusText(httpCode) } if len(msg) > 0 { http.Error(w, msg, httpCode) stats.RecordEvent(stats.ErroredRequest) } if len(msg) > 0 || glog.V(2) { reqString := r.URL.Path + " " + r.RemoteAddr if glog.V(3) { reqString = r.URL.RequestURI() + " " + r.RemoteAddr } if len(msg) > 0 { glog.Errorf("[API - %9s] %s (%d - %s)", duration, reqString, httpCode, msg) } else { glog.Infof("[API - %9s] %s (%d)", duration, reqString, httpCode) } } stats.RecordEvent(stats.HandledRequest) stats.RecordTiming(stats.ResponseTime, duration) } }
func handleError(err error) (int, error) { if err == nil { return http.StatusOK, nil } else if _, ok := err.(models.NotFoundError); ok { stats.RecordEvent(stats.ClientError) return http.StatusNotFound, nil } else if _, ok := err.(models.ClientError); ok { stats.RecordEvent(stats.ClientError) return http.StatusBadRequest, nil } return http.StatusInternalServerError, err }
// connState is used by graceful in order to gracefully shutdown. It also // keeps track of connection stats. func (s *Server) connState(conn net.Conn, state http.ConnState) { switch state { case http.StateNew: stats.RecordEvent(stats.AcceptedConnection) case http.StateClosed: stats.RecordEvent(stats.ClosedConnection) case http.StateHijacked: panic("connection impossibly hijacked") // Ignore the following cases. case http.StateActive, http.StateIdle: default: glog.Errorf("Connection transitioned to unknown state %s (%d)", state, state) } }
func handleTorrentError(err error, w *Writer) (int, error) { if err == nil { return http.StatusOK, nil } else if models.IsPublicError(err) { w.WriteError(err) stats.RecordEvent(stats.ClientError) return http.StatusOK, nil } return http.StatusInternalServerError, err }
func (s *Storage) PurgeInactivePeers(purgeEmptyTorrents bool, before time.Time) error { unixtime := before.Unix() // Build a list of keys to process. index := 0 maxkeys := s.Len() keys := make([]string, maxkeys) for i := range s.shards { shard := &s.shards[i] shard.RLock() for infohash := range shard.torrents { keys[index] = infohash index++ if index >= maxkeys { break } } shard.RUnlock() if index >= maxkeys { break } } // Process the keys while allowing other goroutines to run. for _, infohash := range keys { runtime.Gosched() shard := s.getTorrentShard(infohash, false) torrent := shard.torrents[infohash] if torrent == nil { // The torrent has already been deleted since keys were computed. shard.Unlock() continue } torrent.Seeders.Purge(unixtime) torrent.Leechers.Purge(unixtime) peers := torrent.PeerCount() shard.Unlock() if purgeEmptyTorrents && peers == 0 { s.PurgeInactiveTorrent(infohash) stats.RecordEvent(stats.ReapedTorrent) } } return nil }
// HandleScrape encapsulates all the logic of handling a BitTorrent client's // scrape without being coupled to any transport protocol. func (tkr *Tracker) HandleScrape(scrape *models.Scrape, w Writer) (err error) { if tkr.Config.PrivateEnabled { if _, err = tkr.FindUser(scrape.Passkey); err != nil { return err } } var torrents []*models.Torrent for _, infohash := range scrape.Infohashes { torrent, err := tkr.FindTorrent(infohash) if err != nil { return err } torrents = append(torrents, torrent) } stats.RecordEvent(stats.Scrape) return w.WriteScrape(&models.ScrapeResponse{ Files: torrents, }) }
// HandleAnnounce encapsulates all of the logic of handling a BitTorrent // client's Announce without being coupled to any transport protocol. func (tkr *Tracker) HandleAnnounce(ann *models.Announce, w Writer) (err error) { if tkr.Config.ClientWhitelistEnabled { if err = tkr.ClientApproved(ann.ClientID()); err != nil { return err } } var user *models.User if tkr.Config.PrivateEnabled { if user, err = tkr.FindUser(ann.Passkey); err != nil { return err } } torrent, err := tkr.FindTorrent(ann.Infohash) if err == models.ErrTorrentDNE && tkr.Config.CreateOnAnnounce { torrent = &models.Torrent{ Infohash: ann.Infohash, Seeders: models.NewPeerMap(true, tkr.Config), Leechers: models.NewPeerMap(false, tkr.Config), } tkr.PutTorrent(torrent) stats.RecordEvent(stats.NewTorrent) } else if err != nil { return err } ann.BuildPeer(user, torrent) var delta *models.AnnounceDelta if tkr.Config.PrivateEnabled { delta = newAnnounceDelta(ann, torrent) } created, err := tkr.updateSwarm(ann) if err != nil { return err } snatched, err := tkr.handleEvent(ann) if err != nil { return err } if tkr.Config.PrivateEnabled { delta.Created = created delta.Snatched = snatched if err = tkr.Backend.RecordAnnounce(delta); err != nil { return err } } else if tkr.Config.PurgeInactiveTorrents && torrent.PeerCount() == 0 { // Rather than deleting the torrent explicitly, let the tracker driver delete torrents // ensure there are no race conditions. tkr.PurgeInactiveTorrent(torrent.Infohash) stats.RecordEvent(stats.DeletedTorrent) } stats.RecordEvent(stats.Announce) return w.WriteAnnounce(newAnnounceResponse(ann)) }