mirror of
https://github.com/go-i2p/go-i2p-bt.git
synced 2025-07-13 11:54:35 -04:00
fix merge conflicts
This commit is contained in:
1
.github/workflows/go.yml
vendored
1
.github/workflows/go.yml
vendored
@ -16,6 +16,7 @@ jobs:
|
||||
- '1.15'
|
||||
- '1.16'
|
||||
- '1.17'
|
||||
- '1.18'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
/// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
@ -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
|
||||
|
29
tracker/httptracker/go1.13.go
Normal file
29
tracker/httptracker/go1.13.go
Normal 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)
|
||||
}
|
33
tracker/httptracker/go1.13_compat.go
Normal file
33
tracker/httptracker/go1.13_compat.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user