diff --git a/http/http.go b/http/http.go index 0031ed0..50d2dce 100644 --- a/http/http.go +++ b/http/http.go @@ -120,8 +120,7 @@ func (s *Server) connState(conn net.Conn, state http.ConnState) { } } -// Serve creates a new Server and proceeds to block while handling requests -// until a graceful shutdown. +// Serve runs an HTTP server, blocking until the server has shut down. func (s *Server) Serve() { glog.V(0).Info("Starting HTTP on ", s.config.HTTPListenAddr) diff --git a/tracker/models/models.go b/tracker/models/models.go index 07d1f83..1fc1f2a 100644 --- a/tracker/models/models.go +++ b/tracker/models/models.go @@ -39,9 +39,11 @@ var ( type ClientError string type NotFoundError ClientError +type ProtocolError ClientError func (e ClientError) Error() string { return string(e) } func (e NotFoundError) Error() string { return string(e) } +func (e ProtocolError) Error() string { return string(e) } type PeerList []Peer type PeerKey string diff --git a/udp/protocol.go b/udp/protocol.go index 4092d93..9958838 100644 --- a/udp/protocol.go +++ b/udp/protocol.go @@ -7,29 +7,26 @@ package udp import ( "bytes" "encoding/binary" - "errors" "net" "github.com/chihaya/chihaya/stats" "github.com/chihaya/chihaya/tracker/models" ) +// initialConnectionID is the magic initial connection ID specified by BEP 15. var initialConnectionID = []byte{0, 0, 0x04, 0x17, 0x27, 0x10, 0x19, 0x80} +// eventIDs maps IDs to event names. var eventIDs = []string{"", "completed", "started", "stopped"} var ( - errMalformedPacket = errors.New("malformed packet") - errMalformedIP = errors.New("malformed IP address") - errMalformedEvent = errors.New("malformed event ID") - errBadConnectionID = errors.New("bad connection ID") + errMalformedPacket = models.ProtocolError("malformed packet") + errMalformedIP = models.ProtocolError("malformed IP address") + errMalformedEvent = models.ProtocolError("malformed event ID") + errBadConnectionID = models.ProtocolError("bad connection ID") ) -func writeHeader(response []byte, action uint32, transactionID []byte) { - binary.BigEndian.PutUint32(response, action) - copy(response[4:], transactionID) -} - +// handleTorrentError writes err to w if err is a models.ClientError. func handleTorrentError(err error, w *Writer) { if err == nil { return @@ -41,9 +38,10 @@ func handleTorrentError(err error, w *Writer) { } } -func (s *Server) handlePacket(packet []byte, addr *net.UDPAddr) (response []byte) { +// handlePacket decodes and processes one UDP request, returning the response. +func (s *Server) handlePacket(packet []byte, addr *net.UDPAddr) (response []byte, actionName string) { if len(packet) < 16 { - return nil // Malformed, no client packets are less than 16 bytes. + return // Malformed, no client packets are less than 16 bytes. } connID := packet[0:8] @@ -71,17 +69,16 @@ func (s *Server) handlePacket(packet []byte, addr *net.UDPAddr) (response []byte switch action { case 0: - // Connect request. + actionName = "connect" if !bytes.Equal(connID, initialConnectionID) { - return nil // Malformed packet. + return // Malformed packet. } - response = make([]byte, 16) - writeHeader(response, action, transactionID) - copy(response[8:], generatedConnID) + writer.writeHeader(0) + writer.buf.Write(generatedConnID) case 1: - // Announce request. + actionName = "announce" ann, err := s.newAnnounce(packet, addr.IP) if err == nil { @@ -91,7 +88,7 @@ func (s *Server) handlePacket(packet []byte, addr *net.UDPAddr) (response []byte handleTorrentError(err, writer) case 2: - // Scrape request. + actionName = "scrape" scrape, err := s.newScrape(packet) if err == nil { @@ -104,6 +101,7 @@ func (s *Server) handlePacket(packet []byte, addr *net.UDPAddr) (response []byte return } +// newAnnounce decodes one announce packet, returning a models.Announce. func (s *Server) newAnnounce(packet []byte, ip net.IP) (*models.Announce, error) { if len(packet) < 98 { return nil, errMalformedPacket @@ -151,8 +149,9 @@ func (s *Server) newAnnounce(packet []byte, ip net.IP) (*models.Announce, error) }, nil } +// newScrape decodes one announce packet, returning a models.Scrape. func (s *Server) newScrape(packet []byte) (*models.Scrape, error) { - if len(packet) < 16 { + if len(packet) < 36 { return nil, errMalformedPacket } diff --git a/udp/udp.go b/udp/udp.go index efde844..f84b3b0 100644 --- a/udp/udp.go +++ b/udp/udp.go @@ -2,7 +2,7 @@ // Use of this source code is governed by the BSD 2-Clause license, // which can be found in the LICENSE file. -// Package udp implements a UDP BitTorrent tracker per BEP 15 and BEP 41. +// Package udp implements a UDP BitTorrent tracker per BEP 15. // IPv6 is currently unsupported as there is no widely-implemented standard. package udp @@ -55,18 +55,26 @@ func (s *Server) serve() error { return err } + start := time.Now() + go func() { - response := s.handlePacket(buffer[:n], addr) + response, action := s.handlePacket(buffer[:n], addr) if response != nil { sock.WriteToUDP(response, addr) } pool.GiveSlice(buffer) + + if glog.V(2) { + duration := time.Since(start) + glog.Infof("[UDP - %9s] %s", duration, action) + } }() } return nil } +// Serve runs a UDP server, blocking until the server has shut down. func (s *Server) Serve() { glog.V(0).Info("Starting UDP on ", s.config.UDPListenAddr) diff --git a/udp/writer.go b/udp/writer.go index 9c82653..a2b503f 100644 --- a/udp/writer.go +++ b/udp/writer.go @@ -12,6 +12,7 @@ import ( "github.com/chihaya/chihaya/tracker/models" ) +// Writer implements the tracker.Writer interface for the UDP protocol. type Writer struct { buf *bytes.Buffer @@ -19,6 +20,7 @@ type Writer struct { transactionID []byte } +// WriteError writes the failure reason as a null-terminated string. func (w *Writer) WriteError(err error) error { w.writeHeader(3) w.buf.WriteString(err.Error()) @@ -26,6 +28,7 @@ func (w *Writer) WriteError(err error) error { return nil } +// WriteAnnounce encodes an announce response according to the UDP spec. func (w *Writer) WriteAnnounce(res *models.AnnounceResponse) error { w.writeHeader(1) binary.Write(w.buf, binary.BigEndian, uint32(res.Interval/time.Second)) @@ -40,6 +43,7 @@ func (w *Writer) WriteAnnounce(res *models.AnnounceResponse) error { return nil } +// WriteAnnounce encodes a scrape response according to the UDP spec. func (w *Writer) WriteScrape(res *models.ScrapeResponse) error { w.writeHeader(2) @@ -52,6 +56,7 @@ func (w *Writer) WriteScrape(res *models.ScrapeResponse) error { return nil } +// writeHeader writes the action and transaction ID to the response. func (w *Writer) writeHeader(action uint32) { binary.Write(w.buf, binary.BigEndian, action) w.buf.Write(w.transactionID)