mirror of
https://github.com/sot-tech/mochi.git
synced 2026-07-24 16:58:09 -07:00
Merge commits 129aac230aa..828edb8fd8b from https://github.com/chihaya/chihaya
This commit is contained in:
@@ -260,7 +260,7 @@ func (f *Frontend) serveHTTP(handler http.Handler, tls bool) error {
|
||||
err = srv.ListenAndServe()
|
||||
}
|
||||
// Start the HTTP server.
|
||||
if err != http.ErrServerClosed {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -72,25 +72,25 @@ func ParseAnnounce(r *http.Request, opts ParseOptions) (*bittorrent.AnnounceRequ
|
||||
return nil, err
|
||||
}
|
||||
// Determine the number of remaining bytes for the client.
|
||||
request.Left, err = qp.Uint64("left")
|
||||
request.Left, err = qp.Uint("left", 64)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: left")
|
||||
}
|
||||
|
||||
// Determine the number of bytes downloaded by the client.
|
||||
request.Downloaded, err = qp.Uint64("downloaded")
|
||||
request.Downloaded, err = qp.Uint("downloaded", 64)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: downloaded")
|
||||
}
|
||||
|
||||
// Determine the number of bytes shared by the client.
|
||||
request.Uploaded, err = qp.Uint64("uploaded")
|
||||
request.Uploaded, err = qp.Uint("uploaded", 64)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: uploaded")
|
||||
}
|
||||
|
||||
// Determine the number of peers the client wants in the response.
|
||||
numwant, err := qp.Uint64("numwant")
|
||||
numwant, err := qp.Uint("numwant", 32)
|
||||
if err != nil && err != bittorrent.ErrKeyNotFound {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: numwant")
|
||||
}
|
||||
@@ -99,7 +99,7 @@ func ParseAnnounce(r *http.Request, opts ParseOptions) (*bittorrent.AnnounceRequ
|
||||
request.NumWant = uint32(numwant)
|
||||
|
||||
// Parse the port where the client is listening.
|
||||
port, err := qp.Uint64("port")
|
||||
port, err := qp.Uint("port", 16)
|
||||
if err != nil {
|
||||
return nil, bittorrent.ClientError("failed to parse parameter: port")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -26,8 +27,9 @@ var promResponseDurationMilliseconds = prometheus.NewHistogramVec(
|
||||
func recordResponseDuration(action string, af *bittorrent.AddressFamily, err error, duration time.Duration) {
|
||||
var errString string
|
||||
if err != nil {
|
||||
if _, ok := err.(bittorrent.ClientError); ok {
|
||||
errString = err.Error()
|
||||
var clientErr bittorrent.ClientError
|
||||
if errors.As(err, &clientErr) {
|
||||
errString = clientErr.Error()
|
||||
} else {
|
||||
errString = "internal error"
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
"github.com/sot-tech/mochi/bittorrent"
|
||||
"github.com/sot-tech/mochi/pkg/log"
|
||||
@@ -8,19 +9,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type strMap map[string]interface{}
|
||||
|
||||
// WriteError communicates an error to a BitTorrent client over HTTP.
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
message := "internal server error"
|
||||
if _, clientErr := err.(bittorrent.ClientError); clientErr {
|
||||
message = err.Error()
|
||||
var clientErr bittorrent.ClientError
|
||||
if errors.As(err, &clientErr) {
|
||||
message = clientErr.Error()
|
||||
} else {
|
||||
log.Error("http: internal error", log.Err(err))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err = bencode.NewEncoder(w).Encode(map[string]interface{}{
|
||||
if err = bencode.NewEncoder(w).Encode(map[string]any{
|
||||
"failure reason": message,
|
||||
}); err != nil {
|
||||
log.Error("unable to encode string", log.Err(err))
|
||||
@@ -38,7 +38,7 @@ func WriteAnnounceResponse(w http.ResponseWriter, resp *bittorrent.AnnounceRespo
|
||||
resp.MinInterval /= time.Second
|
||||
}
|
||||
|
||||
bdict := strMap{
|
||||
bdict := map[string]any{
|
||||
"complete": resp.Complete,
|
||||
"incomplete": resp.Incomplete,
|
||||
"interval": resp.Interval,
|
||||
@@ -67,7 +67,7 @@ func WriteAnnounceResponse(w http.ResponseWriter, resp *bittorrent.AnnounceRespo
|
||||
|
||||
} else {
|
||||
// Add the peers to the dictionary.
|
||||
var peers []strMap
|
||||
peers := make([]map[string]any, 0, len(resp.IPv4Peers)+len(resp.IPv6Peers))
|
||||
for _, peer := range resp.IPv4Peers {
|
||||
peers = append(peers, dict(peer))
|
||||
}
|
||||
@@ -83,15 +83,15 @@ func WriteAnnounceResponse(w http.ResponseWriter, resp *bittorrent.AnnounceRespo
|
||||
// WriteScrapeResponse communicates the results of a Scrape to a BitTorrent
|
||||
// client over HTTP.
|
||||
func WriteScrapeResponse(w http.ResponseWriter, resp *bittorrent.ScrapeResponse) error {
|
||||
filesDict := make(strMap)
|
||||
filesDict := make(map[string]any, len(resp.Files))
|
||||
for _, scrape := range resp.Files {
|
||||
filesDict[string(scrape.InfoHash[:])] = strMap{
|
||||
filesDict[string(scrape.InfoHash[:])] = map[string]any{
|
||||
"complete": scrape.Complete,
|
||||
"incomplete": scrape.Incomplete,
|
||||
}
|
||||
}
|
||||
|
||||
return bencode.NewEncoder(w).Encode(strMap{
|
||||
return bencode.NewEncoder(w).Encode(map[string]any{
|
||||
"files": filesDict,
|
||||
})
|
||||
}
|
||||
@@ -118,8 +118,8 @@ func compact6(peer bittorrent.Peer) (buf []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
func dict(peer bittorrent.Peer) strMap {
|
||||
return strMap{
|
||||
func dict(peer bittorrent.Peer) map[string]any {
|
||||
return map[string]any{
|
||||
"peer id": string(peer.ID[:]),
|
||||
"ip": peer.IP.String(),
|
||||
"port": peer.Port,
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestWriteError(t *testing.T) {
|
||||
var table = []struct {
|
||||
table := []struct {
|
||||
reason, expected string
|
||||
}{
|
||||
{"hello world", "d14:failure reason11:hello worlde"},
|
||||
@@ -28,7 +28,7 @@ func TestWriteError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWriteStatus(t *testing.T) {
|
||||
var table = []struct {
|
||||
table := []struct {
|
||||
reason, expected string
|
||||
}{
|
||||
{"something is missing", "d14:failure reason20:something is missinge"},
|
||||
|
||||
@@ -10,25 +10,30 @@ type BytePool struct {
|
||||
// New allocates a new BytePool with slices of equal length and capacity.
|
||||
func New(length int) *BytePool {
|
||||
var bp BytePool
|
||||
bp.Pool.New = func() interface{} {
|
||||
return make([]byte, length, length)
|
||||
bp.Pool.New = func() any {
|
||||
// This avoids allocations for the slice metadata, see:
|
||||
// https://staticcheck.io/docs/checks#SA6002
|
||||
b := make([]byte, length)
|
||||
return &b
|
||||
}
|
||||
return &bp
|
||||
}
|
||||
|
||||
// Get returns a byte slice from the pool.
|
||||
func (bp *BytePool) Get() []byte {
|
||||
return bp.Pool.Get().([]byte)
|
||||
func (bp *BytePool) Get() *[]byte {
|
||||
return bp.Pool.Get().(*[]byte)
|
||||
}
|
||||
|
||||
// Put returns a byte slice to the pool.
|
||||
func (bp *BytePool) Put(b []byte) {
|
||||
b = b[:cap(b)]
|
||||
func (bp *BytePool) Put(b *[]byte) {
|
||||
*b = (*b)[:cap(*b)]
|
||||
|
||||
// Zero out the bytes.
|
||||
// Apparently this specific expression is optimized by the compiler, see
|
||||
// github.com/golang/go/issues/5373.
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
// This specific expression is optimized by the compiler:
|
||||
// https://github.com/golang/go/issues/5373.
|
||||
for i := range *b {
|
||||
(*b)[i] = 0
|
||||
}
|
||||
|
||||
bp.Pool.Put(b)
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func BenchmarkConnectionIDGenerator_Generate(b *testing.B) {
|
||||
createdAt := time.Now()
|
||||
|
||||
pool := &sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return NewConnectionIDGenerator(key)
|
||||
},
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func BenchmarkConnectionIDGenerator_Validate(b *testing.B) {
|
||||
cid := NewConnectionID(ip, createdAt, key)
|
||||
|
||||
pool := &sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return NewConnectionIDGenerator(key)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ func (cfg Config) Validate() Config {
|
||||
|
||||
// Generate a private key if one isn't provided by the user.
|
||||
if cfg.PrivateKey == "" {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
pkeyRunes := make([]rune, 64)
|
||||
for i := range pkeyRunes {
|
||||
pkeyRunes[i] = allowedGeneratedPrivateKeyRunes[rand.Intn(len(allowedGeneratedPrivateKeyRunes))]
|
||||
@@ -117,14 +116,13 @@ func NewFrontend(logic frontend.TrackerLogic, provided Config) (*Frontend, error
|
||||
logic: logic,
|
||||
Config: cfg,
|
||||
genPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return NewConnectionIDGenerator(cfg.PrivateKey)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := f.listen()
|
||||
if err != nil {
|
||||
if err := f.listen(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -185,10 +183,10 @@ func (t *Frontend) serve() error {
|
||||
|
||||
// Read a UDP packet into a reusable buffer.
|
||||
buffer := pool.Get()
|
||||
n, addr, err := t.socket.ReadFromUDP(buffer)
|
||||
n, addr, err := t.socket.ReadFromUDP(*buffer)
|
||||
if err != nil {
|
||||
pool.Put(buffer)
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
// A temporary failure is not fatal; just pretend it never happened.
|
||||
continue
|
||||
}
|
||||
@@ -217,7 +215,7 @@ func (t *Frontend) serve() error {
|
||||
}
|
||||
action, af, err := t.handleRequest(
|
||||
// Make sure the IP is copied, not referenced.
|
||||
Request{buffer[:n], append([]byte{}, addr.IP...)},
|
||||
Request{(*buffer)[:n], append([]byte{}, addr.IP...)},
|
||||
ResponseWriter{t.socket, addr},
|
||||
)
|
||||
if t.EnableRequestTiming {
|
||||
@@ -244,8 +242,7 @@ type ResponseWriter struct {
|
||||
|
||||
// Write implements the io.Writer interface for a ResponseWriter.
|
||||
func (w ResponseWriter) Write(b []byte) (int, error) {
|
||||
w.socket.WriteToUDP(b, w.addr)
|
||||
return len(b), nil
|
||||
return w.socket.WriteToUDP(b, w.addr)
|
||||
}
|
||||
|
||||
// handleRequest parses and responds to a UDP Request.
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/sot-tech/mochi/frontend/udp"
|
||||
"github.com/sot-tech/mochi/middleware"
|
||||
_ "github.com/sot-tech/mochi/pkg/rand_seed"
|
||||
"github.com/sot-tech/mochi/storage"
|
||||
_ "github.com/sot-tech/mochi/storage/memory"
|
||||
)
|
||||
|
||||
@@ -23,9 +23,9 @@ const (
|
||||
|
||||
// Option-Types as described in BEP 41 and BEP 45.
|
||||
const (
|
||||
optionEndOfOptions byte = 0x0
|
||||
optionNOP = 0x1
|
||||
optionURLData = 0x2
|
||||
optionEndOfOptions = 0x0
|
||||
optionNOP = 0x1
|
||||
optionURLData = 0x2
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -80,6 +80,7 @@ func ParseAnnounce(r Request, v6Action bool, opts ParseOptions) (*bittorrent.Ann
|
||||
return nil, errMalformedPacket
|
||||
}
|
||||
|
||||
// XXX: pure V2 hashes will cause invalid parsing
|
||||
infohash := r.Packet[16:36]
|
||||
peerIDBytes := r.Packet[36:56]
|
||||
downloaded := binary.BigEndian.Uint64(r.Packet[56:64])
|
||||
@@ -93,10 +94,10 @@ func ParseAnnounce(r Request, v6Action bool, opts ParseOptions) (*bittorrent.Ann
|
||||
|
||||
ip := r.IP
|
||||
ipProvided := false
|
||||
ipbytes := r.Packet[84:ipEnd]
|
||||
ipBytes := r.Packet[84:ipEnd]
|
||||
if opts.AllowIPSpoofing {
|
||||
// Make sure the bytes are copied to a new slice.
|
||||
copy(ip, net.IP(ipbytes))
|
||||
copy(ip, ipBytes)
|
||||
ipProvided = true
|
||||
}
|
||||
if !opts.AllowIPSpoofing && r.IP == nil {
|
||||
@@ -152,7 +153,7 @@ type buffer struct {
|
||||
}
|
||||
|
||||
var bufferFree = sync.Pool{
|
||||
New: func() interface{} { return new(buffer) },
|
||||
New: func() any { return new(buffer) },
|
||||
}
|
||||
|
||||
func newBuffer() *buffer {
|
||||
@@ -171,7 +172,7 @@ func handleOptionalParameters(packet []byte) (bittorrent.Params, error) {
|
||||
return bittorrent.ParseURLData("")
|
||||
}
|
||||
|
||||
var buf = newBuffer()
|
||||
buf := newBuffer()
|
||||
defer buf.free()
|
||||
|
||||
for i := 0; i < len(packet); {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
@@ -51,7 +52,7 @@ func TestHandleOptionalParameters(t *testing.T) {
|
||||
for _, tt := range table {
|
||||
t.Run(fmt.Sprintf("%#v as %#v", tt.data, tt.values), func(t *testing.T) {
|
||||
params, err := handleOptionalParameters(tt.data)
|
||||
if err != tt.err {
|
||||
if !errors.Is(err, tt.err) {
|
||||
if tt.err == nil {
|
||||
t.Fatalf("expected no parsing error for %x but got %s", tt.data, err)
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package udp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -26,8 +27,9 @@ var promResponseDurationMilliseconds = prometheus.NewHistogramVec(
|
||||
func recordResponseDuration(action string, af *bittorrent.AddressFamily, err error, duration time.Duration) {
|
||||
var errString string
|
||||
if err != nil {
|
||||
if _, ok := err.(bittorrent.ClientError); ok {
|
||||
errString = err.Error()
|
||||
var clientErr bittorrent.ClientError
|
||||
if errors.As(err, &clientErr) {
|
||||
errString = clientErr.Error()
|
||||
} else {
|
||||
errString = "internal error"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package udp
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
@@ -12,8 +13,9 @@ import (
|
||||
// WriteError writes the failure reason as a null-terminated string.
|
||||
func WriteError(w io.Writer, txID []byte, err error) {
|
||||
// If the client wasn't at fault, acknowledge it.
|
||||
if _, ok := err.(bittorrent.ClientError); !ok {
|
||||
err = fmt.Errorf("internal error occurred: %s", err.Error())
|
||||
var clientErr bittorrent.ClientError
|
||||
if !errors.As(err, &clientErr) {
|
||||
err = fmt.Errorf("internal error occurred: %w", err)
|
||||
}
|
||||
|
||||
buf := newBuffer()
|
||||
|
||||
Reference in New Issue
Block a user