mirror of
https://github.com/sot-tech/mochi.git
synced 2026-06-08 06:01:55 -07:00
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:
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user