add support for no_peer_id HTTP announce parameter

* remove `compact` from req/resp structures, because
it used only in HTTP and only while response write
This commit is contained in:
Lawrence, Rendall
2023-03-19 18:37:57 +03:00
parent 24c1539583
commit 34c2921be8
10 changed files with 47 additions and 49 deletions
+1 -5
View File
@@ -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).
+9 -4
View File
@@ -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
+4 -7
View File
@@ -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()
+9 -7
View File
@@ -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'})
+11 -11
View File
@@ -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
+1 -1
View File
@@ -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),
+4 -4
View File
@@ -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
+8 -8
View File
@@ -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)
-1
View File
@@ -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 {
-1
View File
@@ -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) {