fix merge conflicts

This commit is contained in:
idk
2022-07-29 22:59:01 -04:00
9 changed files with 153 additions and 58 deletions

View File

@ -16,6 +16,7 @@ jobs:
- '1.15'
- '1.16'
- '1.17'
- '1.18'
steps:
- uses: actions/checkout@v2
- name: Setup Go

View File

@ -2,5 +2,5 @@
// which has a similar API to the encoding/json package and many other
// serialization formats.
//
// Notice: the package is moved and modified from github.com/zeebo/bencode@v1.0.0
// Notice: the package is ported from github.com/zeebo/bencode@v1.0.0
package bencode

View File

@ -622,8 +622,16 @@ func (s *Server) onError(t *transaction, code int, reason string) {
func (s *Server) onTimeout(t *transaction) {
// TODO: Should we use a task pool??
t.Done(Result{Timeout: true})
s.conf.ErrorLog("transaction '%s' timeout: query=%s, raddr=%s",
t.ID, t.Query, t.Addr.String())
var qid string
switch t.Query {
case "find_node":
qid = t.Arg.Target.String()
case "get_peers", "announce_peer":
qid = t.Arg.InfoHash.String()
}
s.conf.ErrorLog("transaction '%s' timeout: sid=%s, q=%s, qid=%s, laddr=%s, raddr=%s",
t.ID, s.conf.ID, t.Query, qid, s.conn.LocalAddr(), t.Addr.String())
}
func (s *Server) onPingResp(t *transaction, a net.Addr, m krpc.Message) {
@ -645,10 +653,12 @@ func (s *Server) onGetPeersResp(t *transaction, a net.Addr, m krpc.Message) {
return
}
// Terminate the transaction.
t.Done(Result{})
// Search the torrent infohash recursively.
t.Depth--
if t.Depth < 1 {
t.Done(Result{})
return
}
@ -681,7 +691,6 @@ func (s *Server) onGetPeersResp(t *transaction, a net.Addr, m krpc.Message) {
}
if found || len(nodes) == 0 {
t.Done(Result{})
return
}

View File

@ -332,10 +332,6 @@ func (b *bucket) CheckAllNodes(now time.Time) {
}
func bucketid(ownerid, nid metainfo.Hash) int {
if ownerid == nid {
return 0
}
var i int
var bite byte
var bitDiff int
@ -371,7 +367,7 @@ func bucketid(ownerid, nid metainfo.Hash) int {
}
calc:
return i*8 + (8 - bitDiff)
return i*8 + (7 - bitDiff)
}
/// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@ -42,10 +42,10 @@ const (
// as specified by the KRPC protocol, which are also referred to as the KRPC
// messages.
//
// There are three types of messages: QUERY, RESPONSE, ERROR
// The message is a dictonary that is then "bencoded"
// (serialization & compression format adopted by the BitTorrent)
// and sent via the UDP connection to peers.
// The message is a dictonary that is "bencoded" (serialization & compression
// format adopted by the BitTorrent) and sent via the UDP connection to peers.
//
// There are three types of messages: QUERY, RESPONSE, ERROR.
//
// A KRPC message is a single dictionary with two keys common to every message
// and additional keys depending on the type of message. Every message has a key
@ -58,12 +58,24 @@ const (
// the type of message. The value of the "y" key is one of "q" for query,
// "r" for response, or "e" for error.
type Message struct {
T string `bencode:"t"` // required: transaction ID
Y string `bencode:"y"` // required: type of the message: q for QUERY, r for RESPONSE, e for ERROR
Q string `bencode:"q,omitempty"` // Query method (one of 4: "ping", "find_node", "get_peers", "announce_peer")
A QueryArg `bencode:"a,omitempty"` // named arguments sent with a query
R ResponseResult `bencode:"r,omitempty"` // RESPONSE type only
E Error `bencode:"e,omitempty"` // ERROR type only
// Transaction ID
//
// Required
T string `bencode:"t"` // BEP 5
// Message Type: "q" for QUERY, "r" for RESPONSE, "e" for ERROR
//
// Required
Y string `bencode:"y"` // BEP 5
// Query Method: one of "ping", "find_node", "get_peers", "announce_peer"
//
// Required only if "y" is equal to "q".
Q string `bencode:"q,omitempty"` // BEP 5
A QueryArg `bencode:"a,omitempty"` // BEP 5: Only for the QUERY message
R ResponseResult `bencode:"r,omitempty"` // BEP 5: Only for the RESPONSE message
E Error `bencode:"e,omitempty"` // BEP 5: Only for the ERROR message
RO bool `bencode:"ro,omitempty"` // BEP 43: ReadOnly
}
@ -83,34 +95,24 @@ func NewErrorMsg(tid string, code int, reason string) Message {
return Message{T: tid, Y: "e", E: Error{Code: code, Reason: reason}}
}
// IsQuery reports whether the message is an QUERY.
func (m Message) IsQuery() bool {
return m.Y == "q"
}
// IsQuery reports whether the message is a QUERY message.
func (m Message) IsQuery() bool { return m.Y == "q" }
// IsResponse reports whether the message is an RESPONSE.
func (m Message) IsResponse() bool {
return m.Y == "r"
}
// IsResponse reports whether the message is a RESPONSE message.
func (m Message) IsResponse() bool { return m.Y == "r" }
// IsError reports whether the message is an ERROR.
func (m Message) IsError() bool {
return m.Y == "e"
}
// IsError reports whether the message is an ERROR message.
func (m Message) IsError() bool { return m.Y == "e" }
// RID returns the value named "id" in "r".
//
// Return the ZERO value instead if no "id".
func (m Message) RID() metainfo.Hash {
return m.R.ID
}
func (m Message) RID() metainfo.Hash { return m.R.ID }
// QID returns the value named "id" in "a", that's, the query arguments.
//
// Return the ZERO value instead if no "id".
func (m Message) QID() metainfo.Hash {
return m.A.ID
}
func (m Message) QID() metainfo.Hash { return m.A.ID }
// ID returns the QID or RID.
//
@ -128,8 +130,8 @@ func (m Message) ID() metainfo.Hash {
// Error represents a response error.
type Error struct {
Code int
Reason string
Code int // BEP 5
Reason string // BEP 5
}
// NewError returns a new Error.
@ -140,7 +142,7 @@ func NewError(code int, reason string) Error {
func (e *Error) decode(vs []interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("unpacking %#v: %v", vs, r)
err = fmt.Errorf("panic: %v", r)
}
}()
@ -177,7 +179,7 @@ func (e Error) MarshalBencode() (ret []byte, err error) {
buf := bytes.NewBuffer(nil)
buf.Grow(32)
err = bencode.NewEncoder(buf).Encode([]interface{}{e.Code, e.Reason})
if err != nil {
if err == nil {
ret = buf.Bytes()
}
return

View File

@ -0,0 +1,29 @@
// Copyright 2020 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build go1.13
// +build go1.13
package httptracker
import (
"context"
"io"
"net/http"
)
// NewRequestWithContext returns a new Request given a method, URL, and optional body.
func NewRequestWithContext(c context.Context, method, url string, body io.Reader) (*http.Request, error) {
return http.NewRequestWithContext(c, method, url, body)
}

View File

@ -0,0 +1,33 @@
// Copyright 2020 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !go1.13
// +build !go1.13
package httptracker
import (
"context"
"io"
"net/http"
)
// NewRequestWithContext returns a new Request given a method, URL, and optional body.
func NewRequestWithContext(c context.Context, method, url string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, nil
}
return req.WithContext(c), nil
}

View File

@ -21,6 +21,7 @@ package httptracker
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
@ -267,6 +268,7 @@ func (sr ScrapeResponse) EncodeTo(w io.Writer) (err error) {
// Client represents a tracker client based on HTTP/HTTPS.
type Client struct {
Client *http.Client
ID metainfo.Hash
AnnounceURL string
ScrapeURL string
@ -289,22 +291,38 @@ func (t *Client) Close() error { return nil }
func (t *Client) String() string { return t.AnnounceURL }
func (t *Client) send(c context.Context, u string, vs url.Values, r interface{}) (err error) {
sym := "?"
if strings.IndexByte(u, '?') > 0 {
sym = "&"
var url string
if strings.IndexByte(u, '?') < 0 {
url = fmt.Sprintf("%s?%s", u, vs.Encode())
} else {
url = fmt.Sprintf("%s&%s", u, vs.Encode())
}
resp, err := http.Get(u + sym + vs.Encode())
req, err := NewRequestWithContext(c, http.MethodGet, url, nil)
if err != nil {
return
}
defer resp.Body.Close()
var resp *http.Response
if t.Client == nil {
resp, err = http.DefaultClient.Do(req)
} else {
resp, err = t.Client.Do(req)
}
if resp.Body != nil {
defer resp.Body.Close()
}
if err != nil {
return
}
return bencode.NewDecoder(resp.Body).Decode(r)
}
// Announce sends a Announce request to the tracker.
func (t *Client) Announce(c context.Context, req AnnounceRequest) (
resp AnnounceResponse, err error) {
func (t *Client) Announce(c context.Context, req AnnounceRequest) (resp AnnounceResponse, err error) {
if req.PeerID.IsZero() {
if t.ID.IsZero() {
req.PeerID = metainfo.NewRandomHash()
@ -318,12 +336,12 @@ func (t *Client) Announce(c context.Context, req AnnounceRequest) (
}
// Scrape sends a Scrape request to the tracker.
func (t *Client) Scrape(c context.Context, infohashes []metainfo.Hash) (
resp ScrapeResponse, err error) {
func (t *Client) Scrape(c context.Context, infohashes []metainfo.Hash) (resp ScrapeResponse, err error) {
hs := make([]string, len(infohashes))
for i, h := range infohashes {
hs[i] = h.BytesString()
}
err = t.send(c, t.ScrapeURL, url.Values{"info_hash": hs}, &resp)
return
}

View File

@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"net"
"net/http"
"net/url"
"github.com/xgfone/bt/metainfo"
@ -196,27 +197,33 @@ type Client interface {
// ClientConfig is used to configure the defalut client implementation.
type ClientConfig struct {
ID metainfo.Hash // The ID of the local client peer.
// The ID of the local client peer.
ID metainfo.Hash
// The http client used only the tracker client is based on HTTP.
HTTPClient *http.Client
}
// NewClient returns a new Client.
func NewClient(connURL string, conf ...ClientConfig) (c Client, err error) {
var id metainfo.Hash
var config ClientConfig
if len(conf) > 0 {
id = conf[0].ID
config = conf[0]
}
u, err := url.Parse(connURL)
if err == nil {
switch u.Scheme {
case "http", "https":
c = &tclient{url: connURL, http: httptracker.NewClient(connURL, "")}
if !id.IsZero() {
c.(*tclient).http.ID = id
tracker := httptracker.NewClient(connURL, "")
if !config.ID.IsZero() {
tracker.ID = config.ID
}
c = &tclient{url: connURL, http: tracker}
case "udp", "udp4", "udp6":
var utc *udptracker.Client
config := udptracker.ClientConfig{ID: id}
config := udptracker.ClientConfig{ID: config.ID}
utc, err = udptracker.NewClientByDial(u.Scheme, u.Host, config)
if err == nil {
var e []udptracker.Extension