Merge commits 129aac230aa..828edb8fd8b from https://github.com/chihaya/chihaya

This commit is contained in:
Širhoe Biazhkovič
2022-04-12 15:58:14 +03:00
parent c858576f76
commit c7edbb52f2
51 changed files with 562 additions and 1327 deletions
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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")
}
+4 -2
View File
@@ -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
View File
@@ -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,
+2 -2
View File
@@ -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"},
+15 -10
View File
@@ -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)
}
+2 -2
View File
@@ -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)
},
}
+6 -9
View File
@@ -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.
+1
View File
@@ -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"
)
+8 -7
View File
@@ -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); {
+2 -1
View File
@@ -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 {
+4 -2
View File
@@ -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"
}
+4 -2
View File
@@ -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()