diff --git a/bittorrent/request.go b/bittorrent/request.go index b5cd4a0..150f87d 100644 --- a/bittorrent/request.go +++ b/bittorrent/request.go @@ -147,7 +147,6 @@ func (rp RequestPeer) MarshalZerologObject(e *zerolog.Event) { type AnnounceRequest struct { Event Event InfoHash InfoHash - Compact bool EventProvided bool NumWantProvided bool NumWant uint32 @@ -163,7 +162,6 @@ type AnnounceRequest struct { func (r AnnounceRequest) MarshalZerologObject(e *zerolog.Event) { e.Stringer("event", r.Event). Stringer("infoHash", r.InfoHash). - Bool("compact", r.Compact). Bool("eventProvided", r.EventProvided). Bool("numWantProvided", r.NumWantProvided). Uint32("numWant", r.NumWant). @@ -177,7 +175,6 @@ func (r AnnounceRequest) MarshalZerologObject(e *zerolog.Event) { // AnnounceResponse represents the parameters used to create an announce // response. type AnnounceResponse struct { - Compact bool Complete uint32 Incomplete uint32 Interval time.Duration @@ -188,8 +185,7 @@ type AnnounceResponse struct { // MarshalZerologObject writes fields into zerolog event func (r AnnounceResponse) MarshalZerologObject(e *zerolog.Event) { - e.Bool("compact", r.Compact). - Uint32("complete", r.Complete). + e.Uint32("complete", r.Complete). Uint32("incomplete", r.Incomplete). Dur("interval", r.Interval). Dur("minInterval", r.MinInterval). diff --git a/frontend/http/frontend.go b/frontend/http/frontend.go index f5e29aa..76279e8 100644 --- a/frontend/http/frontend.go +++ b/frontend/http/frontend.go @@ -239,7 +239,7 @@ func (f *httpFE) announceRoute(reqCtx *fasthttp.RequestCtx) { }() } - aReq, err = ParseAnnounce(reqCtx, f.ParseOptions) + aReq, err = parseAnnounce(reqCtx, f.ParseOptions) if err != nil { writeErrorResponse(reqCtx, err) return @@ -254,11 +254,16 @@ func (f *httpFE) announceRoute(reqCtx *fasthttp.RequestCtx) { } reqCtx.Response.Header.Set("Content-Type", "text/plain; charset=utf-8") - writeAnnounceResponse(reqCtx, aResp) + qArgs := reqCtx.QueryArgs() + // `compact` means that tracker should return addresses in + // binary (single concatenated string) mode instead of dictionary. + // `no_peer_id` means, that tracker may omit PeerID field in response dictionary. + // see https://wiki.theory.org/BitTorrentSpecification#Tracker_Request_Parameters + writeAnnounceResponse(reqCtx, aResp, qArgs.GetBool("compact"), !qArgs.GetBool("no_peer_id")) // next actions are background and should not be canceled after http writer closed ctx = bittorrent.RemapRouteParamsToBgContext(ctx) - // params mapped from fasthttp.QueryArgs will in the next request + // params mapped from fasthttp.QueryArgs will be reused in the next request aReq.Params = nil go f.logic.AfterAnnounce(ctx, aReq, aResp) } @@ -275,7 +280,7 @@ func (f *httpFE) scrapeRoute(reqCtx *fasthttp.RequestCtx) { }() } - req, err := ParseScrape(reqCtx, f.ParseOptions) + req, err := parseScrape(reqCtx, f.ParseOptions) if err != nil { writeErrorResponse(reqCtx, err) return diff --git a/frontend/http/parser.go b/frontend/http/parser.go index 32faa57..6542496 100644 --- a/frontend/http/parser.go +++ b/frontend/http/parser.go @@ -31,8 +31,8 @@ var ( errInvalidParameterNumWant = bittorrent.ClientError("parameter 'num want' invalid or not provided") ) -// ParseAnnounce parses an bittorrent.AnnounceRequest from an http.Request. -func ParseAnnounce(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.AnnounceRequest, error) { +// parseAnnounce parses an bittorrent.AnnounceRequest from an http.Request. +func parseAnnounce(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.AnnounceRequest, error) { qp := &queryParams{r.QueryArgs()} request := &bittorrent.AnnounceRequest{Params: qp} @@ -60,9 +60,6 @@ func ParseAnnounce(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.Annou // FIXME: make sure that we have a copy of InfoHash request.InfoHash = infoHashes[0] - // Determine if the client expects a compact response. - request.Compact = qp.GetBool("compact") - // Parse the PeerID from the request. request.ID, err = bittorrent.NewPeerID(qp.Peek("peer_id")) if err != nil { @@ -115,8 +112,8 @@ func ParseAnnounce(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.Annou return request, err } -// ParseScrape parses an bittorrent.ScrapeRequest from an http.Request. -func ParseScrape(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.ScrapeRequest, error) { +// parseScrape parses an bittorrent.ScrapeRequest from an http.Request. +func parseScrape(r *fasthttp.RequestCtx, opts ParseOptions) (*bittorrent.ScrapeRequest, error) { qp := &queryParams{r.QueryArgs()} infoHashes := qp.InfoHashes() diff --git a/frontend/http/writer.go b/frontend/http/writer.go index f61e223..b384703 100644 --- a/frontend/http/writer.go +++ b/frontend/http/writer.go @@ -27,7 +27,7 @@ func writeErrorResponse(w io.StringWriter, err error) { _, _ = w.WriteString("d14:failure reason" + strconv.Itoa(len(message)) + ":" + message + "e") } -func writeAnnounceResponse(w io.Writer, resp *bittorrent.AnnounceResponse) { +func writeAnnounceResponse(w io.Writer, resp *bittorrent.AnnounceResponse, compact, includePeerID bool) { bb := respBufferPool.Get() defer respBufferPool.Put(bb) @@ -49,7 +49,7 @@ func writeAnnounceResponse(w io.Writer, resp *bittorrent.AnnounceResponse) { bb.WriteByte('e') // Add the peers to the dictionary in the compact format. - if resp.Compact { + if compact { // Add the IPv4 peers to the dictionary. compactAddresses(bb, resp.IPv4Peers, false) // Add the IPv6 peers to the dictionary. @@ -58,10 +58,10 @@ func writeAnnounceResponse(w io.Writer, resp *bittorrent.AnnounceResponse) { // Add the peers to the dictionary. bb.WriteString("5:peersl") for _, peer := range resp.IPv4Peers { - dictAddress(bb, peer) + dictAddress(bb, peer, includePeerID) } for _, peer := range resp.IPv6Peers { - dictAddress(bb, peer) + dictAddress(bb, peer, includePeerID) } bb.WriteByte('e') } @@ -88,14 +88,16 @@ func compactAddresses(bb *bytes.Buffer, peers bittorrent.Peers, v6 bool) { } } -func dictAddress(bb *bytes.Buffer, peer bittorrent.Peer) { +func dictAddress(bb *bytes.Buffer, peer bittorrent.Peer, includePeerID bool) { bb.WriteString("d2:ip") addr := peer.Addr().String() bb.Write(fasthttp.AppendUint(nil, len(addr))) bb.WriteByte(':') bb.WriteString(addr) - bb.WriteString("7:peer id20:") - bb.Write(peer.ID[:]) + if includePeerID { + bb.WriteString("7:peer id20:") + bb.Write(peer.ID[:]) + } bb.WriteString("4:porti") bb.Write(fasthttp.AppendUint(nil, int(peer.Port()))) bb.Write([]byte{'e', 'e'}) diff --git a/frontend/udp/frontend.go b/frontend/udp/frontend.go index cce7ed3..5e61dc1 100644 --- a/frontend/udp/frontend.go +++ b/frontend/udp/frontend.go @@ -268,7 +268,7 @@ func (f *udpFE) handleRequest(ctx context.Context, r Request, w ResponseWriter) // invalid, then fail. if actionID != connectActionID && !gen.Validate(connID, r.IP, timecache.Now()) { err = errBadConnectionID - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) return } @@ -282,15 +282,15 @@ func (f *udpFE) handleRequest(ctx context.Context, r Request, w ResponseWriter) return } - WriteConnectionID(w, txID, gen.Generate(r.IP, timecache.Now())) + writeConnectionID(w, txID, gen.Generate(r.IP, timecache.Now())) case announceActionID, announceV6ActionID: actionName = "announce" var req *bittorrent.AnnounceRequest - req, err = ParseAnnounce(r, actionID == announceV6ActionID, f.ParseOptions) + req, err = parseAnnounce(r, actionID == announceV6ActionID, f.ParseOptions) if err != nil { - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) return } @@ -298,11 +298,11 @@ func (f *udpFE) handleRequest(ctx context.Context, r Request, w ResponseWriter) ctx := bittorrent.InjectRouteParamsToContext(ctx, bittorrent.RouteParams{}) ctx, resp, err = f.logic.HandleAnnounce(ctx, req) if err != nil { - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) return } - WriteAnnounce(w, txID, resp, actionID == announceV6ActionID, r.IP.Is6()) + writeAnnounceResponse(w, txID, resp, actionID == announceV6ActionID, r.IP.Is6()) ctx = bittorrent.RemapRouteParamsToBgContext(ctx) go f.logic.AfterAnnounce(ctx, req, resp) @@ -311,9 +311,9 @@ func (f *udpFE) handleRequest(ctx context.Context, r Request, w ResponseWriter) actionName = "scrape" var req *bittorrent.ScrapeRequest - req, err = ParseScrape(r, f.ParseOptions) + req, err = parseScrape(r, f.ParseOptions) if err != nil { - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) return } @@ -321,18 +321,18 @@ func (f *udpFE) handleRequest(ctx context.Context, r Request, w ResponseWriter) ctx := bittorrent.InjectRouteParamsToContext(ctx, bittorrent.RouteParams{}) ctx, resp, err = f.logic.HandleScrape(ctx, req) if err != nil { - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) return } - WriteScrape(w, txID, resp) + writeScrapeResponse(w, txID, resp) ctx = bittorrent.RemapRouteParamsToBgContext(ctx) go f.logic.AfterScrape(ctx, req, resp) default: err = errUnknownAction - WriteError(w, txID, err) + writeErrorResponse(w, txID, err) } return diff --git a/frontend/udp/params.go b/frontend/udp/params.go index be73a72..59a10e9 100644 --- a/frontend/udp/params.go +++ b/frontend/udp/params.go @@ -39,7 +39,7 @@ func parseQuery(query []byte) (q *queryParams, err error) { if queryDelim != -1 { query = query[queryDelim+1:] } - // This is basically url.parseQuery, but with a map[string]string + // This is basically url.ParseQuery, but with a map[string]string // instead of map[string][]string for the values. q = &queryParams{ params: make(map[string]string), diff --git a/frontend/udp/parser.go b/frontend/udp/parser.go index 8f36782..2117392 100644 --- a/frontend/udp/parser.go +++ b/frontend/udp/parser.go @@ -51,12 +51,12 @@ var ( reqRespBufferPool = bytepool.NewBufferPool() ) -// ParseAnnounce parses an AnnounceRequest from a UDP request. +// parseAnnounce parses an AnnounceRequest from a UDP request. // // If v6Action is true, the announce is parsed the // "old opentracker way": // https://web.archive.org/web/20170503181830/http://opentracker.blog.h3q.com/2007/12/28/the-ipv6-situation/ -func ParseAnnounce(r Request, v6Action bool, opts frontend.ParseOptions) (*bittorrent.AnnounceRequest, error) { +func parseAnnounce(r Request, v6Action bool, opts frontend.ParseOptions) (*bittorrent.AnnounceRequest, error) { var err error ipEnd := 84 + net.IPv4len if v6Action { @@ -157,8 +157,8 @@ func handleOptionalParameters(packet []byte) (bittorrent.Params, error) { return parseQuery(buf.Bytes()) } -// ParseScrape parses a ScrapeRequest from a UDP request. -func ParseScrape(r Request, opts frontend.ParseOptions) (*bittorrent.ScrapeRequest, error) { +// parseScrape parses a ScrapeRequest from a UDP request. +func parseScrape(r Request, opts frontend.ParseOptions) (*bittorrent.ScrapeRequest, error) { // If a scrape isn't at least 36 bytes long, it's malformed. if len(r.Packet) < 36 { return nil, errMalformedPacket diff --git a/frontend/udp/writer.go b/frontend/udp/writer.go index 5ed0efe..61c195b 100644 --- a/frontend/udp/writer.go +++ b/frontend/udp/writer.go @@ -9,8 +9,8 @@ import ( "github.com/sot-tech/mochi/bittorrent" ) -// WriteError writes the failure reason as a null-terminated string. -func WriteError(w io.Writer, txID []byte, err error) { +// writeErrorResponse writes the failure reason as a null-terminated string. +func writeErrorResponse(w io.Writer, txID []byte, err error) { buf := reqRespBufferPool.Get() defer reqRespBufferPool.Put(buf) writeHeader(buf, txID, errorActionID) @@ -23,12 +23,12 @@ func WriteError(w io.Writer, txID []byte, err error) { _, _ = w.Write(buf.Bytes()) } -// WriteAnnounce encodes an announce response according to BEP 15. +// writeAnnounceResponse encodes an announce response according to BEP 15. // The peers returned will be resp.IPv6Peers or resp.IPv4Peers, depending on // whether v6Peers is set. // If v6Action is set, the action will be 4, according to // https://web.archive.org/web/20170503181830/http://opentracker.blog.h3q.com/2007/12/28/the-ipv6-situation/ -func WriteAnnounce(w io.Writer, txID []byte, resp *bittorrent.AnnounceResponse, v6Action, v6Peers bool) { +func writeAnnounceResponse(w io.Writer, txID []byte, resp *bittorrent.AnnounceResponse, v6Action, v6Peers bool) { buf := reqRespBufferPool.Get() defer reqRespBufferPool.Put(buf) @@ -54,8 +54,8 @@ func WriteAnnounce(w io.Writer, txID []byte, resp *bittorrent.AnnounceResponse, _, _ = w.Write(buf.Bytes()) } -// WriteScrape encodes a scrape response according to BEP 15. -func WriteScrape(w io.Writer, txID []byte, resp *bittorrent.ScrapeResponse) { +// writeScrapeResponse encodes a scrape response according to BEP 15. +func writeScrapeResponse(w io.Writer, txID []byte, resp *bittorrent.ScrapeResponse) { buf := reqRespBufferPool.Get() defer reqRespBufferPool.Put(buf) @@ -70,8 +70,8 @@ func WriteScrape(w io.Writer, txID []byte, resp *bittorrent.ScrapeResponse) { _, _ = w.Write(buf.Bytes()) } -// WriteConnectionID encodes a new connection response according to BEP 15. -func WriteConnectionID(w io.Writer, txID, connID []byte) { +// writeConnectionID encodes a new connection response according to BEP 15. +func writeConnectionID(w io.Writer, txID, connID []byte) { buf := reqRespBufferPool.Get() defer reqRespBufferPool.Put(buf) diff --git a/middleware/logic.go b/middleware/logic.go index 26495e1..2476ff3 100644 --- a/middleware/logic.go +++ b/middleware/logic.go @@ -47,7 +47,6 @@ func (l *Logic) HandleAnnounce(ctx context.Context, req *bittorrent.AnnounceRequ resp = &bittorrent.AnnounceResponse{ Interval: l.announceInterval, MinInterval: l.minAnnounceInterval, - Compact: req.Compact, } for _, h := range l.preHooks { if ctx, err = h.HandleAnnounce(ctx, req, resp); err != nil { diff --git a/middleware/logic_test.go b/middleware/logic_test.go index 7b0592e..8bdbd98 100644 --- a/middleware/logic_test.go +++ b/middleware/logic_test.go @@ -34,7 +34,6 @@ func (hooks hookList) handleAnnounce(ctx context.Context, req *bittorrent.Announ resp = &bittorrent.AnnounceResponse{ Interval: 60, MinInterval: 60, - Compact: true, } for _, h := range []Hook(hooks) {