diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index b62effb..0000000 --- a/AUTHORS +++ /dev/null @@ -1,5 +0,0 @@ -# This is the official list of Chihaya authors for copyright purposes, in alphabetical order. - -Jimmy Zelinskie -Justin Li - diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 9511c1b..0000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# vim: ft=dockerfile -FROM golang -MAINTAINER Jimmy Zelinskie - -# Install glide -WORKDIR /tmp -ADD https://github.com/Masterminds/glide/releases/download/0.10.2/glide-0.10.2-linux-amd64.tar.gz /tmp -RUN tar xvf /tmp/glide-0.10.2-linux-amd64.tar.gz -RUN mv /tmp/linux-amd64/glide /usr/bin/glide - -# Add files -WORKDIR /go/src/github.com/chihaya/chihaya/ -RUN mkdir -p /go/src/github.com/chihaya/chihaya/ - -# Add source -ADD . . - -# Install chihaya -RUN glide install -RUN go install github.com/chihaya/chihaya/cmd/chihaya - -# Configuration/environment -VOLUME ["/config"] -EXPOSE 6880-6882 - -# docker run -p 6880-6882:6880-6882 -v $PATH_TO_DIR_WITH_CONF_FILE:/config:ro -e quay.io/jzelinskie/chihaya:latest -ENTRYPOINT ["chihaya", "-config=/config/config.json"] diff --git a/chihaya.go b/chihaya.go deleted file mode 100644 index 399e555..0000000 --- a/chihaya.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file.package middleware - -package chihaya - -import ( - "net" - "time" - - "github.com/chihaya/chihaya/pkg/event" -) - -// PeerID represents a peer ID. -type PeerID [20]byte - -// PeerIDFromBytes creates a PeerID from a byte slice. -// -// It panics if b is not 20 bytes long. -func PeerIDFromBytes(b []byte) PeerID { - if len(b) != 20 { - panic("peer ID must be 20 bytes") - } - - var buf [20]byte - copy(buf[:], b) - return PeerID(buf) -} - -// PeerIDFromString creates a PeerID from a string. -// -// It panics if s is not 20 bytes long. -func PeerIDFromString(s string) PeerID { - if len(s) != 20 { - panic("peer ID must be 20 bytes") - } - - var buf [20]byte - copy(buf[:], s) - return PeerID(buf) -} - -// InfoHash represents an infohash. -type InfoHash [20]byte - -// InfoHashFromBytes creates an InfoHash from a byte slice. -// -// It panics if b is not 20 bytes long. -func InfoHashFromBytes(b []byte) InfoHash { - if len(b) != 20 { - panic("infohash must be 20 bytes") - } - - var buf [20]byte - copy(buf[:], b) - return InfoHash(buf) -} - -// InfoHashFromString creates an InfoHash from a string. -// -// It panics if s is not 20 bytes long. -func InfoHashFromString(s string) InfoHash { - if len(s) != 20 { - panic("infohash must be 20 bytes") - } - - var buf [20]byte - copy(buf[:], s) - return InfoHash(buf) -} - -// AnnounceRequest represents the parsed parameters from an announce request. -type AnnounceRequest struct { - Event event.Event - InfoHash InfoHash - PeerID PeerID - - IPv4, IPv6 net.IP - Port uint16 - - Compact bool - NumWant int32 - - Left, Downloaded, Uploaded uint64 - - Params Params -} - -// Peer4 returns a Peer using the IPv4 endpoint of the Announce. -// Note that, if the Announce does not contain an IPv4 address, the IP field of -// the returned Peer can be nil. -func (r *AnnounceRequest) Peer4() Peer { - return Peer{ - IP: r.IPv4, - Port: r.Port, - ID: r.PeerID, - } -} - -// Peer6 returns a Peer using the IPv6 endpoint of the Announce. -// Note that, if the Announce does not contain an IPv6 address, the IP field of -// the returned Peer can be nil. -func (r *AnnounceRequest) Peer6() Peer { - return Peer{ - IP: r.IPv6, - Port: r.Port, - ID: r.PeerID, - } -} - -// AnnounceResponse represents the parameters used to create an announce -// response. -type AnnounceResponse struct { - Compact bool - Complete int32 - Incomplete int32 - Interval time.Duration - MinInterval time.Duration - IPv4Peers []Peer - IPv6Peers []Peer -} - -// ScrapeRequest represents the parsed parameters from a scrape request. -type ScrapeRequest struct { - InfoHashes []InfoHash - Params Params -} - -// ScrapeResponse represents the parameters used to create a scrape response. -type ScrapeResponse struct { - Files map[InfoHash]Scrape -} - -// Scrape represents the state of a swarm that is returned in a scrape response. -type Scrape struct { - Complete int32 - Incomplete int32 -} - -// Peer represents the connection details of a peer that is returned in an -// announce response. -type Peer struct { - ID PeerID - IP net.IP - Port uint16 -} - -// Equal reports whether p and x are the same. -func (p Peer) Equal(x Peer) bool { - return p.EqualEndpoint(x) && p.ID == x.ID -} - -// EqualEndpoint reports whether p and x have the same endpoint. -func (p Peer) EqualEndpoint(x Peer) bool { - return p.Port == x.Port && p.IP.Equal(x.IP) -} - -// Params is used to fetch request parameters. -type Params interface { - String(key string) (string, error) -} diff --git a/chihaya_test.go b/chihaya_test.go deleted file mode 100644 index 6fd513e..0000000 --- a/chihaya_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package chihaya - -import ( - "net" - "testing" - - "github.com/stretchr/testify/assert" -) - -var ( - peers = []struct { - peerID string - ip string - port uint16 - }{ - {"-AZ3034-6wfG2wk6wWLc", "250.183.81.177", 5720}, - {"-BS5820-oy4La2MWGEFj", "fd45:7856:3dae::48", 2878}, - {"-TR0960-6ep6svaa61r4", "fd45:7856:3dae::48", 2878}, - {"-BS5820-oy4La2MWGEFj", "fd0a:29a8:8445::38", 2878}, - {"-BS5820-oy4La2MWGEFj", "fd45:7856:3dae::48", 8999}, - } -) - -func TestPeerEquality(t *testing.T) { - // Build peers from test data. - var builtPeers []Peer - for _, peer := range peers { - builtPeers = append(builtPeers, Peer{ - ID: PeerIDFromString(peer.peerID), - IP: net.ParseIP(peer.ip), - Port: peer.port, - }) - } - - assert.True(t, builtPeers[0].Equal(builtPeers[0])) - assert.False(t, builtPeers[0].Equal(builtPeers[1])) - assert.True(t, builtPeers[1].Equal(builtPeers[1])) - assert.False(t, builtPeers[1].Equal(builtPeers[2])) - assert.False(t, builtPeers[1].Equal(builtPeers[3])) - assert.False(t, builtPeers[1].Equal(builtPeers[4])) -} diff --git a/cmd/chihaya/main.go b/cmd/chihaya/main.go deleted file mode 100644 index 54d9c57..0000000 --- a/cmd/chihaya/main.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package main - -import ( - "flag" - "log" - "os" - "os/signal" - "runtime/pprof" - "syscall" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server" - "github.com/chihaya/chihaya/tracker" - - // Servers - _ "github.com/chihaya/chihaya/server/http" - _ "github.com/chihaya/chihaya/server/prometheus" - _ "github.com/chihaya/chihaya/server/store" - _ "github.com/chihaya/chihaya/server/store/memory" - - // Middleware - _ "github.com/chihaya/chihaya/middleware/deniability" - _ "github.com/chihaya/chihaya/middleware/varinterval" - _ "github.com/chihaya/chihaya/server/store/middleware/client" - _ "github.com/chihaya/chihaya/server/store/middleware/infohash" - _ "github.com/chihaya/chihaya/server/store/middleware/ip" - _ "github.com/chihaya/chihaya/server/store/middleware/response" - _ "github.com/chihaya/chihaya/server/store/middleware/swarm" -) - -var ( - configPath string - cpuprofile string -) - -func init() { - flag.StringVar(&configPath, "config", "", "path to the configuration file") - flag.StringVar(&cpuprofile, "cpuprofile", "", "path to cpu profile output") -} - -func main() { - flag.Parse() - - if cpuprofile != "" { - log.Println("profiling...") - f, err := os.Create(cpuprofile) - if err != nil { - log.Fatal(err) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - cfg, err := chihaya.OpenConfigFile(configPath) - if err != nil { - log.Fatal("failed to load config: " + err.Error()) - } - - tkr, err := tracker.NewTracker(&cfg.Tracker) - if err != nil { - log.Fatal("failed to create tracker: " + err.Error()) - } - - pool, err := server.StartPool(cfg.Servers, tkr) - if err != nil { - log.Fatal("failed to create server pool: " + err.Error()) - } - - shutdown := make(chan os.Signal) - signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM) - <-shutdown - pool.Stop() -} diff --git a/config.go b/config.go deleted file mode 100644 index eb26b24..0000000 --- a/config.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package chihaya - -import ( - "io" - "io/ioutil" - "os" - "time" - - "gopkg.in/yaml.v2" -) - -// DefaultConfig is a sane configuration used as a fallback or for testing. -var DefaultConfig = Config{ - Tracker: TrackerConfig{ - AnnounceInterval: 30 * time.Minute, - MinAnnounceInterval: 20 * time.Minute, - AnnounceMiddleware: []MiddlewareConfig{}, - ScrapeMiddleware: []MiddlewareConfig{}, - }, - Servers: []ServerConfig{}, -} - -// Config represents the global configuration of a chihaya binary. -type Config struct { - Tracker TrackerConfig `yaml:"tracker"` - Servers []ServerConfig `yaml:"servers"` -} - -// TrackerConfig represents the configuration of protocol-agnostic BitTorrent -// Tracker used by Servers started by chihaya. -type TrackerConfig struct { - AnnounceInterval time.Duration `yaml:"announce"` - MinAnnounceInterval time.Duration `yaml:"min_announce"` - AnnounceMiddleware []MiddlewareConfig `yaml:"announce_middleware"` - ScrapeMiddleware []MiddlewareConfig `yaml:"scrape_middleware"` -} - -// MiddlewareConfig represents the configuration of a middleware used by -// the tracker. -type MiddlewareConfig struct { - Name string `yaml:"name"` - Config interface{} `yaml:"config"` -} - -// ServerConfig represents the configuration of the Servers started by chihaya. -type ServerConfig struct { - Name string `yaml:"name"` - Config interface{} `yaml:"config"` -} - -// ConfigFile represents a YAML configuration file that namespaces all chihaya -// configuration under the "chihaya" namespace. -type ConfigFile struct { - Chihaya Config `yaml:"chihaya"` -} - -// DecodeConfigFile unmarshals an io.Reader into a new Config. -func DecodeConfigFile(r io.Reader) (*Config, error) { - contents, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - cfgFile := &ConfigFile{} - err = yaml.Unmarshal(contents, cfgFile) - if err != nil { - return nil, err - } - - return &cfgFile.Chihaya, nil -} - -// OpenConfigFile returns a new Config given the path to a YAML configuration -// file. -// It supports relative and absolute paths and environment variables. -// Given "", it returns DefaultConfig. -func OpenConfigFile(path string) (*Config, error) { - if path == "" { - return &DefaultConfig, nil - } - - f, err := os.Open(os.ExpandEnv(path)) - if err != nil { - return nil, err - } - defer f.Close() - - cfg, err := DecodeConfigFile(f) - if err != nil { - return nil, err - } - - return cfg, nil -} diff --git a/config_example.yaml b/config_example.yaml deleted file mode 100644 index 8e8f4a0..0000000 --- a/config_example.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2016 The Chihaya Authors. All rights reserved. -# Use of this source code is governed by the BSD 2-Clause license, -# which can be found in the LICENSE file. - -chihaya: - tracker: - announce: 10m - min_announce: 5m - announce_middleware: -# - name: ip_blacklist -# - name: ip_whitelist -# - name: client_blacklist -# - name: client_whitelist -# - name: infohash_blacklist -# - name: infohash_whitelist -# - name: varinterval -# - name: deniability - - name: store_swarm_interaction - - name: store_response - scrape_middleware: -# - name: infohash_blacklist -# config: -# mode: block - - name: store_response - - servers: - - name: store - config: - addr: localhost:6880 - request_timeout: 10s - read_timeout: 10s - write_timeout: 10s - client_store: - name: memory - ip_store: - name: memory - string_store: - name: memory - peer_store: - name: memory - config: - gcAfter: 30m - shards: 1 - - - name: prometheus - config: - addr: localhost:6881 - shutdown_timeout: 10s - read_timeout: 10s - write_timeout: 10s - - - name: http - config: - addr: localhost:6882 - request_timeout: 10s - read_timeout: 10s - write_timeout: 10s - -# - name: udp -# config: -# addr: localhost:6883 diff --git a/glide.lock b/glide.lock deleted file mode 100644 index 89a301c..0000000 --- a/glide.lock +++ /dev/null @@ -1,44 +0,0 @@ -hash: e7d2be6c361fe6fe6242b56e502829e8a72733f9ff0aa57443c9397c3488174f -updated: 2016-05-21T17:58:26.448148976-04:00 -imports: -- name: github.com/beorn7/perks - version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 - subpackages: - - quantile -- name: github.com/golang/protobuf - version: cd85f19845cc96cc6e5269c894d8cd3c67e9ed83 - subpackages: - - proto -- name: github.com/julienschmidt/httprouter - version: 77366a47451a56bb3ba682481eed85b64fea14e8 -- name: github.com/matttproud/golang_protobuf_extensions - version: c12348ce28de40eed0136aa2b644d0ee0650e56c - subpackages: - - pbutil -- name: github.com/mrd0ll4r/netmatch - version: af335c21c765757f2649dbf1d3d43f77eb6c4eb8 -- name: github.com/prometheus/client_golang - version: d38f1ef46f0d78136db3e585f7ebe1bcc3476f73 - subpackages: - - prometheus -- name: github.com/prometheus/client_model - version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6 - subpackages: - - go -- name: github.com/prometheus/common - version: a715f9d07a512e8339f70a275ace0e67c0f9a65f - subpackages: - - expfmt - - internal/bitbucket.org/ww/goautoneg - - model -- name: github.com/prometheus/procfs - version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 -- name: github.com/tylerb/graceful - version: 9a3d4236b03bb5d26f7951134d248f9d5510d599 -- name: golang.org/x/net - version: 0c607074acd38c5f23d1344dfe74c977464d1257 - subpackages: - - netutil -- name: gopkg.in/yaml.v2 - version: a83829b6f1293c91addabc89d0571c246397bbf4 -devImports: [] diff --git a/glide.yaml b/glide.yaml deleted file mode 100644 index d3a516d..0000000 --- a/glide.yaml +++ /dev/null @@ -1,9 +0,0 @@ -package: github.com/chihaya/chihaya -import: -- package: github.com/julienschmidt/httprouter -- package: github.com/mrd0ll4r/netmatch -- package: github.com/prometheus/client_golang - subpackages: - - prometheus -- package: github.com/tylerb/graceful -- package: gopkg.in/yaml.v2 diff --git a/middleware/deniability/README.md b/middleware/deniability/README.md deleted file mode 100644 index fd50614..0000000 --- a/middleware/deniability/README.md +++ /dev/null @@ -1,39 +0,0 @@ -## Deniability Middleware - -This package provides the announce middleware `deniability` which inserts ghost peers into announce responses to achieve plausible deniability. - -### Functionality - -This middleware will choose random announces and modify the list of peers returned. -A random number of randomly generated peers will be inserted at random positions into the list of peers. -As soon as the list of peers exceeds `numWant`, peers will be replaced rather than inserted. - -Note that if a response is picked for augmentation, both IPv4 and IPv6 peers will be modified, in case they are not empty. - -Also note that the IP address for the generated peeer consists of bytes in the range [1,254]. - -### Configuration - -This middleware provides the following parameters for configuration: - -- `modify_response_probability` (float, >0, <= 1) indicates the probability by which a response will be augmented with random peers. -- `max_random_peers` (int, >0) sets an upper boundary (inclusive) for the amount of peers added. -- `prefix` (string, 20 characters at most) sets the prefix for generated peer IDs. - The peer ID will be padded to 20 bytes using a random string of alphanumeric characters. -- `min_port` (int, >0, <=65535) sets a lower boundary for the port for generated peers. -- `max_port` (int, >0, <=65536, > `min_port`) sets an upper boundary for the port for generated peers. - -An example config might look like this: - - chihaya: - tracker: - announce_middleware: - - name: deniability - config: - modify_response_probability: 0.2 - max_random_peers: 5 - prefix: -AZ2060- - min_port: 40000 - max_port: 60000 - -For more information about peer IDs and their prefixes, see [this wiki entry](https://wiki.theory.org/BitTorrentSpecification#peer_id). \ No newline at end of file diff --git a/middleware/deniability/config.go b/middleware/deniability/config.go deleted file mode 100644 index 0423493..0000000 --- a/middleware/deniability/config.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package deniability - -import ( - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -// Config represents the configuration for the deniability middleware. -type Config struct { - // ModifyResponseProbability is the probability by which a response will - // be augmented with random peers. - ModifyResponseProbability float32 `yaml:"modify_response_probability"` - - // MaxRandomPeers is the amount of peers that will be added at most. - MaxRandomPeers int `yaml:"max_random_peers"` - - // Prefix is the prefix to be used for peer IDs. - Prefix string `yaml:"prefix"` - - // MinPort is the minimum port (inclusive) for the generated peer. - MinPort int `yaml:"min_port"` - - // MaxPort is the maximum port (exclusive) for the generated peer. - MaxPort int `yaml:"max_port"` -} - -// newConfig parses the given MiddlewareConfig as a deniability.Config. -func newConfig(mwcfg chihaya.MiddlewareConfig) (*Config, error) { - bytes, err := yaml.Marshal(mwcfg.Config) - if err != nil { - return nil, err - } - - var cfg Config - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - return &cfg, nil -} diff --git a/middleware/deniability/config_test.go b/middleware/deniability/config_test.go deleted file mode 100644 index b271ba4..0000000 --- a/middleware/deniability/config_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package deniability - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -type configTestData struct { - modifyProbability string - maxNewPeers string - prefix string - minPort string - maxPort string - err bool - expected Config -} - -var ( - configTemplate = ` -name: foo -config: - modify_response_probability: %s - max_random_peers: %s - prefix: %s - min_port: %s - max_port: %s` - - configData = []configTestData{ - {"1.0", "5", "abc", "2000", "3000", false, Config{1.0, 5, "abc", 2000, 3000}}, - {"a", "a", "12", "a", "a", true, Config{}}, - } -) - -func TestNewConfig(t *testing.T) { - var mwconfig chihaya.MiddlewareConfig - - cfg, err := newConfig(mwconfig) - assert.Nil(t, err) - assert.NotNil(t, cfg) - - for _, test := range configData { - config := fmt.Sprintf(configTemplate, test.modifyProbability, test.maxNewPeers, test.prefix, test.minPort, test.maxPort) - err = yaml.Unmarshal([]byte(config), &mwconfig) - assert.Nil(t, err) - - cfg, err = newConfig(mwconfig) - if test.err { - assert.NotNil(t, err) - continue - } - assert.Nil(t, err) - assert.Equal(t, test.expected, *cfg) - } -} diff --git a/middleware/deniability/deniability.go b/middleware/deniability/deniability.go deleted file mode 100644 index 0e27347..0000000 --- a/middleware/deniability/deniability.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package deniability - -import ( - "errors" - "math/rand" - "time" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/random" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddlewareConstructor("deniability", constructor) -} - -type deniabilityMiddleware struct { - cfg *Config - r *rand.Rand -} - -// constructor provides a middleware constructor that returns a middleware to -// insert peers into the peer lists returned as a response to an announce. -// -// It returns an error if the config provided is either syntactically or -// semantically incorrect. -func constructor(c chihaya.MiddlewareConfig) (tracker.AnnounceMiddleware, error) { - cfg, err := newConfig(c) - if err != nil { - return nil, err - } - - if cfg.ModifyResponseProbability <= 0 || cfg.ModifyResponseProbability > 1 { - return nil, errors.New("modify_response_probability must be in [0,1)") - } - - if cfg.MaxRandomPeers <= 0 { - return nil, errors.New("max_random_peers must be > 0") - } - - if cfg.MinPort <= 0 { - return nil, errors.New("min_port must not be <= 0") - } - - if cfg.MaxPort > 65536 { - return nil, errors.New("max_port must not be > 65536") - } - - if cfg.MinPort >= cfg.MaxPort { - return nil, errors.New("max_port must not be <= min_port") - } - - if len(cfg.Prefix) > 20 { - return nil, errors.New("prefix must not be longer than 20 bytes") - } - - mw := deniabilityMiddleware{ - cfg: cfg, - r: rand.New(rand.NewSource(time.Now().UnixNano())), - } - - return mw.modifyResponse, nil -} - -func (mw *deniabilityMiddleware) modifyResponse(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - err := next(cfg, req, resp) - if err != nil { - return err - } - - if mw.cfg.ModifyResponseProbability == 1 || mw.r.Float32() < mw.cfg.ModifyResponseProbability { - numNewPeers := mw.r.Intn(mw.cfg.MaxRandomPeers) + 1 - for i := 0; i < numNewPeers; i++ { - if len(resp.IPv6Peers) > 0 { - if len(resp.IPv6Peers) >= int(req.NumWant) { - mw.replacePeer(resp.IPv6Peers, true) - } else { - resp.IPv6Peers = mw.insertPeer(resp.IPv6Peers, true) - } - } - - if len(resp.IPv4Peers) > 0 { - if len(resp.IPv4Peers) >= int(req.NumWant) { - mw.replacePeer(resp.IPv4Peers, false) - } else { - resp.IPv4Peers = mw.insertPeer(resp.IPv4Peers, false) - } - } - } - } - - return nil - } -} - -// replacePeer replaces a peer from a random position within the given slice -// of peers with a randomly generated one. -// -// replacePeer panics if len(peers) == 0. -func (mw *deniabilityMiddleware) replacePeer(peers []chihaya.Peer, v6 bool) { - peers[mw.r.Intn(len(peers))] = random.Peer(mw.r, mw.cfg.Prefix, v6, mw.cfg.MinPort, mw.cfg.MaxPort) -} - -// insertPeer inserts a randomly generated peer at a random position into the -// given slice and returns the new slice. -func (mw *deniabilityMiddleware) insertPeer(peers []chihaya.Peer, v6 bool) []chihaya.Peer { - pos := 0 - if len(peers) > 0 { - pos = mw.r.Intn(len(peers)) - } - peers = append(peers, chihaya.Peer{}) - copy(peers[pos+1:], peers[pos:]) - peers[pos] = random.Peer(mw.r, mw.cfg.Prefix, v6, mw.cfg.MinPort, mw.cfg.MaxPort) - - return peers -} diff --git a/middleware/deniability/deniability_test.go b/middleware/deniability/deniability_test.go deleted file mode 100644 index 4492898..0000000 --- a/middleware/deniability/deniability_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package deniability - -import ( - "fmt" - "math/rand" - "net" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/chihaya/chihaya" -) - -type constructorTestData struct { - cfg Config - error bool -} - -var constructorData = []constructorTestData{ - {Config{1.0, 10, "abc", 1024, 1025}, false}, - {Config{1.1, 10, "abc", 1024, 1025}, true}, - {Config{0, 10, "abc", 1024, 1025}, true}, - {Config{1.0, 0, "abc", 1024, 1025}, true}, - {Config{1.0, 10, "01234567890123456789_", 1024, 1025}, true}, - {Config{1.0, 10, "abc", 0, 1025}, true}, - {Config{1.0, 10, "abc", 1024, 0}, true}, - {Config{1.0, 10, "abc", 1024, 65537}, true}, -} - -func TestReplacePeer(t *testing.T) { - cfg := Config{ - Prefix: "abc", - MinPort: 1024, - MaxPort: 1025, - } - mw := deniabilityMiddleware{ - r: rand.New(rand.NewSource(0)), - cfg: &cfg, - } - peer := chihaya.Peer{ - ID: chihaya.PeerID([20]byte{}), - Port: 2000, - IP: net.ParseIP("10.150.255.23"), - } - peers := []chihaya.Peer{peer} - - mw.replacePeer(peers, false) - assert.Equal(t, 1, len(peers)) - assert.Equal(t, "abc", string(peers[0].ID[:3])) - assert.Equal(t, uint16(1024), peers[0].Port) - assert.NotNil(t, peers[0].IP.To4()) - - mw.replacePeer(peers, true) - assert.Equal(t, 1, len(peers)) - assert.Equal(t, "abc", string(peers[0].ID[:3])) - assert.Equal(t, uint16(1024), peers[0].Port) - assert.Nil(t, peers[0].IP.To4()) - - peers = []chihaya.Peer{peer, peer} - - mw.replacePeer(peers, true) - assert.True(t, (peers[0].Port == peer.Port) != (peers[1].Port == peer.Port), "not exactly one peer was replaced") -} - -func TestInsertPeer(t *testing.T) { - cfg := Config{ - Prefix: "abc", - MinPort: 1024, - MaxPort: 1025, - } - mw := deniabilityMiddleware{ - r: rand.New(rand.NewSource(0)), - cfg: &cfg, - } - peer := chihaya.Peer{ - ID: chihaya.PeerID([20]byte{}), - Port: 2000, - IP: net.ParseIP("10.150.255.23"), - } - var peers []chihaya.Peer - - peers = mw.insertPeer(peers, false) - assert.Equal(t, 1, len(peers)) - assert.Equal(t, uint16(1024), peers[0].Port) - assert.Equal(t, "abc", string(peers[0].ID[:3])) - assert.NotNil(t, peers[0].IP.To4()) - - peers = []chihaya.Peer{peer, peer} - - peers = mw.insertPeer(peers, true) - assert.Equal(t, 3, len(peers)) -} - -func TestConstructor(t *testing.T) { - for _, tt := range constructorData { - _, err := constructor(chihaya.MiddlewareConfig{ - Config: tt.cfg, - }) - - if tt.error { - assert.NotNil(t, err, fmt.Sprintf("error expected for %+v", tt.cfg)) - } else { - assert.Nil(t, err, fmt.Sprintf("no error expected for %+v", tt.cfg)) - } - } -} diff --git a/middleware/varinterval/README.md b/middleware/varinterval/README.md deleted file mode 100644 index 50cd812..0000000 --- a/middleware/varinterval/README.md +++ /dev/null @@ -1,34 +0,0 @@ -## Announce Interval Variation Middleware - -This package provides the announce middleware `varinterval` which randomizes the announce interval. - -### Functionality - -This middleware will choose random announces and modify the `interval` and `min_interval` fields. -A random number of seconds will be added to the `interval` field and, if desired, also to the `min_interval` field. - -Note that if a response is picked for modification and `min_interval` should be changed as well, both `interval` and `min_interval` will be modified by the same amount. - -### Use Case - -Use this middleware to avoid recurring load spikes on the tracker. -By randomizing the announce interval, load spikes will flatten out after a few cycles. - -### Configuration - -This middleware provides the following parameters for configuration: - -- `modify_response_probability` (float, >0, <= 1) indicates the probability by which a response will be chosen to have its announce intervals modified. -- `max_increase_delta` (int, >0) sets an upper boundary (inclusive) for the amount of seconds added. -- `modify_min_interval` (boolean) whether to modify the `min_interval` field as well. - -An example config might look like this: - - chihaya: - tracker: - announce_middleware: - - name: varinterval - config: - modify_response_probability: 0.2 - max_increase_delta: 60 - modify_min_interval: true diff --git a/middleware/varinterval/config.go b/middleware/varinterval/config.go deleted file mode 100644 index 19eeaf8..0000000 --- a/middleware/varinterval/config.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package varinterval - -import ( - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -// Config represents the configuration for the varinterval middleware. -type Config struct { - // ModifyResponseProbability is the probability by which a response will - // be modified. - ModifyResponseProbability float32 `yaml:"modify_response_probability"` - - // MaxIncreaseDelta is the amount of seconds that will be added at most. - MaxIncreaseDelta int `yaml:"max_increase_delta"` - - // ModifyMinInterval specifies whether min_interval should be increased - // as well. - ModifyMinInterval bool `yaml:"modify_min_interval"` -} - -// newConfig parses the given MiddlewareConfig as a varinterval.Config. -// -// The contents of the config are not checked. -func newConfig(mwcfg chihaya.MiddlewareConfig) (*Config, error) { - bytes, err := yaml.Marshal(mwcfg.Config) - if err != nil { - return nil, err - } - - var cfg Config - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - return &cfg, nil -} diff --git a/middleware/varinterval/config_test.go b/middleware/varinterval/config_test.go deleted file mode 100644 index f43c73d..0000000 --- a/middleware/varinterval/config_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package varinterval - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -type configTestData struct { - modifyProbability string - maxIncreaseDelta string - modifyMinInterval string - err bool - expected Config -} - -var ( - configTemplate = ` -name: foo -config: - modify_response_probability: %s - max_increase_delta: %s - modify_min_interval: %s` - - configData = []configTestData{ - {"1.0", "60", "false", false, Config{1.0, 60, false}}, - {"a", "60", "false", true, Config{}}, - } -) - -func TestNewConfig(t *testing.T) { - var mwconfig chihaya.MiddlewareConfig - - cfg, err := newConfig(mwconfig) - assert.Nil(t, err) - assert.NotNil(t, cfg) - - for _, test := range configData { - config := fmt.Sprintf(configTemplate, test.modifyProbability, test.maxIncreaseDelta, test.modifyMinInterval) - err = yaml.Unmarshal([]byte(config), &mwconfig) - assert.Nil(t, err) - - cfg, err = newConfig(mwconfig) - if test.err { - assert.NotNil(t, err) - continue - } - assert.Nil(t, err) - assert.Equal(t, test.expected, *cfg) - } -} diff --git a/middleware/varinterval/varinterval.go b/middleware/varinterval/varinterval.go deleted file mode 100644 index 2f8893e..0000000 --- a/middleware/varinterval/varinterval.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package varinterval - -import ( - "errors" - "math/rand" - "time" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddlewareConstructor("varinterval", constructor) -} - -type varintervalMiddleware struct { - cfg *Config - r *rand.Rand -} - -// constructor provides a middleware constructor that returns a middleware to -// insert a variation into announce intervals. -// -// It returns an error if the config provided is either syntactically or -// semantically incorrect. -func constructor(c chihaya.MiddlewareConfig) (tracker.AnnounceMiddleware, error) { - cfg, err := newConfig(c) - if err != nil { - return nil, err - } - - if cfg.ModifyResponseProbability <= 0 || cfg.ModifyResponseProbability > 1 { - return nil, errors.New("modify_response_probability must be in [0,1)") - } - - if cfg.MaxIncreaseDelta <= 0 { - return nil, errors.New("max_increase_delta must be > 0") - } - - mw := varintervalMiddleware{ - cfg: cfg, - r: rand.New(rand.NewSource(time.Now().UnixNano())), - } - - return mw.modifyResponse, nil -} - -func (mw *varintervalMiddleware) modifyResponse(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - err := next(cfg, req, resp) - if err != nil { - return err - } - - if mw.cfg.ModifyResponseProbability == 1 || mw.r.Float32() < mw.cfg.ModifyResponseProbability { - addSeconds := time.Duration(mw.r.Intn(mw.cfg.MaxIncreaseDelta)+1) * time.Second - resp.Interval += addSeconds - - if mw.cfg.ModifyMinInterval { - resp.MinInterval += addSeconds - } - } - - return nil - } -} diff --git a/middleware/varinterval/varinterval_test.go b/middleware/varinterval/varinterval_test.go deleted file mode 100644 index 5da0fe6..0000000 --- a/middleware/varinterval/varinterval_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package varinterval - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -type constructorTestData struct { - cfg Config - error bool -} - -var constructorData = []constructorTestData{ - {Config{1.0, 10, false}, false}, - {Config{1.1, 10, false}, true}, - {Config{0, 10, true}, true}, - {Config{1.0, 0, false}, true}, -} - -func TestConstructor(t *testing.T) { - for _, tt := range constructorData { - _, err := constructor(chihaya.MiddlewareConfig{ - Config: tt.cfg, - }) - - if tt.error { - assert.NotNil(t, err, fmt.Sprintf("error expected for %+v", tt.cfg)) - } else { - assert.Nil(t, err, fmt.Sprintf("no error expected for %+v", tt.cfg)) - } - } -} - -func TestModifyResponse(t *testing.T) { - var ( - achain tracker.AnnounceChain - req chihaya.AnnounceRequest - resp chihaya.AnnounceResponse - ) - - mw, err := constructor(chihaya.MiddlewareConfig{ - Config: Config{ - ModifyResponseProbability: 1.0, - MaxIncreaseDelta: 10, - ModifyMinInterval: true, - }, - }) - assert.Nil(t, err) - - achain.Append(mw) - handler := achain.Handler() - - err = handler(nil, &req, &resp) - assert.Nil(t, err) - assert.True(t, resp.Interval > 0, "interval should have been increased") - assert.True(t, resp.MinInterval > 0, "min_interval should have been increased") -} diff --git a/pkg/bencode/bencode.go b/pkg/bencode/bencode.go deleted file mode 100644 index 819d0c8..0000000 --- a/pkg/bencode/bencode.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package bencode implements bencoding of data as defined in BEP 3 using -// type assertion over reflection for performance. -package bencode - -// Dict represents a bencode dictionary. -type Dict map[string]interface{} - -// NewDict allocates the memory for a Dict. -func NewDict() Dict { - return make(Dict) -} - -// List represents a bencode list. -type List []interface{} - -// NewList allocates the memory for a List. -func NewList() List { - return make(List, 0) -} diff --git a/pkg/bencode/decoder.go b/pkg/bencode/decoder.go deleted file mode 100644 index 345bb95..0000000 --- a/pkg/bencode/decoder.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package bencode - -import ( - "bufio" - "bytes" - "errors" - "io" - "strconv" -) - -// A Decoder reads bencoded objects from an input stream. -type Decoder struct { - r *bufio.Reader -} - -// NewDecoder returns a new decoder that reads from r. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r: bufio.NewReader(r)} -} - -// Decode unmarshals the next bencoded value in the stream. -func (dec *Decoder) Decode() (interface{}, error) { - return unmarshal(dec.r) -} - -// Unmarshal deserializes and returns the bencoded value in buf. -func Unmarshal(buf []byte) (interface{}, error) { - r := bufio.NewReader(bytes.NewBuffer(buf)) - return unmarshal(r) -} - -// unmarshal reads bencoded values from a bufio.Reader -func unmarshal(r *bufio.Reader) (interface{}, error) { - tok, err := r.ReadByte() - if err != nil { - return nil, err - } - - switch tok { - case 'i': - return readTerminatedInt(r, 'e') - - case 'l': - list := NewList() - for { - ok, err := readTerminator(r, 'e') - if err != nil { - return nil, err - } else if ok { - break - } - - v, err := unmarshal(r) - if err != nil { - return nil, err - } - list = append(list, v) - } - return list, nil - - case 'd': - dict := NewDict() - for { - ok, err := readTerminator(r, 'e') - if err != nil { - return nil, err - } else if ok { - break - } - - v, err := unmarshal(r) - if err != nil { - return nil, err - } - - key, ok := v.(string) - if !ok { - return nil, errors.New("bencode: non-string map key") - } - - dict[key], err = unmarshal(r) - if err != nil { - return nil, err - } - } - return dict, nil - - default: - err = r.UnreadByte() - if err != nil { - return nil, err - } - - length, err := readTerminatedInt(r, ':') - if err != nil { - return nil, errors.New("bencode: unknown input sequence") - } - - buf := make([]byte, length) - n, err := r.Read(buf) - - if err != nil { - return nil, err - } else if int64(n) != length { - return nil, errors.New("bencode: short read") - } - - return string(buf), nil - } -} - -func readTerminator(r io.ByteScanner, term byte) (bool, error) { - tok, err := r.ReadByte() - if err != nil { - return false, err - } else if tok == term { - return true, nil - } - return false, r.UnreadByte() -} - -func readTerminatedInt(r *bufio.Reader, term byte) (int64, error) { - buf, err := r.ReadSlice(term) - if err != nil { - return 0, err - } else if len(buf) <= 1 { - return 0, errors.New("bencode: empty integer field") - } - - return strconv.ParseInt(string(buf[:len(buf)-1]), 10, 64) -} diff --git a/pkg/bencode/decoder_test.go b/pkg/bencode/decoder_test.go deleted file mode 100644 index a5c9406..0000000 --- a/pkg/bencode/decoder_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package bencode - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -var unmarshalTests = []struct { - input string - expected interface{} -}{ - {"i42e", int64(42)}, - {"i-42e", int64(-42)}, - - {"7:example", "example"}, - - {"l3:one3:twoe", List{"one", "two"}}, - {"le", List{}}, - - {"d3:one2:aa3:two2:bbe", Dict{"one": "aa", "two": "bb"}}, - {"de", Dict{}}, -} - -func TestUnmarshal(t *testing.T) { - for _, tt := range unmarshalTests { - got, err := Unmarshal([]byte(tt.input)) - assert.Nil(t, err, "unmarshal should not fail") - assert.Equal(t, got, tt.expected, "unmarshalled values should match the expected results") - } -} - -type bufferLoop struct { - val string -} - -func (r *bufferLoop) Read(b []byte) (int, error) { - n := copy(b, r.val) - return n, nil -} - -func BenchmarkUnmarshalScalar(b *testing.B) { - d1 := NewDecoder(&bufferLoop{"7:example"}) - d2 := NewDecoder(&bufferLoop{"i42e"}) - - for i := 0; i < b.N; i++ { - d1.Decode() - d2.Decode() - } -} - -func TestUnmarshalLarge(t *testing.T) { - data := Dict{ - "k1": List{"a", "b", "c"}, - "k2": int64(42), - "k3": "val", - "k4": int64(-42), - } - - buf, _ := Marshal(data) - dec := NewDecoder(&bufferLoop{string(buf)}) - - got, err := dec.Decode() - assert.Nil(t, err, "decode should not fail") - assert.Equal(t, got, data, "encoding and decoding should equal the original value") -} - -func BenchmarkUnmarshalLarge(b *testing.B) { - data := map[string]interface{}{ - "k1": []string{"a", "b", "c"}, - "k2": 42, - "k3": "val", - "k4": uint(42), - } - - buf, _ := Marshal(data) - dec := NewDecoder(&bufferLoop{string(buf)}) - - for i := 0; i < b.N; i++ { - dec.Decode() - } -} diff --git a/pkg/bencode/encoder.go b/pkg/bencode/encoder.go deleted file mode 100644 index 85ee29e..0000000 --- a/pkg/bencode/encoder.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package bencode - -import ( - "bytes" - "fmt" - "io" - "strconv" - "time" -) - -// An Encoder writes bencoded objects to an output stream. -type Encoder struct { - w io.Writer -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w} -} - -// Encode writes the bencoding of v to the stream. -func (enc *Encoder) Encode(v interface{}) error { - return marshal(enc.w, v) -} - -// Marshal returns the bencoding of v. -func Marshal(v interface{}) ([]byte, error) { - buf := &bytes.Buffer{} - err := marshal(buf, v) - return buf.Bytes(), err -} - -// Marshaler is the interface implemented by objects that can marshal -// themselves. -type Marshaler interface { - MarshalBencode() ([]byte, error) -} - -// marshal writes types bencoded to an io.Writer -func marshal(w io.Writer, data interface{}) error { - switch v := data.(type) { - case Marshaler: - bencoded, err := v.MarshalBencode() - if err != nil { - return err - } - _, err = w.Write(bencoded) - if err != nil { - return err - } - - case string: - marshalString(w, v) - - case int: - marshalInt(w, int64(v)) - - case uint: - marshalUint(w, uint64(v)) - - case int16: - marshalInt(w, int64(v)) - - case uint16: - marshalUint(w, uint64(v)) - - case int32: - marshalInt(w, int64(v)) - - case uint32: - marshalUint(w, uint64(v)) - - case int64: - marshalInt(w, v) - - case uint64: - marshalUint(w, v) - - case []byte: - marshalBytes(w, v) - - case time.Duration: // Assume seconds - marshalInt(w, int64(v/time.Second)) - - case Dict: - marshal(w, map[string]interface{}(v)) - - case []Dict: - w.Write([]byte{'l'}) - for _, val := range v { - err := marshal(w, val) - if err != nil { - return err - } - } - w.Write([]byte{'e'}) - - case map[string]interface{}: - w.Write([]byte{'d'}) - for key, val := range v { - marshalString(w, key) - err := marshal(w, val) - if err != nil { - return err - } - } - w.Write([]byte{'e'}) - - case []string: - w.Write([]byte{'l'}) - for _, val := range v { - err := marshal(w, val) - if err != nil { - return err - } - } - w.Write([]byte{'e'}) - - case List: - marshal(w, []interface{}(v)) - - case []interface{}: - w.Write([]byte{'l'}) - for _, val := range v { - err := marshal(w, val) - if err != nil { - return err - } - } - w.Write([]byte{'e'}) - - default: - return fmt.Errorf("attempted to marshal unsupported type:\n%t", v) - } - - return nil -} - -func marshalInt(w io.Writer, v int64) { - w.Write([]byte{'i'}) - w.Write([]byte(strconv.FormatInt(v, 10))) - w.Write([]byte{'e'}) -} - -func marshalUint(w io.Writer, v uint64) { - w.Write([]byte{'i'}) - w.Write([]byte(strconv.FormatUint(v, 10))) - w.Write([]byte{'e'}) -} - -func marshalBytes(w io.Writer, v []byte) { - w.Write([]byte(strconv.Itoa(len(v)))) - w.Write([]byte{':'}) - w.Write(v) -} - -func marshalString(w io.Writer, v string) { - marshalBytes(w, []byte(v)) -} diff --git a/pkg/bencode/encoder_test.go b/pkg/bencode/encoder_test.go deleted file mode 100644 index 61186d0..0000000 --- a/pkg/bencode/encoder_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package bencode - -import ( - "bytes" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -var marshalTests = []struct { - input interface{} - expected []string -}{ - {int(42), []string{"i42e"}}, - {int(-42), []string{"i-42e"}}, - {uint(43), []string{"i43e"}}, - {int64(44), []string{"i44e"}}, - {uint64(45), []string{"i45e"}}, - {int16(44), []string{"i44e"}}, - {uint16(45), []string{"i45e"}}, - - {"example", []string{"7:example"}}, - {[]byte("example"), []string{"7:example"}}, - {30 * time.Minute, []string{"i1800e"}}, - - {[]string{"one", "two"}, []string{"l3:one3:twoe", "l3:two3:onee"}}, - {[]interface{}{"one", "two"}, []string{"l3:one3:twoe", "l3:two3:onee"}}, - {[]string{}, []string{"le"}}, - - {map[string]interface{}{"one": "aa", "two": "bb"}, []string{"d3:one2:aa3:two2:bbe", "d3:two2:bb3:one2:aae"}}, - {map[string]interface{}{}, []string{"de"}}, -} - -func TestMarshal(t *testing.T) { - for _, test := range marshalTests { - got, err := Marshal(test.input) - assert.Nil(t, err, "marshal should not fail") - assert.Contains(t, test.expected, string(got), "the marshaled result should be one of the expected permutations") - } -} - -func BenchmarkMarshalScalar(b *testing.B) { - buf := &bytes.Buffer{} - encoder := NewEncoder(buf) - - for i := 0; i < b.N; i++ { - encoder.Encode("test") - encoder.Encode(123) - } -} - -func BenchmarkMarshalLarge(b *testing.B) { - data := map[string]interface{}{ - "k1": []string{"a", "b", "c"}, - "k2": 42, - "k3": "val", - "k4": uint(42), - } - - buf := &bytes.Buffer{} - encoder := NewEncoder(buf) - - for i := 0; i < b.N; i++ { - encoder.Encode(data) - } -} diff --git a/pkg/clientid/client_id.go b/pkg/clientid/client_id.go deleted file mode 100644 index 3f97009..0000000 --- a/pkg/clientid/client_id.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package clientid implements the parsing of BitTorrent ClientIDs from -// BitTorrent PeerIDs. -package clientid - -// New returns the part of a PeerID that identifies a peer's client software. -func New(peerID string) (clientID string) { - length := len(peerID) - if length >= 6 { - if peerID[0] == '-' { - if length >= 7 { - clientID = peerID[1:7] - } - } else { - clientID = peerID[:6] - } - } - - return -} diff --git a/pkg/clientid/client_id_test.go b/pkg/clientid/client_id_test.go deleted file mode 100644 index 949ac66..0000000 --- a/pkg/clientid/client_id_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package clientid - -import "testing" - -func TestClientID(t *testing.T) { - var clientTable = []struct { - peerID string - clientID string - }{ - {"-AZ3034-6wfG2wk6wWLc", "AZ3034"}, - {"-AZ3042-6ozMq5q6Q3NX", "AZ3042"}, - {"-BS5820-oy4La2MWGEFj", "BS5820"}, - {"-AR6360-6oZyyMWoOOBe", "AR6360"}, - {"-AG2083-s1hiF8vGAAg0", "AG2083"}, - {"-AG3003-lEl2Mm4NEO4n", "AG3003"}, - {"-MR1100-00HS~T7*65rm", "MR1100"}, - {"-LK0140-ATIV~nbEQAMr", "LK0140"}, - {"-KT2210-347143496631", "KT2210"}, - {"-TR0960-6ep6svaa61r4", "TR0960"}, - {"-XX1150-dv220cotgj4d", "XX1150"}, - {"-AZ2504-192gwethivju", "AZ2504"}, - {"-KT4310-3L4UvarKuqIu", "KT4310"}, - {"-AZ2060-0xJQ02d4309O", "AZ2060"}, - {"-BD0300-2nkdf08Jd890", "BD0300"}, - {"-A~0010-a9mn9DFkj39J", "A~0010"}, - {"-UT2300-MNu93JKnm930", "UT2300"}, - {"-UT2300-KT4310KT4301", "UT2300"}, - - {"T03A0----f089kjsdf6e", "T03A0-"}, - {"S58B-----nKl34GoNb75", "S58B--"}, - {"M4-4-0--9aa757Efd5Bl", "M4-4-0"}, - - {"AZ2500BTeYUzyabAfo6U", "AZ2500"}, // BitTyrant - {"exbc0JdSklm834kj9Udf", "exbc0J"}, // Old BitComet - {"FUTB0L84j542mVc84jkd", "FUTB0L"}, // Alt BitComet - {"XBT054d-8602Jn83NnF9", "XBT054"}, // XBT - {"OP1011affbecbfabeefb", "OP1011"}, // Opera - {"-ML2.7.2-kgjjfkd9762", "ML2.7."}, // MLDonkey - {"-BOWA0C-SDLFJWEIORNM", "BOWA0C"}, // Bits on Wheels - {"Q1-0-0--dsn34DFn9083", "Q1-0-0"}, // Queen Bee - {"Q1-10-0-Yoiumn39BDfO", "Q1-10-"}, // Queen Bee Alt - {"346------SDFknl33408", "346---"}, // TorreTopia - {"QVOD0054ABFFEDCCDEDB", "QVOD00"}, // Qvod - - {"", ""}, - {"-", ""}, - {"12345", ""}, - {"-12345", ""}, - {"123456", "123456"}, - {"-123456", "123456"}, - } - - for _, tt := range clientTable { - if parsedID := New(tt.peerID); parsedID != tt.clientID { - t.Error("Incorrectly parsed peer ID", tt.peerID, "as", parsedID) - } - } -} diff --git a/pkg/event/event.go b/pkg/event/event.go deleted file mode 100644 index c896cda..0000000 --- a/pkg/event/event.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package event implements type-level constraints for dealing with the events -// communicated via BitTorrent announce. -package event - -import ( - "errors" - "strings" -) - -// ErrUnknownEvent is returned when New fails to return an event. -var ErrUnknownEvent = errors.New("unknown event") - -// Event represents an event done by a BitTorrent client. -type Event uint8 - -const ( - // None is the event when a BitTorrent client announces due to time lapsed - // since the previous announce. - None Event = iota - - // Started is the event sent by a BitTorrent client when it joins a swarm. - Started - - // Stopped is the event sent by a BitTorrent client when it leaves a swarm. - Stopped - - // Completed is the event sent by a BitTorrent client when it finishes - // downloading all of the required chunks. - Completed -) - -var ( - eventToString = make(map[Event]string) - stringToEvent = make(map[string]Event) -) - -func init() { - eventToString[None] = "none" - eventToString[Started] = "started" - eventToString[Stopped] = "stopped" - eventToString[Completed] = "completed" - - stringToEvent[""] = None - - for k, v := range eventToString { - stringToEvent[v] = k - } -} - -// New returns the proper Event given a string. -func New(eventStr string) (Event, error) { - if e, ok := stringToEvent[strings.ToLower(eventStr)]; ok { - return e, nil - } - - return None, ErrUnknownEvent -} - -// String implements Stringer for an event. -func (e Event) String() string { - if name, ok := eventToString[e]; ok { - return name - } - - panic("event: event has no associated name") -} diff --git a/pkg/event/event_test.go b/pkg/event/event_test.go deleted file mode 100644 index d586f25..0000000 --- a/pkg/event/event_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package event - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNew(t *testing.T) { - var table = []struct { - data string - expected Event - expectedErr error - }{ - {"", None, nil}, - {"NONE", None, nil}, - {"none", None, nil}, - {"started", Started, nil}, - {"stopped", Stopped, nil}, - {"completed", Completed, nil}, - {"notAnEvent", None, ErrUnknownEvent}, - } - - for _, tt := range table { - got, err := New(tt.data) - assert.Equal(t, err, tt.expectedErr, "errors should equal the expected value") - assert.Equal(t, got, tt.expected, "events should equal the expected value") - } -} diff --git a/pkg/random/peer.go b/pkg/random/peer.go deleted file mode 100644 index c58a11f..0000000 --- a/pkg/random/peer.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package random - -import ( - "math/rand" - "net" - - "github.com/chihaya/chihaya" -) - -// Peer generates a random chihaya.Peer. -// -// prefix is the prefix to use for the peer ID. If len(prefix) > 20, it will be -// truncated to 20 characters. If len(prefix) < 20, it will be padded with an -// alphanumeric random string to have 20 characters. -// -// v6 indicates whether an IPv6 address should be generated. -// Regardless of the length of the generated IP address, its bytes will have -// values in [1,254]. -// -// minPort and maxPort describe the range for the randomly generated port, where -// minPort <= port < maxPort. -// minPort and maxPort will be checked and altered so that -// 1 <= minPort <= maxPort <= 65536. -// If minPort == maxPort, port will be set to minPort. -func Peer(r *rand.Rand, prefix string, v6 bool, minPort, maxPort int) chihaya.Peer { - var ( - port uint16 - ip net.IP - ) - - if minPort <= 0 { - minPort = 1 - } - if maxPort > 65536 { - maxPort = 65536 - } - if maxPort < minPort { - maxPort = minPort - } - if len(prefix) > 20 { - prefix = prefix[:20] - } - - if minPort == maxPort { - port = uint16(minPort) - } else { - port = uint16(r.Int63()%int64(maxPort-minPort)) + uint16(minPort) - } - - if v6 { - b := make([]byte, 16) - ip = net.IP(b) - } else { - b := make([]byte, 4) - ip = net.IP(b) - } - - for i := range ip { - b := r.Intn(254) + 1 - ip[i] = byte(b) - } - - prefix = prefix + AlphaNumericString(r, 20-len(prefix)) - - return chihaya.Peer{ - ID: chihaya.PeerIDFromString(prefix), - Port: port, - IP: ip, - } -} diff --git a/pkg/random/peer_test.go b/pkg/random/peer_test.go deleted file mode 100644 index 636f42d..0000000 --- a/pkg/random/peer_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package random - -import ( - "math/rand" - "net" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPeer(t *testing.T) { - r := rand.New(rand.NewSource(0)) - - for i := 0; i < 100; i++ { - minPort := 2000 - maxPort := 2010 - p := Peer(r, "", false, minPort, maxPort) - assert.Equal(t, 20, len(p.ID)) - assert.True(t, p.Port >= uint16(minPort) && p.Port < uint16(maxPort)) - assert.NotNil(t, p.IP.To4()) - } - - for i := 0; i < 100; i++ { - minPort := 2000 - maxPort := 2010 - p := Peer(r, "", true, minPort, maxPort) - assert.Equal(t, 20, len(p.ID)) - assert.True(t, p.Port >= uint16(minPort) && p.Port < uint16(maxPort)) - assert.True(t, len(p.IP) == net.IPv6len) - } - - p := Peer(r, "abcdefghijklmnopqrst", false, 2000, 2000) - assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID[:])) - assert.Equal(t, uint16(2000), p.Port) - - p = Peer(r, "abcdefghijklmnopqrstUVWXYZ", true, -10, -5) - assert.Equal(t, "abcdefghijklmnopqrst", string(p.ID[:])) - assert.True(t, p.Port >= uint16(1) && p.Port <= uint16(65535)) -} diff --git a/pkg/random/string.go b/pkg/random/string.go deleted file mode 100644 index bdfa9f2..0000000 --- a/pkg/random/string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package random - -import "math/rand" - -// AlphaNumeric is an alphabet with all lower- and uppercase letters and -// numbers. -const AlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - -// AlphaNumericString is a shorthand for String(r, l, AlphaNumeric). -func AlphaNumericString(r rand.Source, l int) string { - return String(r, l, AlphaNumeric) -} - -// String generates a random string of length l, containing only runes from -// the alphabet using the random source r. -func String(r rand.Source, l int, alphabet string) string { - b := make([]byte, l) - for i := range b { - b[i] = alphabet[r.Int63()%int64(len(alphabet))] - } - return string(b) -} diff --git a/pkg/random/string_test.go b/pkg/random/string_test.go deleted file mode 100644 index e2bd0b2..0000000 --- a/pkg/random/string_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package random - -import ( - "math/rand" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAlphaNumericString(t *testing.T) { - r := rand.NewSource(0) - - s := AlphaNumericString(r, 0) - assert.Equal(t, 0, len(s)) - - s = AlphaNumericString(r, 10) - assert.Equal(t, 10, len(s)) - - for i := 0; i < 100; i++ { - s := AlphaNumericString(r, 10) - for _, c := range s { - assert.True(t, strings.Contains(AlphaNumeric, string(c))) - } - } -} diff --git a/pkg/stopper/stopper.go b/pkg/stopper/stopper.go deleted file mode 100644 index ddf1a21..0000000 --- a/pkg/stopper/stopper.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package stopper - -import ( - "sync" -) - -// AlreadyStopped is a closed error channel to be used by StopperFuncs when -// an element was already stopped. -var AlreadyStopped <-chan error - -// AlreadyStoppedFunc is a StopperFunc that returns AlreadyStopped. -var AlreadyStoppedFunc = func() <-chan error { return AlreadyStopped } - -func init() { - closeMe := make(chan error) - close(closeMe) - AlreadyStopped = closeMe -} - -// Stopper is an interface that allows a clean shutdown. -type Stopper interface { - // Stop returns a channel that indicates whether the stop was - // successful. - // The channel can either return one error or be closed. Closing the - // channel signals a clean shutdown. - // The Stop function should return immediately and perform the actual - // shutdown in a seperate goroutine. - Stop() <-chan error -} - -// StopGroup is a group that can be stopped. -type StopGroup struct { - stoppables []StopperFunc - stoppablesLock sync.Mutex -} - -// StopperFunc is a function that can be used to provide a clean shutdown. -type StopperFunc func() <-chan error - -// NewStopGroup creates a new StopGroup. -func NewStopGroup() *StopGroup { - return &StopGroup{ - stoppables: make([]StopperFunc, 0), - } -} - -// Add adds a Stopper to the StopGroup. -// On the next call to Stop(), the Stopper will be stopped. -func (cg *StopGroup) Add(toAdd Stopper) { - cg.stoppablesLock.Lock() - defer cg.stoppablesLock.Unlock() - - cg.stoppables = append(cg.stoppables, toAdd.Stop) -} - -// AddFunc adds a StopperFunc to the StopGroup. -// On the next call to Stop(), the StopperFunc will be called. -func (cg *StopGroup) AddFunc(toAddFunc StopperFunc) { - cg.stoppablesLock.Lock() - defer cg.stoppablesLock.Unlock() - - cg.stoppables = append(cg.stoppables, toAddFunc) -} - -// Stop stops all members of the StopGroup. -// Stopping will be done in a concurrent fashion. -// The slice of errors returned contains all errors returned by stopping the -// members. -func (cg *StopGroup) Stop() []error { - cg.stoppablesLock.Lock() - defer cg.stoppablesLock.Unlock() - - var errors []error - whenDone := make(chan struct{}) - - waitChannels := make([]<-chan error, 0, len(cg.stoppables)) - for _, toStop := range cg.stoppables { - waitFor := toStop() - if waitFor == nil { - panic("received a nil chan from Stop") - } - waitChannels = append(waitChannels, waitFor) - } - - go func() { - for _, waitForMe := range waitChannels { - err := <-waitForMe - if err != nil { - errors = append(errors, err) - } - } - close(whenDone) - }() - - <-whenDone - return errors -} diff --git a/server/http/config.go b/server/http/config.go deleted file mode 100644 index 9f14193..0000000 --- a/server/http/config.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package http - -import ( - "time" - - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -type httpConfig struct { - Addr string `yaml:"addr"` - RequestTimeout time.Duration `yaml:"request_timeout"` - ReadTimeout time.Duration `yaml:"read_timeout"` - WriteTimeout time.Duration `yaml:"write_timeout"` - AllowIPSpoofing bool `yaml:"allow_ip_spoofing"` - DualStackedPeers bool `yaml:"dual_stacked_peers"` - RealIPHeader string `yaml:"real_ip_header"` -} - -func newHTTPConfig(srvcfg *chihaya.ServerConfig) (*httpConfig, error) { - bytes, err := yaml.Marshal(srvcfg.Config) - if err != nil { - return nil, err - } - - var cfg httpConfig - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - return &cfg, nil -} diff --git a/server/http/query/query.go b/server/http/query/query.go deleted file mode 100644 index e8fe6e8..0000000 --- a/server/http/query/query.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package query implements a simple, fast URL parser designed to be used to -// parse parameters sent from BitTorrent clients. The last value of a key wins, -// except for they key "info_hash". -package query - -import ( - "errors" - "net/url" - "strconv" - "strings" - - "github.com/chihaya/chihaya" -) - -// ErrKeyNotFound is returned when a provided key has no value associated with -// it. -var ErrKeyNotFound = errors.New("query: value for the provided key does not exist") - -// ErrInvalidInfohash is returned when parsing a query encounters an infohash -// with invalid length. -var ErrInvalidInfohash = errors.New("query: invalid infohash") - -// Query represents a parsed URL.Query. -type Query struct { - query string - params map[string]string - infoHashes []chihaya.InfoHash -} - -// New parses a raw URL query. -func New(query string) (*Query, error) { - var ( - keyStart, keyEnd int - valStart, valEnd int - - onKey = true - - q = &Query{ - query: query, - infoHashes: nil, - params: make(map[string]string), - } - ) - - for i, length := 0, len(query); i < length; i++ { - separator := query[i] == '&' || query[i] == ';' || query[i] == '?' - last := i == length-1 - - if separator || last { - if onKey && !last { - keyStart = i + 1 - continue - } - - if last && !separator && !onKey { - valEnd = i - } - - keyStr, err := url.QueryUnescape(query[keyStart : keyEnd+1]) - if err != nil { - return nil, err - } - - var valStr string - - if valEnd > 0 { - valStr, err = url.QueryUnescape(query[valStart : valEnd+1]) - if err != nil { - return nil, err - } - } - - if keyStr == "info_hash" { - if len(valStr) != 20 { - return nil, ErrInvalidInfohash - } - q.infoHashes = append(q.infoHashes, chihaya.InfoHashFromString(valStr)) - } else { - q.params[strings.ToLower(keyStr)] = valStr - } - - valEnd = 0 - onKey = true - keyStart = i + 1 - - } else if query[i] == '=' { - onKey = false - valStart = i + 1 - valEnd = 0 - } else if onKey { - keyEnd = i - } else { - valEnd = i - } - } - - return q, nil -} - -// String returns a string parsed from a query. Every key can be returned as a -// string because they are encoded in the URL as strings. -func (q *Query) String(key string) (string, error) { - val, exists := q.params[key] - if !exists { - return "", ErrKeyNotFound - } - return val, nil -} - -// Uint64 returns a uint parsed from a query. After being called, it is safe to -// cast the uint64 to your desired length. -func (q *Query) Uint64(key string) (uint64, error) { - str, exists := q.params[key] - if !exists { - return 0, ErrKeyNotFound - } - - val, err := strconv.ParseUint(str, 10, 64) - if err != nil { - return 0, err - } - - return val, nil -} - -// InfoHashes returns a list of requested infohashes. -func (q *Query) InfoHashes() []chihaya.InfoHash { - return q.infoHashes -} diff --git a/server/http/query/query_test.go b/server/http/query/query_test.go deleted file mode 100644 index f421f1d..0000000 --- a/server/http/query/query_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package query - -import ( - "net/url" - "testing" -) - -var ( - baseAddr = "https://www.subdomain.tracker.com:80/" - testInfoHash = "01234567890123456789" - testPeerID = "-TEST01-6wfG2wk6wWLc" - - ValidAnnounceArguments = []url.Values{ - url.Values{"peer_id": {testPeerID}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}}, - url.Values{"peer_id": {testPeerID}, "ip": {"192.168.0.1"}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}}, - url.Values{"peer_id": {testPeerID}, "ip": {"192.168.0.1"}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "numwant": {"28"}}, - url.Values{"peer_id": {testPeerID}, "ip": {"192.168.0.1"}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "event": {"stopped"}}, - url.Values{"peer_id": {testPeerID}, "ip": {"192.168.0.1"}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "event": {"started"}, "numwant": {"13"}}, - url.Values{"peer_id": {testPeerID}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "no_peer_id": {"1"}}, - url.Values{"peer_id": {testPeerID}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "compact": {"0"}, "no_peer_id": {"1"}}, - url.Values{"peer_id": {testPeerID}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "compact": {"0"}, "no_peer_id": {"1"}, "key": {"peerKey"}}, - url.Values{"peer_id": {testPeerID}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "compact": {"0"}, "no_peer_id": {"1"}, "key": {"peerKey"}, "trackerid": {"trackerId"}}, - url.Values{"peer_id": {"%3Ckey%3A+0x90%3E"}, "port": {"6881"}, "downloaded": {"1234"}, "left": {"4321"}, "compact": {"0"}, "no_peer_id": {"1"}, "key": {"peerKey"}, "trackerid": {"trackerId"}}, - url.Values{"peer_id": {"%3Ckey%3A+0x90%3E"}, "compact": {"1"}}, - url.Values{"peer_id": {""}, "compact": {""}}, - } - - InvalidQueries = []string{ - baseAddr + "announce/?" + "info_hash=%0%a", - } -) - -func mapArrayEqual(boxed map[string][]string, unboxed map[string]string) bool { - if len(boxed) != len(unboxed) { - return false - } - - for mapKey, mapVal := range boxed { - // Always expect box to hold only one element - if len(mapVal) != 1 || mapVal[0] != unboxed[mapKey] { - return false - } - } - - return true -} - -func TestValidQueries(t *testing.T) { - for parseIndex, parseVal := range ValidAnnounceArguments { - parsedQueryObj, err := New(baseAddr + "announce/?" + parseVal.Encode()) - if err != nil { - t.Error(err) - } - - if !mapArrayEqual(parseVal, parsedQueryObj.params) { - t.Errorf("Incorrect parse at item %d.\n Expected=%v\n Recieved=%v\n", parseIndex, parseVal, parsedQueryObj.params) - } - } -} - -func TestInvalidQueries(t *testing.T) { - for parseIndex, parseStr := range InvalidQueries { - parsedQueryObj, err := New(parseStr) - if err == nil { - t.Error("Should have produced error", parseIndex) - } - - if parsedQueryObj != nil { - t.Error("Should be nil after error", parsedQueryObj, parseIndex) - } - } -} - -func BenchmarkParseQuery(b *testing.B) { - for bCount := 0; bCount < b.N; bCount++ { - for parseIndex, parseStr := range ValidAnnounceArguments { - parsedQueryObj, err := New(baseAddr + "announce/?" + parseStr.Encode()) - if err != nil { - b.Error(err, parseIndex) - b.Log(parsedQueryObj) - } - } - } -} - -func BenchmarkURLParseQuery(b *testing.B) { - for bCount := 0; bCount < b.N; bCount++ { - for parseIndex, parseStr := range ValidAnnounceArguments { - parsedQueryObj, err := url.ParseQuery(baseAddr + "announce/?" + parseStr.Encode()) - if err != nil { - b.Error(err, parseIndex) - b.Log(parsedQueryObj) - } - } - } -} diff --git a/server/http/request.go b/server/http/request.go deleted file mode 100644 index 5d17aad..0000000 --- a/server/http/request.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package http - -import ( - "net" - "net/http" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/event" - "github.com/chihaya/chihaya/server/http/query" - "github.com/chihaya/chihaya/tracker" -) - -func announceRequest(r *http.Request, cfg *httpConfig) (*chihaya.AnnounceRequest, error) { - q, err := query.New(r.URL.RawQuery) - if err != nil { - return nil, err - } - - request := &chihaya.AnnounceRequest{Params: q} - - eventStr, err := q.String("event") - if err == query.ErrKeyNotFound { - eventStr = "" - } else if err != nil { - return nil, tracker.ClientError("failed to parse parameter: event") - } - request.Event, err = event.New(eventStr) - if err != nil { - return nil, tracker.ClientError("failed to provide valid client event") - } - - compactStr, _ := q.String("compact") - request.Compact = compactStr != "" && compactStr != "0" - - infoHashes := q.InfoHashes() - if len(infoHashes) < 1 { - return nil, tracker.ClientError("no info_hash parameter supplied") - } - if len(infoHashes) > 1 { - return nil, tracker.ClientError("multiple info_hash parameters supplied") - } - request.InfoHash = infoHashes[0] - - peerID, err := q.String("peer_id") - if err != nil { - return nil, tracker.ClientError("failed to parse parameter: peer_id") - } - if len(peerID) != 20 { - return nil, tracker.ClientError("failed to provide valid peer_id") - } - request.PeerID = chihaya.PeerIDFromString(peerID) - - request.Left, err = q.Uint64("left") - if err != nil { - return nil, tracker.ClientError("failed to parse parameter: left") - } - - request.Downloaded, err = q.Uint64("downloaded") - if err != nil { - return nil, tracker.ClientError("failed to parse parameter: downloaded") - } - - request.Uploaded, err = q.Uint64("uploaded") - if err != nil { - return nil, tracker.ClientError("failed to parse parameter: uploaded") - } - - numwant, _ := q.Uint64("numwant") - request.NumWant = int32(numwant) - - port, err := q.Uint64("port") - if err != nil { - return nil, tracker.ClientError("failed to parse parameter: port") - } - request.Port = uint16(port) - - v4, v6, err := requestedIP(q, r, cfg) - if err != nil { - return nil, tracker.ClientError("failed to parse remote IP") - } - request.IPv4 = v4 - request.IPv6 = v6 - - return request, nil -} - -func scrapeRequest(r *http.Request, cfg *httpConfig) (*chihaya.ScrapeRequest, error) { - q, err := query.New(r.URL.RawQuery) - if err != nil { - return nil, err - } - - infoHashes := q.InfoHashes() - if len(infoHashes) < 1 { - return nil, tracker.ClientError("no info_hash parameter supplied") - } - - request := &chihaya.ScrapeRequest{ - InfoHashes: infoHashes, - Params: q, - } - - return request, nil -} - -// requestedIP returns the IP address for a request. If there are multiple in -// the request, one IPv4 and one IPv6 will be returned. -func requestedIP(p chihaya.Params, r *http.Request, cfg *httpConfig) (v4, v6 net.IP, err error) { - var done bool - - if cfg.AllowIPSpoofing { - if str, e := p.String("ip"); e == nil { - if v4, v6, done = getIPs(str, v4, v6, cfg); done { - return - } - } - - if str, e := p.String("ipv4"); e == nil { - if v4, v6, done = getIPs(str, v4, v6, cfg); done { - return - } - } - - if str, e := p.String("ipv6"); e == nil { - if v4, v6, done = getIPs(str, v4, v6, cfg); done { - return - } - } - } - - if cfg.RealIPHeader != "" { - if xRealIPs, ok := r.Header[cfg.RealIPHeader]; ok { - if v4, v6, done = getIPs(string(xRealIPs[0]), v4, v6, cfg); done { - return - } - } - } else { - if r.RemoteAddr == "" && v4 == nil { - if v4, v6, done = getIPs("127.0.0.1", v4, v6, cfg); done { - return - } - } - - if v4, v6, done = getIPs(r.RemoteAddr, v4, v6, cfg); done { - return - } - } - - if v4 == nil && v6 == nil { - err = tracker.ClientError("failed to parse IP address") - } - - return -} - -func getIPs(ipstr string, ipv4, ipv6 net.IP, cfg *httpConfig) (net.IP, net.IP, bool) { - host, _, err := net.SplitHostPort(ipstr) - if err != nil { - host = ipstr - } - - if ip := net.ParseIP(host); ip != nil { - ipTo4 := ip.To4() - if ipv4 == nil && ipTo4 != nil { - ipv4 = ipTo4 - } else if ipv6 == nil && ipTo4 == nil { - ipv6 = ip - } - } - - var done bool - if cfg.DualStackedPeers { - done = ipv4 != nil && ipv6 != nil - } else { - done = ipv4 != nil || ipv6 != nil - } - - return ipv4, ipv6, done -} diff --git a/server/http/server.go b/server/http/server.go deleted file mode 100644 index bcd9ac1..0000000 --- a/server/http/server.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package http - -import ( - "errors" - "log" - "net" - "net/http" - - "github.com/julienschmidt/httprouter" - "github.com/tylerb/graceful" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - server.Register("http", constructor) -} - -func constructor(srvcfg *chihaya.ServerConfig, tkr *tracker.Tracker) (server.Server, error) { - cfg, err := newHTTPConfig(srvcfg) - if err != nil { - return nil, errors.New("http: invalid config: " + err.Error()) - } - - return &httpServer{ - cfg: cfg, - tkr: tkr, - }, nil -} - -type httpServer struct { - cfg *httpConfig - tkr *tracker.Tracker - grace *graceful.Server -} - -// Start runs the server and blocks until it has exited. -// -// It panics if the server exits unexpectedly. -func (s *httpServer) Start() { - s.grace = &graceful.Server{ - Server: &http.Server{ - Addr: s.cfg.Addr, - Handler: s.routes(), - ReadTimeout: s.cfg.ReadTimeout, - WriteTimeout: s.cfg.WriteTimeout, - }, - Timeout: s.cfg.RequestTimeout, - NoSignalHandling: true, - ConnState: func(conn net.Conn, state http.ConnState) { - switch state { - case http.StateNew: - //stats.RecordEvent(stats.AcceptedConnection) - - case http.StateClosed: - //stats.RecordEvent(stats.ClosedConnection) - - case http.StateHijacked: - panic("http: connection impossibly hijacked") - - // Ignore the following cases. - case http.StateActive, http.StateIdle: - - default: - panic("http: connection transitioned to unknown state") - } - }, - } - s.grace.SetKeepAlivesEnabled(false) - - if err := s.grace.ListenAndServe(); err != nil { - if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { - log.Printf("Failed to gracefully run HTTP server: %s", err.Error()) - panic(err) - } - } - - log.Println("HTTP server shut down cleanly") -} - -// Stop stops the server and blocks until the server has exited. -func (s *httpServer) Stop() { - s.grace.Stop(s.grace.Timeout) - <-s.grace.StopChan() -} - -func (s *httpServer) routes() *httprouter.Router { - r := httprouter.New() - r.GET("/announce", s.serveAnnounce) - r.GET("/scrape", s.serveScrape) - return r -} - -func (s *httpServer) serveAnnounce(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - req, err := announceRequest(r, s.cfg) - if err != nil { - writeError(w, err) - return - } - - resp, err := s.tkr.HandleAnnounce(req) - if err != nil { - writeError(w, err) - return - } - - err = writeAnnounceResponse(w, resp) - if err != nil { - log.Println("error serializing response", err) - } -} - -func (s *httpServer) serveScrape(w http.ResponseWriter, r *http.Request, p httprouter.Params) { - req, err := scrapeRequest(r, s.cfg) - if err != nil { - writeError(w, err) - return - } - - resp, err := s.tkr.HandleScrape(req) - if err != nil { - writeError(w, err) - return - } - - writeScrapeResponse(w, resp) -} diff --git a/server/http/writer.go b/server/http/writer.go deleted file mode 100644 index 5de6c3d..0000000 --- a/server/http/writer.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package http - -import ( - "net/http" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/bencode" - "github.com/chihaya/chihaya/tracker" -) - -func writeError(w http.ResponseWriter, err error) error { - message := "internal server error" - if _, clientErr := err.(tracker.ClientError); clientErr { - message = err.Error() - } - - w.WriteHeader(http.StatusOK) - return bencode.NewEncoder(w).Encode(bencode.Dict{ - "failure reason": message, - }) -} - -func writeAnnounceResponse(w http.ResponseWriter, resp *chihaya.AnnounceResponse) error { - bdict := bencode.Dict{ - "complete": resp.Complete, - "incomplete": resp.Incomplete, - "interval": resp.Interval, - "min interval": resp.MinInterval, - } - - // Add the peers to the dictionary in the compact format. - if resp.Compact { - var IPv4CompactDict, IPv6CompactDict []byte - - // Add the IPv4 peers to the dictionary. - for _, peer := range resp.IPv4Peers { - IPv4CompactDict = append(IPv4CompactDict, compact(peer)...) - } - if len(IPv4CompactDict) > 0 { - bdict["peers"] = IPv4CompactDict - } - - // Add the IPv6 peers to the dictionary. - for _, peer := range resp.IPv6Peers { - IPv6CompactDict = append(IPv6CompactDict, compact(peer)...) - } - if len(IPv6CompactDict) > 0 { - bdict["peers6"] = IPv6CompactDict - } - - return bencode.NewEncoder(w).Encode(bdict) - } - - // Add the peers to the dictionary. - var peers []bencode.Dict - for _, peer := range resp.IPv4Peers { - peers = append(peers, dict(peer)) - } - for _, peer := range resp.IPv6Peers { - peers = append(peers, dict(peer)) - } - bdict["peers"] = peers - - return bencode.NewEncoder(w).Encode(bdict) -} - -func writeScrapeResponse(w http.ResponseWriter, resp *chihaya.ScrapeResponse) error { - filesDict := bencode.NewDict() - for infohash, scrape := range resp.Files { - filesDict[string(infohash[:])] = bencode.Dict{ - "complete": scrape.Complete, - "incomplete": scrape.Incomplete, - } - } - - return bencode.NewEncoder(w).Encode(bencode.Dict{ - "files": filesDict, - }) -} - -func compact(peer chihaya.Peer) (buf []byte) { - buf = []byte(peer.IP) - buf = append(buf, byte(peer.Port>>8)) - buf = append(buf, byte(peer.Port&0xff)) - return -} - -func dict(peer chihaya.Peer) bencode.Dict { - return bencode.Dict{ - "peer id": string(peer.ID[:]), - "ip": peer.IP.String(), - "port": peer.Port, - } -} diff --git a/server/http/writer_test.go b/server/http/writer_test.go deleted file mode 100644 index 00c345c..0000000 --- a/server/http/writer_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package http - -import ( - "net/http/httptest" - "testing" - - "github.com/chihaya/chihaya/tracker" - "github.com/stretchr/testify/assert" -) - -func TestWriteError(t *testing.T) { - var table = []struct { - reason, expected string - }{ - {"hello world", "d14:failure reason11:hello worlde"}, - {"what's up", "d14:failure reason9:what's upe"}, - } - - for _, tt := range table { - r := httptest.NewRecorder() - err := writeError(r, tracker.ClientError(tt.reason)) - assert.Nil(t, err) - assert.Equal(t, r.Body.String(), tt.expected) - } -} - -func TestWriteStatus(t *testing.T) { - r := httptest.NewRecorder() - err := writeError(r, tracker.ClientError("something is missing")) - assert.Nil(t, err) - assert.Equal(t, r.Body.String(), "d14:failure reason20:something is missinge") -} diff --git a/server/pool.go b/server/pool.go deleted file mode 100644 index 9aa368b..0000000 --- a/server/pool.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package server - -import ( - "sync" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -// Pool represents a running pool of servers. -type Pool struct { - servers []Server - wg sync.WaitGroup -} - -// StartPool creates a new pool of servers specified by the provided -// configuration and runs them. -func StartPool(cfgs []chihaya.ServerConfig, tkr *tracker.Tracker) (*Pool, error) { - var toReturn Pool - - for _, cfg := range cfgs { - srv, err := New(&cfg, tkr) - if err != nil { - return nil, err - } - - toReturn.wg.Add(1) - go func(srv Server) { - defer toReturn.wg.Done() - srv.Start() - }(srv) - - toReturn.servers = append(toReturn.servers, srv) - } - - return &toReturn, nil -} - -// Stop safely shuts down a pool of servers. -func (p *Pool) Stop() { - for _, srv := range p.servers { - srv.Stop() - } - p.wg.Wait() -} diff --git a/server/prometheus/prometheus.go b/server/prometheus/prometheus.go deleted file mode 100644 index 1dbf3f1..0000000 --- a/server/prometheus/prometheus.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package prometheus implements a chihaya Server for serving metrics to -// Prometheus. -package prometheus - -import ( - "errors" - "log" - "net" - "net/http" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/tylerb/graceful" - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - server.Register("prometheus", constructor) -} - -func constructor(srvcfg *chihaya.ServerConfig, tkr *tracker.Tracker) (server.Server, error) { - cfg, err := NewServerConfig(srvcfg) - if err != nil { - return nil, errors.New("prometheus: invalid config: " + err.Error()) - } - - return &Server{ - cfg: cfg, - }, nil -} - -// ServerConfig represents the configuration options for a -// PrometheusServer. -type ServerConfig struct { - Addr string `yaml:"addr"` - ShutdownTimeout time.Duration `yaml:"shutdown_timeout"` - ReadTimeout time.Duration `yaml:"read_timeout"` - WriteTimeout time.Duration `yaml:"write_timeout"` -} - -// NewServerConfig marshals a chihaya.ServerConfig and unmarshals it -// into a more specific prometheus ServerConfig. -func NewServerConfig(srvcfg *chihaya.ServerConfig) (*ServerConfig, error) { - bytes, err := yaml.Marshal(srvcfg.Config) - if err != nil { - return nil, err - } - - var cfg ServerConfig - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - return &cfg, nil -} - -// Server implements a chihaya Server for serving metrics to Prometheus. -type Server struct { - cfg *ServerConfig - grace *graceful.Server -} - -var _ server.Server = &Server{} - -// Start starts the prometheus server and blocks until it exits. -// -// It panics if the server exits unexpectedly. -func (s *Server) Start() { - s.grace = &graceful.Server{ - Server: &http.Server{ - Addr: s.cfg.Addr, - Handler: prometheus.Handler(), - ReadTimeout: s.cfg.ReadTimeout, - WriteTimeout: s.cfg.WriteTimeout, - }, - Timeout: s.cfg.ShutdownTimeout, - NoSignalHandling: true, - } - - if err := s.grace.ListenAndServe(); err != nil { - if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { - log.Printf("Failed to gracefully run Prometheus server: %s", err.Error()) - panic(err) - } - } - - log.Println("Prometheus server shut down cleanly") -} - -// Stop stops the prometheus server and blocks until it exits. -func (s *Server) Stop() { - s.grace.Stop(s.cfg.ShutdownTimeout) - <-s.grace.StopChan() -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 1adcb16..0000000 --- a/server/server.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package server implements an abstraction over servers meant to be run . -// alongside a tracker. -// -// Servers may be implementations of different transport protocols or have their -// own custom behavior. -package server - -import ( - "fmt" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -var constructors = make(map[string]Constructor) - -// Constructor is a function that creates a new Server. -type Constructor func(*chihaya.ServerConfig, *tracker.Tracker) (Server, error) - -// Register makes a Constructor available by the provided name. -// -// If this function is called twice with the same name or if the Constructor is -// nil, it panics. -func Register(name string, con Constructor) { - if con == nil { - panic("server: could not register nil Constructor") - } - if _, dup := constructors[name]; dup { - panic("server: could not register duplicate Constructor: " + name) - } - constructors[name] = con -} - -// New creates a Server specified by a configuration. -func New(cfg *chihaya.ServerConfig, tkr *tracker.Tracker) (Server, error) { - con, ok := constructors[cfg.Name] - if !ok { - return nil, fmt.Errorf("server: unknown Constructor %q (forgotten import?)", cfg.Name) - } - return con(cfg, tkr) -} - -// Server represents one instance of a server accessing the tracker. -type Server interface { - // Start starts a server and blocks until the server exits. - // - // It should panic if the server exits unexpectedly. - Start() - - // Stop stops a server and blocks until the server exits. - Stop() -} diff --git a/server/store/README.md b/server/store/README.md deleted file mode 100644 index 05ff3ff..0000000 --- a/server/store/README.md +++ /dev/null @@ -1,43 +0,0 @@ -## The store Package - -The `store` package offers a storage interface and middlewares sufficient to run a public tracker based on it. - -### Architecture - -The store consists of three parts: -- A set of interfaces, tests based on these interfaces and the store logic, unifying these interfaces into the store -- Drivers, implementing the store interfaces and -- Middleware that depends on the store - -The store interfaces are `IPStore`, `PeerStore` and `StringStore`. -During runtime, each of them will be implemented by a driver. -Even though all different drivers for one interface provide the same functionality, their behaviour can be very different. -For example: The memory implementation keeps all state in-memory - this is very fast, but not persistent, it loses its state on every restart. -A database-backed driver on the other hand could provide persistence, at the cost of performance. - -The pluggable design of Chihaya allows for the different interfaces to use different drivers. -For example: A typical use case of the `StringStore` is to provide blacklists or whitelists for infohashes/client IDs/.... -You'd typically want these lists to be persistent, so you'd choose a driver that provides persistence. -The `PeerStore` on the other hand rarely needs to be persistent, as all peer state will be restored after one announce interval. -You'd therefore typically choose a very performant but non-persistent driver for the `PeerStore`. - -### Testing - -The main store package also contains a set of tests and benchmarks for drivers. -Both use the store interfaces and can work with any driver that implements these interfaces. -The tests verify that the driver behaves as specified by the interface and its documentation. -The benchmarks can be used to compare performance of a wide range of operations on the interfaces. - -This makes it very easy to implement a new driver: -All functions that are part of the store interfaces can be tested easily with the tests that come with the store package. -Generally the memory implementation can be used as a guideline for implementing new drivers. - -Both benchmarks and tests require a clean state to work correctly. -All of the test and benchmark functions therefore take a `*DriverConfig` as a parameter, this should be used to configure the driver in a way that it provides a clean state for every test or benchmark. -For example: Imagine a file-based driver that achieves persistence by storing its state in a file. -It must then be possible to provide the location of this file in the `'DriverConfig`, so that every different benchmark gets to work with a new file. - -Most benchmarks come in two flavors: The "normal" version and the "1K" version. -A normal benchmark uses the same value over and over again to benchmark one operation. -A 1K benchmark uses a different value from a set of 1000 values for every iteration, this can show caching effects, if the driver uses them. -The 1K benchmarks require a little more computation to select the values and thus typically yield slightly lower results even for a "perfect" cache, i.e. the memory implementation. diff --git a/server/store/ip_store.go b/server/store/ip_store.go deleted file mode 100644 index 7d7fc93..0000000 --- a/server/store/ip_store.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "fmt" - "net" - - "github.com/chihaya/chihaya/pkg/stopper" -) - -var ipStoreDrivers = make(map[string]IPStoreDriver) - -// IPStore represents an interface for manipulating IPs and IP ranges. -type IPStore interface { - // AddIP adds a single IP address to the IPStore. - AddIP(ip net.IP) error - - // AddNetwork adds a range of IP addresses, denoted by a network in CIDR - // notation, to the IPStore. - AddNetwork(network string) error - - // HasIP returns whether the given IP address is contained in the IPStore - // or belongs to any of the stored networks. - HasIP(ip net.IP) (bool, error) - - // HasAnyIP returns whether any of the given IP addresses are contained - // in the IPStore or belongs to any of the stored networks. - HasAnyIP(ips []net.IP) (bool, error) - - // HassAllIPs returns whether all of the given IP addresses are - // contained in the IPStore or belongs to any of the stored networks. - HasAllIPs(ips []net.IP) (bool, error) - - // RemoveIP removes a single IP address from the IPStore. - // - // This wil not remove the given address from any networks it belongs to - // that are stored in the IPStore. - // - // Returns ErrResourceDoesNotExist if the given IP address is not - // contained in the store. - RemoveIP(ip net.IP) error - - // RemoveNetwork removes a range of IP addresses that was previously - // added through AddNetwork. - // - // The given network must not, as a string, match the previously added - // network, but rather denote the same network, e.g. if the network - // 192.168.22.255/24 was added, removing the network 192.168.22.123/24 - // will succeed. - // - // Returns ErrResourceDoesNotExist if the given network is not - // contained in the store. - RemoveNetwork(network string) error - - // Stopper provides the Stop method that stops the IPStore. - // Stop should shut down the IPStore in a separate goroutine and send - // an error to the channel if the shutdown failed. If the shutdown - // was successful, the channel is to be closed. - stopper.Stopper -} - -// IPStoreDriver represents an interface for creating a handle to the -// storage of IPs. -type IPStoreDriver interface { - New(*DriverConfig) (IPStore, error) -} - -// RegisterIPStoreDriver makes a driver available by the provided name. -// -// If this function is called twice with the same name or if the driver is nil, -// it panics. -func RegisterIPStoreDriver(name string, driver IPStoreDriver) { - if driver == nil { - panic("store: could not register nil IPStoreDriver") - } - if _, dup := ipStoreDrivers[name]; dup { - panic("store: could not register duplicate IPStoreDriver: " + name) - } - ipStoreDrivers[name] = driver -} - -// OpenIPStore returns an IPStore specified by a configuration. -func OpenIPStore(cfg *DriverConfig) (IPStore, error) { - driver, ok := ipStoreDrivers[cfg.Name] - if !ok { - return nil, fmt.Errorf("store: unknown IPStoreDriver %q (forgotten import?)", cfg) - } - - return driver.New(cfg) -} diff --git a/server/store/memory/ip_store.go b/server/store/memory/ip_store.go deleted file mode 100644 index 8b01e66..0000000 --- a/server/store/memory/ip_store.go +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "net" - "sync" - - "github.com/mrd0ll4r/netmatch" - - "github.com/chihaya/chihaya/server/store" -) - -func init() { - store.RegisterIPStoreDriver("memory", &ipStoreDriver{}) -} - -type ipStoreDriver struct{} - -func (d *ipStoreDriver) New(_ *store.DriverConfig) (store.IPStore, error) { - return &ipStore{ - ips: make(map[[16]byte]struct{}), - networks: netmatch.New(), - closed: make(chan struct{}), - }, nil -} - -// ipStore implements store.IPStore using an in-memory map of byte arrays and -// a trie-like structure. -type ipStore struct { - ips map[[16]byte]struct{} - networks *netmatch.Trie - closed chan struct{} - sync.RWMutex -} - -var ( - _ store.IPStore = &ipStore{} - v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff} -) - -// key converts an IP address to a [16]byte. -// The byte array can then be used as a key for a map, unlike net.IP, which is a -// []byte. -// If an IPv4 address is specified, it will be prefixed with -// the net.v4InV6Prefix and thus becomes a valid IPv6 address. -func key(ip net.IP) [16]byte { - var array [16]byte - - if len(ip) == net.IPv4len { - copy(array[:], v4InV6Prefix) - copy(array[12:], ip) - } else { - copy(array[:], ip) - } - return array -} - -func (s *ipStore) AddNetwork(network string) error { - key, length, err := netmatch.ParseNetwork(network) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - return s.networks.Add(key, length) -} - -func (s *ipStore) AddIP(ip net.IP) error { - s.Lock() - defer s.Unlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - s.ips[key(ip)] = struct{}{} - - return nil -} - -func (s *ipStore) HasIP(ip net.IP) (bool, error) { - key := key(ip) - s.RLock() - defer s.RUnlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - _, ok := s.ips[key] - if ok { - return true, nil - } - - match, err := s.networks.Match(key) - if err != nil { - return false, err - } - - return match, nil -} - -func (s *ipStore) HasAnyIP(ips []net.IP) (bool, error) { - s.RLock() - defer s.RUnlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - for _, ip := range ips { - key := key(ip) - if _, ok := s.ips[key]; ok { - return true, nil - } - - match, err := s.networks.Match(key) - if err != nil { - return false, err - } - if match { - return true, nil - } - } - - return false, nil -} - -func (s *ipStore) HasAllIPs(ips []net.IP) (bool, error) { - s.RLock() - defer s.RUnlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - for _, ip := range ips { - key := key(ip) - if _, ok := s.ips[key]; !ok { - match, err := s.networks.Match(key) - if err != nil { - return false, err - } - if !match { - return false, nil - } - } - } - - return true, nil -} - -func (s *ipStore) RemoveIP(ip net.IP) error { - key := key(ip) - s.Lock() - defer s.Unlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - if _, ok := s.ips[key]; !ok { - return store.ErrResourceDoesNotExist - } - - delete(s.ips, key) - - return nil -} - -func (s *ipStore) RemoveNetwork(network string) error { - key, length, err := netmatch.ParseNetwork(network) - if err != nil { - return err - } - - s.Lock() - defer s.Unlock() - - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - err = s.networks.Remove(key, length) - if err != nil && err == netmatch.ErrNotContained { - return store.ErrResourceDoesNotExist - } - return err -} - -func (s *ipStore) Stop() <-chan error { - toReturn := make(chan error) - go func() { - s.Lock() - defer s.Unlock() - s.ips = make(map[[16]byte]struct{}) - s.networks = netmatch.New() - close(s.closed) - close(toReturn) - }() - return toReturn -} diff --git a/server/store/memory/ip_store_test.go b/server/store/memory/ip_store_test.go deleted file mode 100644 index 0497ab9..0000000 --- a/server/store/memory/ip_store_test.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "net" - "testing" - - "github.com/chihaya/chihaya/server/store" - - "github.com/stretchr/testify/require" -) - -var ( - v6 = net.ParseIP("0c22:384e:0:0c22:384e::68") - v4 = net.ParseIP("12.13.14.15") - v4s = net.ParseIP("12.13.14.15").To4() - - ipStoreTester = store.PrepareIPStoreTester(&ipStoreDriver{}) - ipStoreBenchmarker = store.PrepareIPStoreBenchmarker(&ipStoreDriver{}) - ipStoreTestConfig = &store.DriverConfig{} -) - -func TestKey(t *testing.T) { - var table = []struct { - input net.IP - expected [16]byte - }{ - {v6, [16]byte{12, 34, 56, 78, 0, 0, 12, 34, 56, 78, 0, 0, 0, 0, 0, 104}}, - {v4, [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 13, 14, 15}}, // IPv4 in IPv6 prefix - {v4s, [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 12, 13, 14, 15}}, // is equal to the one above, should produce equal output - } - - for _, tt := range table { - got := key(tt.input) - require.Equal(t, got, tt.expected) - } -} - -func TestIPStore(t *testing.T) { - ipStoreTester.TestIPStore(t, ipStoreTestConfig) -} - -func TestHasAllHasAny(t *testing.T) { - ipStoreTester.TestHasAllHasAny(t, ipStoreTestConfig) -} - -func TestNetworks(t *testing.T) { - ipStoreTester.TestNetworks(t, ipStoreTestConfig) -} - -func TestHasAllHasAnyNetworks(t *testing.T) { - ipStoreTester.TestHasAllHasAnyNetworks(t, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddV4(b *testing.B) { - ipStoreBenchmarker.AddV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddV6(b *testing.B) { - ipStoreBenchmarker.AddV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupV4(b *testing.B) { - ipStoreBenchmarker.LookupV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupV6(b *testing.B) { - ipStoreBenchmarker.LookupV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemoveV4(b *testing.B) { - ipStoreBenchmarker.AddRemoveV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemoveV6(b *testing.B) { - ipStoreBenchmarker.AddRemoveV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupNonExistV4(b *testing.B) { - ipStoreBenchmarker.LookupNonExistV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupNonExistV6(b *testing.B) { - ipStoreBenchmarker.LookupNonExistV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExistV4(b *testing.B) { - ipStoreBenchmarker.RemoveNonExistV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExistV6(b *testing.B) { - ipStoreBenchmarker.RemoveNonExistV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddV4Network(b *testing.B) { - ipStoreBenchmarker.AddV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddV6Network(b *testing.B) { - ipStoreBenchmarker.AddV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupV4Network(b *testing.B) { - ipStoreBenchmarker.LookupV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupV6Network(b *testing.B) { - ipStoreBenchmarker.LookupV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemoveV4Network(b *testing.B) { - ipStoreBenchmarker.AddRemoveV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemoveV6Network(b *testing.B) { - ipStoreBenchmarker.AddRemoveV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExistV4Network(b *testing.B) { - ipStoreBenchmarker.RemoveNonExistV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExistV6Network(b *testing.B) { - ipStoreBenchmarker.RemoveNonExistV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Add1KV4(b *testing.B) { - ipStoreBenchmarker.Add1KV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Add1KV6(b *testing.B) { - ipStoreBenchmarker.Add1KV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Lookup1KV4(b *testing.B) { - ipStoreBenchmarker.Lookup1KV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Lookup1KV6(b *testing.B) { - ipStoreBenchmarker.Lookup1KV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemove1KV4(b *testing.B) { - ipStoreBenchmarker.AddRemove1KV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemove1KV6(b *testing.B) { - ipStoreBenchmarker.AddRemove1KV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupNonExist1KV4(b *testing.B) { - ipStoreBenchmarker.LookupNonExist1KV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_LookupNonExist1KV6(b *testing.B) { - ipStoreBenchmarker.LookupNonExist1KV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExist1KV4(b *testing.B) { - ipStoreBenchmarker.RemoveNonExist1KV4(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExist1KV6(b *testing.B) { - ipStoreBenchmarker.RemoveNonExist1KV6(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Add1KV4Network(b *testing.B) { - ipStoreBenchmarker.Add1KV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Add1KV6Network(b *testing.B) { - ipStoreBenchmarker.Add1KV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Lookup1KV4Network(b *testing.B) { - ipStoreBenchmarker.Lookup1KV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_Lookup1KV6Network(b *testing.B) { - ipStoreBenchmarker.Lookup1KV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemove1KV4Network(b *testing.B) { - ipStoreBenchmarker.AddRemove1KV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_AddRemove1KV6Network(b *testing.B) { - ipStoreBenchmarker.AddRemove1KV6Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExist1KV4Network(b *testing.B) { - ipStoreBenchmarker.RemoveNonExist1KV4Network(b, ipStoreTestConfig) -} - -func BenchmarkIPStore_RemoveNonExist1KV6Network(b *testing.B) { - ipStoreBenchmarker.RemoveNonExist1KV6Network(b, ipStoreTestConfig) -} diff --git a/server/store/memory/peer_store.go b/server/store/memory/peer_store.go deleted file mode 100644 index 8654ab1..0000000 --- a/server/store/memory/peer_store.go +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "encoding/binary" - "log" - "net" - "runtime" - "sync" - "time" - - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server/store" -) - -func init() { - store.RegisterPeerStoreDriver("memory", &peerStoreDriver{}) -} - -type peerStoreDriver struct{} - -func (d *peerStoreDriver) New(storecfg *store.DriverConfig) (store.PeerStore, error) { - cfg, err := newPeerStoreConfig(storecfg) - if err != nil { - return nil, err - } - - shards := make([]*peerShard, cfg.Shards) - for i := 0; i < cfg.Shards; i++ { - shards[i] = &peerShard{} - shards[i].swarms = make(map[chihaya.InfoHash]swarm) - } - return &peerStore{ - shards: shards, - closed: make(chan struct{}), - }, nil -} - -type peerStoreConfig struct { - Shards int `yaml:"shards"` -} - -func newPeerStoreConfig(storecfg *store.DriverConfig) (*peerStoreConfig, error) { - bytes, err := yaml.Marshal(storecfg.Config) - if err != nil { - return nil, err - } - - var cfg peerStoreConfig - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - if cfg.Shards < 1 { - cfg.Shards = 1 - } - return &cfg, nil -} - -type serializedPeer string - -type peerShard struct { - swarms map[chihaya.InfoHash]swarm - sync.RWMutex -} - -type swarm struct { - // map serialized peer to mtime - seeders map[serializedPeer]int64 - leechers map[serializedPeer]int64 -} - -type peerStore struct { - shards []*peerShard - closed chan struct{} -} - -var _ store.PeerStore = &peerStore{} - -func (s *peerStore) shardIndex(infoHash chihaya.InfoHash) uint32 { - return binary.BigEndian.Uint32(infoHash[:4]) % uint32(len(s.shards)) -} - -func peerKey(p chihaya.Peer) serializedPeer { - b := make([]byte, 20+2+len(p.IP)) - copy(b[:20], p.ID[:]) - binary.BigEndian.PutUint16(b[20:22], p.Port) - copy(b[22:], p.IP) - - return serializedPeer(b) -} - -func decodePeerKey(pk serializedPeer) chihaya.Peer { - return chihaya.Peer{ - ID: chihaya.PeerIDFromString(string(pk[:20])), - Port: binary.BigEndian.Uint16([]byte(pk[20:22])), - IP: net.IP(pk[22:]), - } -} - -func (s *peerStore) PutSeeder(infoHash chihaya.InfoHash, p chihaya.Peer) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.Lock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.swarms[infoHash] = swarm{ - seeders: make(map[serializedPeer]int64), - leechers: make(map[serializedPeer]int64), - } - } - - shard.swarms[infoHash].seeders[peerKey(p)] = time.Now().UnixNano() - - shard.Unlock() - return nil -} - -func (s *peerStore) DeleteSeeder(infoHash chihaya.InfoHash, p chihaya.Peer) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - pk := peerKey(p) - shard.Lock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.Unlock() - return store.ErrResourceDoesNotExist - } - - if _, ok := shard.swarms[infoHash].seeders[pk]; !ok { - shard.Unlock() - return store.ErrResourceDoesNotExist - } - - delete(shard.swarms[infoHash].seeders, pk) - - if len(shard.swarms[infoHash].seeders)|len(shard.swarms[infoHash].leechers) == 0 { - delete(shard.swarms, infoHash) - } - - shard.Unlock() - return nil -} - -func (s *peerStore) PutLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.Lock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.swarms[infoHash] = swarm{ - seeders: make(map[serializedPeer]int64), - leechers: make(map[serializedPeer]int64), - } - } - - shard.swarms[infoHash].leechers[peerKey(p)] = time.Now().UnixNano() - - shard.Unlock() - return nil -} - -func (s *peerStore) DeleteLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - pk := peerKey(p) - shard.Lock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.Unlock() - return store.ErrResourceDoesNotExist - } - - if _, ok := shard.swarms[infoHash].leechers[pk]; !ok { - shard.Unlock() - return store.ErrResourceDoesNotExist - } - - delete(shard.swarms[infoHash].leechers, pk) - - if len(shard.swarms[infoHash].seeders)|len(shard.swarms[infoHash].leechers) == 0 { - delete(shard.swarms, infoHash) - } - - shard.Unlock() - return nil -} - -func (s *peerStore) GraduateLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - key := peerKey(p) - shard := s.shards[s.shardIndex(infoHash)] - shard.Lock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.swarms[infoHash] = swarm{ - seeders: make(map[serializedPeer]int64), - leechers: make(map[serializedPeer]int64), - } - } - - delete(shard.swarms[infoHash].leechers, key) - - shard.swarms[infoHash].seeders[key] = time.Now().UnixNano() - - shard.Unlock() - return nil -} - -func (s *peerStore) CollectGarbage(cutoff time.Time) error { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - log.Printf("memory: collecting garbage. Cutoff time: %s", cutoff.String()) - cutoffUnix := cutoff.UnixNano() - for _, shard := range s.shards { - shard.RLock() - var infohashes []chihaya.InfoHash - for key := range shard.swarms { - infohashes = append(infohashes, key) - } - shard.RUnlock() - runtime.Gosched() - - for _, infohash := range infohashes { - shard.Lock() - - for peerKey, mtime := range shard.swarms[infohash].leechers { - if mtime <= cutoffUnix { - delete(shard.swarms[infohash].leechers, peerKey) - } - } - - for peerKey, mtime := range shard.swarms[infohash].seeders { - if mtime <= cutoffUnix { - delete(shard.swarms[infohash].seeders, peerKey) - } - } - - if len(shard.swarms[infohash].seeders)|len(shard.swarms[infohash].leechers) == 0 { - delete(shard.swarms, infohash) - } - - shard.Unlock() - runtime.Gosched() - } - - runtime.Gosched() - } - - return nil -} - -func (s *peerStore) AnnouncePeers(infoHash chihaya.InfoHash, seeder bool, numWant int, peer4, peer6 chihaya.Peer) (peers, peers6 []chihaya.Peer, err error) { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.RLock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.RUnlock() - return nil, nil, store.ErrResourceDoesNotExist - } - - if seeder { - // Append leechers as possible. - leechers := shard.swarms[infoHash].leechers - for p := range leechers { - decodedPeer := decodePeerKey(p) - if numWant == 0 { - break - } - - if decodedPeer.IP.To4() == nil { - peers6 = append(peers6, decodedPeer) - } else { - peers = append(peers, decodedPeer) - } - numWant-- - } - } else { - // Append as many seeders as possible. - seeders := shard.swarms[infoHash].seeders - for p := range seeders { - decodedPeer := decodePeerKey(p) - if numWant == 0 { - break - } - - if decodedPeer.IP.To4() == nil { - peers6 = append(peers6, decodedPeer) - } else { - peers = append(peers, decodedPeer) - } - numWant-- - } - - // Append leechers until we reach numWant. - leechers := shard.swarms[infoHash].leechers - if numWant > 0 { - for p := range leechers { - decodedPeer := decodePeerKey(p) - if numWant == 0 { - break - } - - if decodedPeer.IP.To4() == nil { - if decodedPeer.Equal(peer6) { - continue - } - peers6 = append(peers6, decodedPeer) - } else { - if decodedPeer.Equal(peer4) { - continue - } - peers = append(peers, decodedPeer) - } - numWant-- - } - } - } - - shard.RUnlock() - return -} - -func (s *peerStore) GetSeeders(infoHash chihaya.InfoHash) (peers, peers6 []chihaya.Peer, err error) { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.RLock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.RUnlock() - return nil, nil, store.ErrResourceDoesNotExist - } - - seeders := shard.swarms[infoHash].seeders - for p := range seeders { - decodedPeer := decodePeerKey(p) - if decodedPeer.IP.To4() == nil { - peers6 = append(peers6, decodedPeer) - } else { - peers = append(peers, decodedPeer) - } - } - - shard.RUnlock() - return -} - -func (s *peerStore) GetLeechers(infoHash chihaya.InfoHash) (peers, peers6 []chihaya.Peer, err error) { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.RLock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.RUnlock() - return nil, nil, store.ErrResourceDoesNotExist - } - - leechers := shard.swarms[infoHash].leechers - for p := range leechers { - decodedPeer := decodePeerKey(p) - if decodedPeer.IP.To4() == nil { - peers6 = append(peers6, decodedPeer) - } else { - peers = append(peers, decodedPeer) - } - } - - shard.RUnlock() - return -} - -func (s *peerStore) NumSeeders(infoHash chihaya.InfoHash) int { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.RLock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.RUnlock() - return 0 - } - - numSeeders := len(shard.swarms[infoHash].seeders) - - shard.RUnlock() - return numSeeders -} - -func (s *peerStore) NumLeechers(infoHash chihaya.InfoHash) int { - select { - case <-s.closed: - panic("attempted to interact with stopped store") - default: - } - - shard := s.shards[s.shardIndex(infoHash)] - shard.RLock() - - if _, ok := shard.swarms[infoHash]; !ok { - shard.RUnlock() - return 0 - } - - numLeechers := len(shard.swarms[infoHash].leechers) - - shard.RUnlock() - return numLeechers -} - -func (s *peerStore) Stop() <-chan error { - toReturn := make(chan error) - go func() { - shards := make([]*peerShard, len(s.shards)) - for i := 0; i < len(s.shards); i++ { - shards[i] = &peerShard{} - shards[i].swarms = make(map[chihaya.InfoHash]swarm) - } - s.shards = shards - close(s.closed) - close(toReturn) - }() - return toReturn -} diff --git a/server/store/memory/peer_store_test.go b/server/store/memory/peer_store_test.go deleted file mode 100644 index 9a00b17..0000000 --- a/server/store/memory/peer_store_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "testing" - - "github.com/chihaya/chihaya/server/store" -) - -var ( - peerStoreTester = store.PreparePeerStoreTester(&peerStoreDriver{}) - peerStoreBenchmarker = store.PreparePeerStoreBenchmarker(&peerStoreDriver{}) - peerStoreTestConfig = &store.DriverConfig{} -) - -func init() { - unmarshalledConfig := struct { - Shards int - }{ - 1, - } - peerStoreTestConfig.Config = unmarshalledConfig -} - -func TestPeerStore(t *testing.T) { - peerStoreTester.TestPeerStore(t, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutSeeder(b *testing.B) { - peerStoreBenchmarker.PutSeeder(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutSeeder1KInfohash(b *testing.B) { - peerStoreBenchmarker.PutSeeder1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutSeeder1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutSeeder1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutSeeder1KInfohash1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutSeeder1KInfohash1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutDeleteSeeder(b *testing.B) { - peerStoreBenchmarker.PutDeleteSeeder(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutDeleteSeeder1KInfohash(b *testing.B) { - peerStoreBenchmarker.PutDeleteSeeder1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutDeleteSeeder1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutDeleteSeeder1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutDeleteSeeder1KInfohash1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutDeleteSeeder1KInfohash1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_DeleteSeederNonExist(b *testing.B) { - peerStoreBenchmarker.DeleteSeederNonExist(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash(b *testing.B) { - peerStoreBenchmarker.DeleteSeederNonExist1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_DeleteSeederNonExist1KSeeders(b *testing.B) { - peerStoreBenchmarker.DeleteSeederNonExist1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_DeleteSeederNonExist1KInfohash1KSeeders(b *testing.B) { - peerStoreBenchmarker.DeleteSeederNonExist1KInfohash1KSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutGraduateDeleteLeecher(b *testing.B) { - peerStoreBenchmarker.PutGraduateDeleteLeecher(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash(b *testing.B) { - peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutGraduateDeleteLeecher1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutGraduateDeleteLeecher1KLeechers(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_PutGraduateDeleteLeecher1KInfohash1KSeeders(b *testing.B) { - peerStoreBenchmarker.PutGraduateDeleteLeecher1KInfohash1KLeechers(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GraduateLeecherNonExist(b *testing.B) { - peerStoreBenchmarker.GraduateLeecherNonExist(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash(b *testing.B) { - peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GraduateLeecherNonExist1KSeeders(b *testing.B) { - peerStoreBenchmarker.GraduateLeecherNonExist1KLeechers(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GraduateLeecherNonExist1KInfohash1KSeeders(b *testing.B) { - peerStoreBenchmarker.GraduateLeecherNonExist1KInfohash1KLeechers(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_AnnouncePeers(b *testing.B) { - peerStoreBenchmarker.AnnouncePeers(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_AnnouncePeers1KInfohash(b *testing.B) { - peerStoreBenchmarker.AnnouncePeers1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_AnnouncePeersSeeder(b *testing.B) { - peerStoreBenchmarker.AnnouncePeersSeeder(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_AnnouncePeersSeeder1KInfohash(b *testing.B) { - peerStoreBenchmarker.AnnouncePeersSeeder1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GetSeeders(b *testing.B) { - peerStoreBenchmarker.GetSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_GetSeeders1KInfohash(b *testing.B) { - peerStoreBenchmarker.GetSeeders1KInfohash(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_NumSeeders(b *testing.B) { - peerStoreBenchmarker.NumSeeders(b, peerStoreTestConfig) -} - -func BenchmarkPeerStore_NumSeeders1KInfohash(b *testing.B) { - peerStoreBenchmarker.NumSeeders1KInfohash(b, peerStoreTestConfig) -} diff --git a/server/store/memory/string_store.go b/server/store/memory/string_store.go deleted file mode 100644 index 531e3bb..0000000 --- a/server/store/memory/string_store.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "sync" - - "github.com/chihaya/chihaya/server/store" -) - -func init() { - store.RegisterStringStoreDriver("memory", &stringStoreDriver{}) -} - -type stringStoreDriver struct{} - -func (d *stringStoreDriver) New(_ *store.DriverConfig) (store.StringStore, error) { - return &stringStore{ - strings: make(map[string]struct{}), - closed: make(chan struct{}), - }, nil -} - -type stringStore struct { - strings map[string]struct{} - closed chan struct{} - sync.RWMutex -} - -var _ store.StringStore = &stringStore{} - -func (ss *stringStore) PutString(s string) error { - ss.Lock() - defer ss.Unlock() - - select { - case <-ss.closed: - panic("attempted to interact with stopped store") - default: - } - - ss.strings[s] = struct{}{} - - return nil -} - -func (ss *stringStore) HasString(s string) (bool, error) { - ss.RLock() - defer ss.RUnlock() - - select { - case <-ss.closed: - panic("attempted to interact with stopped store") - default: - } - - _, ok := ss.strings[s] - - return ok, nil -} - -func (ss *stringStore) RemoveString(s string) error { - ss.Lock() - defer ss.Unlock() - - select { - case <-ss.closed: - panic("attempted to interact with stopped store") - default: - } - - if _, ok := ss.strings[s]; !ok { - return store.ErrResourceDoesNotExist - } - - delete(ss.strings, s) - - return nil -} - -func (ss *stringStore) Stop() <-chan error { - toReturn := make(chan error) - go func() { - ss.Lock() - defer ss.Unlock() - ss.strings = make(map[string]struct{}) - close(ss.closed) - close(toReturn) - }() - return toReturn -} diff --git a/server/store/memory/string_store_test.go b/server/store/memory/string_store_test.go deleted file mode 100644 index 3e020c2..0000000 --- a/server/store/memory/string_store_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package memory - -import ( - "testing" - - "github.com/chihaya/chihaya/server/store" -) - -var ( - stringStoreTester = store.PrepareStringStoreTester(&stringStoreDriver{}) - stringStoreBenchmarker = store.PrepareStringStoreBenchmarker(&stringStoreDriver{}) - stringStoreTestConfig = &store.DriverConfig{} -) - -func TestStringStore(t *testing.T) { - stringStoreTester.TestStringStore(t, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddShort(b *testing.B) { - stringStoreBenchmarker.AddShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddLong(b *testing.B) { - stringStoreBenchmarker.AddLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupShort(b *testing.B) { - stringStoreBenchmarker.LookupShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupLong(b *testing.B) { - stringStoreBenchmarker.LookupLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddRemoveShort(b *testing.B) { - stringStoreBenchmarker.AddRemoveShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddRemoveLong(b *testing.B) { - stringStoreBenchmarker.AddRemoveLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupNonExistShort(b *testing.B) { - stringStoreBenchmarker.LookupNonExistShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupNonExistLong(b *testing.B) { - stringStoreBenchmarker.LookupNonExistLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_RemoveNonExistShort(b *testing.B) { - stringStoreBenchmarker.RemoveNonExistShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_RemoveNonExistLong(b *testing.B) { - stringStoreBenchmarker.RemoveNonExistLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_Add1KShort(b *testing.B) { - stringStoreBenchmarker.Add1KShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_Add1KLong(b *testing.B) { - stringStoreBenchmarker.Add1KLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_Lookup1KShort(b *testing.B) { - stringStoreBenchmarker.Lookup1KShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_Lookup1KLong(b *testing.B) { - stringStoreBenchmarker.Lookup1KLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddRemove1KShort(b *testing.B) { - stringStoreBenchmarker.AddRemove1KShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_AddRemove1KLong(b *testing.B) { - stringStoreBenchmarker.AddRemove1KLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupNonExist1KShort(b *testing.B) { - stringStoreBenchmarker.LookupNonExist1KShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_LookupNonExist1KLong(b *testing.B) { - stringStoreBenchmarker.LookupNonExist1KLong(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_RemoveNonExist1KShort(b *testing.B) { - stringStoreBenchmarker.RemoveNonExist1KShort(b, stringStoreTestConfig) -} - -func BenchmarkStringStore_RemoveNonExist1KLong(b *testing.B) { - stringStoreBenchmarker.RemoveNonExist1KLong(b, stringStoreTestConfig) -} diff --git a/server/store/middleware/client/README.md b/server/store/middleware/client/README.md deleted file mode 100644 index c33ac26..0000000 --- a/server/store/middleware/client/README.md +++ /dev/null @@ -1,25 +0,0 @@ -## Client Blacklisting/Whitelisting Middlewares - -This package provides the announce middlewares `client_whitelist` and `client_blacklist` for blacklisting or whitelisting clients for announces. - -### `client_blacklist` - -The `client_blacklist` middleware uses all clientIDs stored in the `StringStore` to blacklist, i.e. block announces. - -The clientID part of the peerID of an announce is matched against the `StringStore`, if it's contained within the `StringStore`, the announce is aborted. - -### `client_whitelist` - -The `client_whitelist` middleware uses all clientIDs stored in the `StringStore` to whitelist, i.e. allow announces. - -The clientID part of the peerID of an announce is matched against the `StringStore`, if it's _not_ contained within the `StringStore`, the announce is aborted. - -### Important things to notice - -Both middlewares operate on announce requests only. - -Both middlewares use the same `StringStore`. -It is therefore not advised to have both the `client_blacklist` and the `client_whitelist` middleware running. -(If you add clientID to the `StringStore`, it will be used for blacklisting and whitelisting. -If your store contains no clientIDs, no announces will be blocked by the blacklist, but all announces will be blocked by the whitelist. -If your store contains all clientIDs, no announces will be blocked by the whitelist, but all announces will be blocked by the blacklist.) \ No newline at end of file diff --git a/server/store/middleware/client/blacklist.go b/server/store/middleware/client/blacklist.go deleted file mode 100644 index e994d2f..0000000 --- a/server/store/middleware/client/blacklist.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package client - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/clientid" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("client_blacklist", blacklistAnnounceClient) -} - -// ErrBlacklistedClient is returned by an announce middleware if the announcing -// Client is blacklisted. -var ErrBlacklistedClient = tracker.ClientError("client blacklisted") - -// blacklistAnnounceClient provides a middleware that only allows Clients to -// announce that are not stored in the StringStore. -func blacklistAnnounceClient(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - blacklisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID[:]))) - if err != nil { - return err - } else if blacklisted { - return ErrBlacklistedClient - } - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/client/whitelist.go b/server/store/middleware/client/whitelist.go deleted file mode 100644 index 275b56e..0000000 --- a/server/store/middleware/client/whitelist.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package client - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/clientid" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("client_whitelist", whitelistAnnounceClient) -} - -// PrefixClient is the prefix to be used for client peer IDs. -const PrefixClient = "c-" - -// ErrNotWhitelistedClient is returned by an announce middleware if the -// announcing Client is not whitelisted. -var ErrNotWhitelistedClient = tracker.ClientError("client not whitelisted") - -// whitelistAnnounceClient provides a middleware that only allows Clients to -// announce that are stored in the StringStore. -func whitelistAnnounceClient(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - whitelisted, err := store.MustGetStore().HasString(PrefixClient + clientid.New(string(req.PeerID[:]))) - if err != nil { - return err - } else if !whitelisted { - return ErrNotWhitelistedClient - } - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/infohash/README.md b/server/store/middleware/infohash/README.md deleted file mode 100644 index 213bcec..0000000 --- a/server/store/middleware/infohash/README.md +++ /dev/null @@ -1,69 +0,0 @@ -## Infohash Blacklisting/Whitelisting Middlewares - -This package provides the middleware `infohash_blacklist` and `infohash_whitelist` for blacklisting or whitelisting infohashes. -It also provides the configurable scrape middleware `infohash_blacklist` and `infohash_whitelist` for blacklisting or whitelisting infohashes. - -### `infohash_blacklist` - -#### For Announces - -The `infohash_blacklist` middleware uses all infohashes stored in the `StringStore` with the `PrefixInfohash` prefix to blacklist, i.e. block announces. - -#### For Scrapes - -The configurable `infohash_blacklist` middleware uses all infohashes stored in the `StringStore` with the `PrefixInfohash` prefix to blacklist scrape requests. - -The scrape middleware has two modes of operation: _Block_ and _Filter_. - -- _Block_ will drop a scrape request if it contains a blacklisted infohash. -- _Filter_ will filter all blacklisted infohashes from a scrape request, potentially leaving behind an empty scrape request. - **IMPORTANT**: This mode **does not work with UDP servers**. - -See the configuration section for information about how to configure the scrape middleware. - -### `infohash_whitelist` - -#### For Announces - -The `infohash_blacklist` middleware uses all infohashes stored in the `StringStore` with the `PrefixInfohash` prefix to whitelist, i.e. allow announces. - -#### For Scrapes - -The configurable `infohash_blacklist` middleware uses all infohashes stored in the `StringStore` with the `PrefixInfohash` prefix to whitelist scrape requests. - -The scrape middleware has two modes of operation: _Block_ and _Filter_. - -- _Block_ will drop a scrape request if it contains a non-whitelisted infohash. -- _Filter_ will filter all non-whitelisted infohashes from a scrape request, potentially leaving behind an empty scrape request. - **IMPORTANT**: This mode **does not work with UDP servers**. - -See the configuration section for information about how to configure the scrape middleware. - -### Important things to notice - -Both blacklist and whitelist middleware use the same `StringStore`. -It is therefore not advised to have both the `infohash_blacklist` and the `infohash_whitelist` announce or scrape middleware running. -(If you add an infohash to the `StringStore`, it will be used for blacklisting and whitelisting. -If your store contains no infohashes, no announces/scrapes will be blocked by the blacklist, but all will be blocked by the whitelist. -If your store contains all addresses, no announces/scrapes will be blocked by the whitelist, but all will be blocked by the blacklist.) - -Also note that the announce and scrape middleware both use the same `StringStore`. -It is therefore not possible to use different infohashes for black-/whitelisting on announces and scrape requests. - -### Configuration - -The scrape middleware is configurable. - -The configuration uses a single required parameter `mode` to determine the mode of operation for the middleware. -An example configuration might look like this: - - chihaya: - tracker: - scrape_middleware: - - name: infohash_blacklist - config: - mode: block - -`mode` accepts two values: `block` and `filter`. - -**IMPORTANT**: The `filter` mode **does not work with UDP servers**. \ No newline at end of file diff --git a/server/store/middleware/infohash/blacklist.go b/server/store/middleware/infohash/blacklist.go deleted file mode 100644 index ff883b1..0000000 --- a/server/store/middleware/infohash/blacklist.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("infohash_blacklist", blacklistAnnounceInfohash) - tracker.RegisterScrapeMiddlewareConstructor("infohash_blacklist", blacklistScrapeInfohash) - mustGetStore = func() store.StringStore { - return store.MustGetStore().StringStore - } -} - -// ErrBlockedInfohash is returned by a middleware if any of the infohashes -// contained in an announce or scrape are disallowed. -var ErrBlockedInfohash = tracker.ClientError("disallowed infohash") - -var mustGetStore func() store.StringStore - -// blacklistAnnounceInfohash provides a middleware that only allows announces -// for infohashes that are not stored in a StringStore. -func blacklistAnnounceInfohash(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - blacklisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash[:])) - if err != nil { - return err - } else if blacklisted { - return ErrBlockedInfohash - } - - return next(cfg, req, resp) - } -} - -// blacklistScrapeInfohash provides a middleware constructor for a middleware -// that blocks or filters scrape requests based on the infohashes scraped. -// -// The middleware works in two modes: block and filter. -// The block mode blocks a scrape completely if any of the infohashes is -// disallowed. -// The filter mode filters any disallowed infohashes from the scrape, -// potentially leaving an empty scrape. -// -// ErrUnknownMode is returned if the Mode specified in the config is unknown. -func blacklistScrapeInfohash(c chihaya.MiddlewareConfig) (tracker.ScrapeMiddleware, error) { - cfg, err := newConfig(c) - if err != nil { - return nil, err - } - - switch cfg.Mode { - case ModeFilter: - return blacklistFilterScrape, nil - case ModeBlock: - return blacklistBlockScrape, nil - default: - panic("unknown mode") - } -} - -func blacklistFilterScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) (err error) { - blacklisted := false - storage := mustGetStore() - infohashes := req.InfoHashes - - for i, ih := range infohashes { - blacklisted, err = storage.HasString(PrefixInfohash + string(ih[:])) - - if err != nil { - return err - } else if blacklisted { - req.InfoHashes[i] = req.InfoHashes[len(req.InfoHashes)-1] - req.InfoHashes = req.InfoHashes[:len(req.InfoHashes)-1] - } - } - - return next(cfg, req, resp) - } -} - -func blacklistBlockScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) (err error) { - blacklisted := false - storage := mustGetStore() - - for _, ih := range req.InfoHashes { - blacklisted, err = storage.HasString(PrefixInfohash + string(ih[:])) - - if err != nil { - return err - } else if blacklisted { - return ErrBlockedInfohash - } - } - - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/infohash/blacklist_test.go b/server/store/middleware/infohash/blacklist_test.go deleted file mode 100644 index 3d06b51..0000000 --- a/server/store/middleware/infohash/blacklist_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/stopper" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -type storeMock struct { - strings map[string]struct{} -} - -func (ss *storeMock) PutString(s string) error { - ss.strings[s] = struct{}{} - - return nil -} - -func (ss *storeMock) HasString(s string) (bool, error) { - _, ok := ss.strings[s] - - return ok, nil -} - -func (ss *storeMock) RemoveString(s string) error { - delete(ss.strings, s) - - return nil -} - -func (ss *storeMock) Stop() <-chan error { - return stopper.AlreadyStopped -} - -var mock store.StringStore = &storeMock{ - strings: make(map[string]struct{}), -} - -var ( - ih1 = chihaya.InfoHash([20]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) - ih2 = chihaya.InfoHash([20]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) -) - -func TestASetUp(t *testing.T) { - mustGetStore = func() store.StringStore { - return mock - } - - mustGetStore().PutString(PrefixInfohash + string(ih1[:])) -} - -func TestBlacklistAnnounceMiddleware(t *testing.T) { - var ( - achain tracker.AnnounceChain - req chihaya.AnnounceRequest - resp chihaya.AnnounceResponse - ) - - achain.Append(blacklistAnnounceInfohash) - handler := achain.Handler() - - err := handler(nil, &req, &resp) - assert.Nil(t, err) - - req.InfoHash = chihaya.InfoHash(ih1) - err = handler(nil, &req, &resp) - assert.Equal(t, ErrBlockedInfohash, err) - - req.InfoHash = chihaya.InfoHash(ih2) - err = handler(nil, &req, &resp) - assert.Nil(t, err) -} - -func TestBlacklistScrapeMiddlewareBlock(t *testing.T) { - var ( - schain tracker.ScrapeChain - req chihaya.ScrapeRequest - resp chihaya.ScrapeResponse - ) - - mw, err := blacklistScrapeInfohash(chihaya.MiddlewareConfig{ - Name: "blacklist_infohash", - Config: Config{ - Mode: ModeBlock, - }, - }) - assert.Nil(t, err) - schain.Append(mw) - handler := schain.Handler() - - err = handler(nil, &req, &resp) - assert.Nil(t, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Equal(t, ErrBlockedInfohash, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) -} - -func TestBlacklistScrapeMiddlewareFilter(t *testing.T) { - var ( - schain tracker.ScrapeChain - req chihaya.ScrapeRequest - resp chihaya.ScrapeResponse - ) - - mw, err := blacklistScrapeInfohash(chihaya.MiddlewareConfig{ - Name: "blacklist_infohash", - Config: Config{ - Mode: ModeFilter, - }, - }) - assert.Nil(t, err) - schain.Append(mw) - handler := schain.Handler() - - err = handler(nil, &req, &resp) - assert.Nil(t, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih2)}, req.InfoHashes) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) -} diff --git a/server/store/middleware/infohash/config.go b/server/store/middleware/infohash/config.go deleted file mode 100644 index 7c399a0..0000000 --- a/server/store/middleware/infohash/config.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "errors" - - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" -) - -// ErrUnknownMode is returned by a MiddlewareConstructor if the Mode specified -// in the configuration is unknown. -var ErrUnknownMode = errors.New("unknown mode") - -// Mode represents the mode of operation for an infohash scrape middleware. -type Mode string - -const ( - // ModeFilter makes the middleware filter disallowed infohashes from a - // scrape request. - ModeFilter = Mode("filter") - - // ModeBlock makes the middleware block a scrape request if it contains - // at least one disallowed infohash. - ModeBlock = Mode("block") -) - -// Config represents the configuration for an infohash scrape middleware. -type Config struct { - Mode Mode `yaml:"mode"` -} - -// newConfig parses the given MiddlewareConfig as an infohash.Config. -// ErrUnknownMode is returned if the mode is unknown. -func newConfig(mwcfg chihaya.MiddlewareConfig) (*Config, error) { - bytes, err := yaml.Marshal(mwcfg.Config) - if err != nil { - return nil, err - } - - var cfg Config - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - if cfg.Mode != ModeBlock && cfg.Mode != ModeFilter { - return nil, ErrUnknownMode - } - - return &cfg, nil -} diff --git a/server/store/middleware/infohash/config_test.go b/server/store/middleware/infohash/config_test.go deleted file mode 100644 index f7cc57a..0000000 --- a/server/store/middleware/infohash/config_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "fmt" - "testing" - - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" - "github.com/stretchr/testify/assert" -) - -var ( - configTemplate = `name: foo -config: - %s: %s` - - data = []testData{ - {"mode", "block", false, ModeBlock}, - {"mode", "filter", false, ModeFilter}, - {"some", "stuff", true, ModeBlock}, - } -) - -type testData struct { - key string - value string - err bool - expected Mode -} - -func TestNewConfig(t *testing.T) { - var mwconfig chihaya.MiddlewareConfig - - cfg, err := newConfig(mwconfig) - assert.NotNil(t, err) - assert.Nil(t, cfg) - - for _, test := range data { - config := fmt.Sprintf(configTemplate, test.key, test.value) - err = yaml.Unmarshal([]byte(config), &mwconfig) - assert.Nil(t, err) - - cfg, err = newConfig(mwconfig) - if test.err { - assert.NotNil(t, err) - continue - } - assert.Nil(t, err) - assert.Equal(t, test.expected, cfg.Mode) - } -} diff --git a/server/store/middleware/infohash/whitelist.go b/server/store/middleware/infohash/whitelist.go deleted file mode 100644 index 99caeda..0000000 --- a/server/store/middleware/infohash/whitelist.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("infohash_whitelist", whitelistAnnounceInfohash) - tracker.RegisterScrapeMiddlewareConstructor("infohash_whitelist", whitelistScrapeInfohash) -} - -// PrefixInfohash is the prefix to be used for infohashes. -const PrefixInfohash = "ih-" - -// whitelistAnnounceInfohash provides a middleware that only allows announces -// for infohashes that are not stored in a StringStore -func whitelistAnnounceInfohash(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - whitelisted, err := mustGetStore().HasString(PrefixInfohash + string(req.InfoHash[:])) - - if err != nil { - return err - } else if !whitelisted { - return ErrBlockedInfohash - } - return next(cfg, req, resp) - } -} - -// whitelistScrapeInfohash provides a middleware constructor for a middleware -// that blocks or filters scrape requests based on the infohashes scraped. -// -// The middleware works in two modes: block and filter. -// The block mode blocks a scrape completely if any of the infohashes is -// disallowed. -// The filter mode filters any disallowed infohashes from the scrape, -// potentially leaving an empty scrape. -// -// ErrUnknownMode is returned if the Mode specified in the config is unknown. -func whitelistScrapeInfohash(c chihaya.MiddlewareConfig) (tracker.ScrapeMiddleware, error) { - cfg, err := newConfig(c) - if err != nil { - return nil, err - } - - switch cfg.Mode { - case ModeFilter: - return whitelistFilterScrape, nil - case ModeBlock: - return whitelistBlockScrape, nil - default: - panic("unknown mode") - } -} - -func whitelistFilterScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) (err error) { - whitelisted := false - storage := mustGetStore() - infohashes := req.InfoHashes - - for i, ih := range infohashes { - whitelisted, err = storage.HasString(PrefixInfohash + string(ih[:])) - - if err != nil { - return err - } else if !whitelisted { - req.InfoHashes[i] = req.InfoHashes[len(req.InfoHashes)-1] - req.InfoHashes = req.InfoHashes[:len(req.InfoHashes)-1] - } - } - - return next(cfg, req, resp) - } -} - -func whitelistBlockScrape(next tracker.ScrapeHandler) tracker.ScrapeHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) (err error) { - whitelisted := false - storage := mustGetStore() - - for _, ih := range req.InfoHashes { - whitelisted, err = storage.HasString(PrefixInfohash + string(ih[:])) - - if err != nil { - return err - } else if !whitelisted { - return ErrBlockedInfohash - } - } - - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/infohash/whitelist_test.go b/server/store/middleware/infohash/whitelist_test.go deleted file mode 100644 index 3a68386..0000000 --- a/server/store/middleware/infohash/whitelist_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package infohash - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/tracker" -) - -func TestWhitelistAnnounceMiddleware(t *testing.T) { - var ( - achain tracker.AnnounceChain - req chihaya.AnnounceRequest - resp chihaya.AnnounceResponse - ) - - achain.Append(whitelistAnnounceInfohash) - handler := achain.Handler() - - err := handler(nil, &req, &resp) - assert.Equal(t, ErrBlockedInfohash, err) - - req.InfoHash = chihaya.InfoHash(ih2) - err = handler(nil, &req, &resp) - assert.Equal(t, ErrBlockedInfohash, err) - - req.InfoHash = chihaya.InfoHash(ih1) - err = handler(nil, &req, &resp) - assert.Nil(t, err) -} - -func TestWhitelistScrapeMiddlewareBlock(t *testing.T) { - var ( - schain tracker.ScrapeChain - req chihaya.ScrapeRequest - resp chihaya.ScrapeResponse - ) - - mw, err := whitelistScrapeInfohash(chihaya.MiddlewareConfig{ - Name: "whitelist_infohash", - Config: Config{ - Mode: ModeBlock, - }, - }) - assert.Nil(t, err) - schain.Append(mw) - handler := schain.Handler() - - err = handler(nil, &req, &resp) - assert.Nil(t, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Equal(t, ErrBlockedInfohash, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) -} - -func TestWhitelistScrapeMiddlewareFilter(t *testing.T) { - var ( - schain tracker.ScrapeChain - req chihaya.ScrapeRequest - resp chihaya.ScrapeResponse - ) - - mw, err := whitelistScrapeInfohash(chihaya.MiddlewareConfig{ - Name: "whitelist_infohash", - Config: Config{ - Mode: ModeFilter, - }, - }) - assert.Nil(t, err) - schain.Append(mw) - handler := schain.Handler() - - err = handler(nil, &req, &resp) - assert.Nil(t, err) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1), chihaya.InfoHash(ih2)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih1)}, req.InfoHashes) - - req.InfoHashes = []chihaya.InfoHash{chihaya.InfoHash(ih1)} - err = handler(nil, &req, &resp) - assert.Nil(t, err) - assert.Equal(t, []chihaya.InfoHash{chihaya.InfoHash(ih1)}, req.InfoHashes) -} diff --git a/server/store/middleware/ip/README.md b/server/store/middleware/ip/README.md deleted file mode 100644 index 16b8cc9..0000000 --- a/server/store/middleware/ip/README.md +++ /dev/null @@ -1,32 +0,0 @@ -## IP Blacklisting/Whitelisting Middlewares - -This package provides the announce middlewares `ip_blacklist` and `ip_whitelist` for blacklisting or whitelisting IP addresses and networks for announces. - -### `ip_blacklist` - -The `ip_blacklist` middleware uses all IP addresses and networks stored in the `IPStore` to blacklist, i.e. block announces. - -Both the IPv4 and the IPv6 addresses contained in the announce are matched against the `IPStore`. -If one or both of the two are contained in the `IPStore`, the announce will be rejected _completely_. - -### `ip_whitelist` - -The `ip_whitelist` middleware uses all IP addresses and networks stored in the `IPStore` to whitelist, i.e. allow announces. - -If present, both the IPv4 and the IPv6 addresses contained in the announce are matched against the `IPStore`. -Only if all IP address that are present in the announce are also present in the `IPStore` will the announce be allowed, otherwise it will be rejected _completely_. - -### Important things to notice - -Both middlewares operate on announce requests only. -The middlewares will check the IPv4 and IPv6 IPs a client announces to the tracker against an `IPStore`. -Normally the IP address embedded in the announce is the public IP address of the machine the client is running on. -Note however, that a client can override this behaviour by specifying an IP address in the announce itself. -_This middleware does not (dis)allow announces coming from certain IP addresses, but announces containing certain IP addresses_. -Always keep that in mind. - -Both middlewares use the same `IPStore`. -It is therefore not advised to have both the `ip_blacklist` and the `ip_whitelist` middleware running. -(If you add an IP address or network to the `IPStore`, it will be used for blacklisting and whitelisting. -If your store contains no addresses, no announces will be blocked by the blacklist, but all announces will be blocked by the whitelist. -If your store contains all addresses, no announces will be blocked by the whitelist, but all announces will be blocked by the blacklist.) \ No newline at end of file diff --git a/server/store/middleware/ip/blacklist.go b/server/store/middleware/ip/blacklist.go deleted file mode 100644 index deee714..0000000 --- a/server/store/middleware/ip/blacklist.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package ip - -import ( - "net" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("ip_blacklist", blacklistAnnounceIP) -} - -// ErrBlockedIP is returned by an announce middleware if any of the announcing -// IPs is disallowed. -var ErrBlockedIP = tracker.ClientError("disallowed IP address") - -// blacklistAnnounceIP provides a middleware that only allows IPs to announce -// that are not stored in an IPStore. -func blacklistAnnounceIP(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - blacklisted := false - storage := store.MustGetStore() - - // We have to check explicitly if they are present, because someone - // could have added a net.IP to the store. - if req.IPv6 != nil && req.IPv4 != nil { - blacklisted, err = storage.HasAnyIP([]net.IP{req.IPv4, req.IPv6}) - } else if req.IPv4 != nil { - blacklisted, err = storage.HasIP(req.IPv4) - } else { - blacklisted, err = storage.HasIP(req.IPv6) - } - - if err != nil { - return err - } else if blacklisted { - return ErrBlockedIP - } - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/ip/whitelist.go b/server/store/middleware/ip/whitelist.go deleted file mode 100644 index 71cd7d9..0000000 --- a/server/store/middleware/ip/whitelist.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package ip - -import ( - "net" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("ip_whitelist", whitelistAnnounceIP) -} - -// whitelistAnnounceIP provides a middleware that only allows IPs to announce -// that are stored in an IPStore. -func whitelistAnnounceIP(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - whitelisted := false - storage := store.MustGetStore() - - // We have to check explicitly if they are present, because someone - // could have added a net.IP to the store. - if req.IPv4 != nil && req.IPv6 != nil { - whitelisted, err = storage.HasAllIPs([]net.IP{req.IPv4, req.IPv6}) - } else if req.IPv4 != nil { - whitelisted, err = storage.HasIP(req.IPv4) - } else { - whitelisted, err = storage.HasIP(req.IPv6) - } - - if err != nil { - return err - } else if !whitelisted { - return ErrBlockedIP - } - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/response/README.md b/server/store/middleware/response/README.md deleted file mode 100644 index f1becc2..0000000 --- a/server/store/middleware/response/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## Response Middleware - -This package provides the final response for a chain of middleware using the “store” package. - -### `store_response` - -The `store_response` middleware uses the peer data stored in the peerStore to create a response for the request. - -### Important things to notice - -This middleware is very basic, and may not do everything that you require. \ No newline at end of file diff --git a/server/store/middleware/response/response.go b/server/store/middleware/response/response.go deleted file mode 100644 index aaab25c..0000000 --- a/server/store/middleware/response/response.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package response - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("store_response", responseAnnounceClient) - tracker.RegisterScrapeMiddleware("store_response", responseScrapeClient) -} - -// FailedToRetrievePeers represents an error that has been return when -// attempting to fetch peers from the store. -type FailedToRetrievePeers string - -// Error interface for FailedToRetrievePeers. -func (f FailedToRetrievePeers) Error() string { return string(f) } - -// responseAnnounceClient provides a middleware to make a response to an -// announce based on the current request. -func responseAnnounceClient(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - storage := store.MustGetStore() - - resp.Interval = cfg.AnnounceInterval - resp.MinInterval = cfg.MinAnnounceInterval - resp.Compact = req.Compact - resp.Complete = int32(storage.NumSeeders(req.InfoHash)) - resp.Incomplete = int32(storage.NumLeechers(req.InfoHash)) - resp.IPv4Peers, resp.IPv6Peers, err = storage.AnnouncePeers(req.InfoHash, req.Left == 0, int(req.NumWant), req.Peer4(), req.Peer6()) - if err != nil { - return FailedToRetrievePeers(err.Error()) - } - - return next(cfg, req, resp) - } -} - -// responseScrapeClient provides a middleware to make a response to an -// scrape based on the current request. -func responseScrapeClient(next tracker.ScrapeHandler) tracker.ScrapeHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) (err error) { - storage := store.MustGetStore() - for _, infoHash := range req.InfoHashes { - resp.Files[infoHash] = chihaya.Scrape{ - Complete: int32(storage.NumSeeders(infoHash)), - Incomplete: int32(storage.NumLeechers(infoHash)), - } - } - - return next(cfg, req, resp) - } -} diff --git a/server/store/middleware/swarm/README.md b/server/store/middleware/swarm/README.md deleted file mode 100644 index 60444e7..0000000 --- a/server/store/middleware/swarm/README.md +++ /dev/null @@ -1,12 +0,0 @@ -## Swarm Interaction Middleware - -This package provides the announce middleware that modifies peer data stored in the `store` package. - -### `store_swarm_interaction` - -The `store_swarm_interaction` middleware updates the data stored in the `peerStore` based on the announce. - -### Important things to notice - -It is recommended to have this middleware run before the `store_response` middleware. -The `store_response` middleware assumes the store to be already updated by the announce. \ No newline at end of file diff --git a/server/store/middleware/swarm/swarm.go b/server/store/middleware/swarm/swarm.go deleted file mode 100644 index 87ff15b..0000000 --- a/server/store/middleware/swarm/swarm.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package response - -import ( - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/event" - "github.com/chihaya/chihaya/server/store" - "github.com/chihaya/chihaya/tracker" -) - -func init() { - tracker.RegisterAnnounceMiddleware("store_swarm_interaction", announceSwarmInteraction) -} - -// FailedSwarmInteraction represents an error that indicates that the -// interaction of a peer with a swarm failed. -type FailedSwarmInteraction string - -// Error satisfies the error interface for FailedSwarmInteraction. -func (f FailedSwarmInteraction) Error() string { return string(f) } - -// announceSwarmInteraction provides a middleware that manages swarm -// interactions for a peer based on the announce. -func announceSwarmInteraction(next tracker.AnnounceHandler) tracker.AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) (err error) { - if req.IPv4 != nil { - err = updatePeerStore(req, req.Peer4()) - if err != nil { - return FailedSwarmInteraction(err.Error()) - } - } - - if req.IPv6 != nil { - err = updatePeerStore(req, req.Peer6()) - if err != nil { - return FailedSwarmInteraction(err.Error()) - } - } - - return next(cfg, req, resp) - } -} - -func updatePeerStore(req *chihaya.AnnounceRequest, peer chihaya.Peer) (err error) { - storage := store.MustGetStore() - - switch { - case req.Event == event.Stopped: - err = storage.DeleteSeeder(req.InfoHash, peer) - if err != nil && err != store.ErrResourceDoesNotExist { - return err - } - - err = storage.DeleteLeecher(req.InfoHash, peer) - if err != nil && err != store.ErrResourceDoesNotExist { - return err - } - - case req.Event == event.Completed || req.Left == 0: - err = storage.GraduateLeecher(req.InfoHash, peer) - if err != nil { - return err - } - default: - err = storage.PutLeecher(req.InfoHash, peer) - if err != nil { - return err - } - } - - return nil -} diff --git a/server/store/peer_store.go b/server/store/peer_store.go deleted file mode 100644 index fff01c3..0000000 --- a/server/store/peer_store.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "fmt" - "time" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/stopper" -) - -var peerStoreDrivers = make(map[string]PeerStoreDriver) - -// PeerStore represents an interface for manipulating peers. -type PeerStore interface { - // PutSeeder adds a seeder for the infoHash to the PeerStore. - PutSeeder(infoHash chihaya.InfoHash, p chihaya.Peer) error - // DeleteSeeder removes a seeder for the infoHash from the PeerStore. - // - // Returns ErrResourceDoesNotExist if the infoHash or peer does not - // exist. - DeleteSeeder(infoHash chihaya.InfoHash, p chihaya.Peer) error - - // PutLeecher adds a leecher for the infoHash to the PeerStore. - PutLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error - // DeleteLeecher removes a leecher for the infoHash from the PeerStore. - // - // Returns ErrResourceDoesNotExist if the infoHash or peer does not - // exist. - DeleteLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error - - // GraduateLeecher promotes a peer from a leecher to a seeder for the - // infoHash within the PeerStore. - // - // If the given Peer is not a leecher, it will still be added to the - // list of seeders and no error will be returned. - GraduateLeecher(infoHash chihaya.InfoHash, p chihaya.Peer) error - - // AnnouncePeers returns a list of both IPv4, and IPv6 peers for an - // announce. - // - // If seeder is true then the peers returned will only be leechers, the - // ammount of leechers returned will be the smaller value of numWant or - // the available leechers. - // If it is false then seeders will be returned up until numWant or the - // available seeders, whichever is smaller. If the available seeders is - // less than numWant then peers are returned until numWant or they run out. - AnnouncePeers(infoHash chihaya.InfoHash, seeder bool, numWant int, peer4, peer6 chihaya.Peer) (peers, peers6 []chihaya.Peer, err error) - // CollectGarbage deletes peers from the peerStore which are older than the - // cutoff time. - CollectGarbage(cutoff time.Time) error - - // GetSeeders gets all the seeders for a particular infoHash. - GetSeeders(infoHash chihaya.InfoHash) (peers, peers6 []chihaya.Peer, err error) - // GetLeechers gets all the leechers for a particular infoHash. - GetLeechers(infoHash chihaya.InfoHash) (peers, peers6 []chihaya.Peer, err error) - - // NumSeeders gets the amount of seeders for a particular infoHash. - NumSeeders(infoHash chihaya.InfoHash) int - // NumLeechers gets the amount of leechers for a particular infoHash. - NumLeechers(infoHash chihaya.InfoHash) int - - // Stopper provides the Stop method that stops the PeerStore. - // Stop should shut down the PeerStore in a separate goroutine and send - // an error to the channel if the shutdown failed. If the shutdown - // was successful, the channel is to be closed. - stopper.Stopper -} - -// PeerStoreDriver represents an interface for creating a handle to the storage -// of peers. -type PeerStoreDriver interface { - New(*DriverConfig) (PeerStore, error) -} - -// RegisterPeerStoreDriver makes a driver available by the provided name. -// -// If this function is called twice with the same name or if the driver is nil, -// it panics. -func RegisterPeerStoreDriver(name string, driver PeerStoreDriver) { - if driver == nil { - panic("storage: could not register nil PeerStoreDriver") - } - - if _, dup := peerStoreDrivers[name]; dup { - panic("storage: could not register duplicate PeerStoreDriver: " + name) - } - - peerStoreDrivers[name] = driver -} - -// OpenPeerStore returns a PeerStore specified by a configuration. -func OpenPeerStore(cfg *DriverConfig) (PeerStore, error) { - driver, ok := peerStoreDrivers[cfg.Name] - if !ok { - return nil, fmt.Errorf("storage: unknown PeerStoreDriver %q (forgotten import?)", cfg) - } - - return driver.New(cfg) -} diff --git a/server/store/store.go b/server/store/store.go deleted file mode 100644 index e47ec94..0000000 --- a/server/store/store.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "errors" - "log" - "time" - - "gopkg.in/yaml.v2" - - "github.com/chihaya/chihaya" - "github.com/chihaya/chihaya/pkg/stopper" - "github.com/chihaya/chihaya/server" - "github.com/chihaya/chihaya/tracker" -) - -var theStore *Store - -func init() { - server.Register("store", constructor) -} - -// ErrResourceDoesNotExist is the error returned by all delete methods in the -// store if the requested resource does not exist. -var ErrResourceDoesNotExist = errors.New("resource does not exist") - -func constructor(srvcfg *chihaya.ServerConfig, tkr *tracker.Tracker) (server.Server, error) { - if theStore == nil { - cfg, err := newConfig(srvcfg) - if err != nil { - return nil, errors.New("store: invalid store config: " + err.Error()) - } - - theStore = &Store{ - cfg: cfg, - tkr: tkr, - shutdown: make(chan struct{}), - sg: stopper.NewStopGroup(), - } - - ps, err := OpenPeerStore(&cfg.PeerStore) - if err != nil { - return nil, err - } - theStore.sg.Add(ps) - - ips, err := OpenIPStore(&cfg.IPStore) - if err != nil { - return nil, err - } - theStore.sg.Add(ips) - - ss, err := OpenStringStore(&cfg.StringStore) - if err != nil { - return nil, err - } - theStore.sg.Add(ss) - - theStore.PeerStore = ps - theStore.IPStore = ips - theStore.StringStore = ss - } - return theStore, nil -} - -// Config represents the configuration for the store. -type Config struct { - Addr string `yaml:"addr"` - RequestTimeout time.Duration `yaml:"request_timeout"` - ReadTimeout time.Duration `yaml:"read_timeout"` - WriteTimeout time.Duration `yaml:"write_timeout"` - GCAfter time.Duration `yaml:"gc_after"` - PeerStore DriverConfig `yaml:"peer_store"` - IPStore DriverConfig `yaml:"ip_store"` - StringStore DriverConfig `yaml:"string_store"` -} - -// DriverConfig represents the configuration for a store driver. -type DriverConfig struct { - Name string `yaml:"name"` - Config interface{} `yaml:"config"` -} - -func newConfig(srvcfg *chihaya.ServerConfig) (*Config, error) { - bytes, err := yaml.Marshal(srvcfg.Config) - if err != nil { - return nil, err - } - - var cfg Config - err = yaml.Unmarshal(bytes, &cfg) - if err != nil { - return nil, err - } - - return &cfg, nil -} - -// MustGetStore is used by middleware to access the store. -// -// This function calls log.Fatal if a server hasn't been already created by -// the server package. -func MustGetStore() *Store { - if theStore == nil { - log.Fatal("store middleware used without store server") - } - return theStore -} - -// Store provides storage for a tracker. -type Store struct { - cfg *Config - tkr *tracker.Tracker - shutdown chan struct{} - sg *stopper.StopGroup - - PeerStore - IPStore - StringStore -} - -// Start starts the store drivers and blocks until all of them exit. -func (s *Store) Start() { - <-s.shutdown -} - -// Stop stops the store drivers and waits for them to exit. -func (s *Store) Stop() { - errors := s.sg.Stop() - if len(errors) == 0 { - log.Println("Store server shut down cleanly") - } else { - log.Println("Store server: failed to shutdown drivers") - for _, err := range errors { - log.Println(err.Error()) - } - } - close(s.shutdown) -} diff --git a/server/store/store_bench.go b/server/store/store_bench.go deleted file mode 100644 index b1cbfc5..0000000 --- a/server/store/store_bench.go +++ /dev/null @@ -1,1262 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "fmt" - "net" - "strings" - "testing" - - "github.com/chihaya/chihaya" - "github.com/stretchr/testify/require" -) - -const num1KElements = 1000 - -// StringStoreBenchmarker is a collection of benchmarks for StringStore drivers. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type StringStoreBenchmarker interface { - AddShort(*testing.B, *DriverConfig) - AddLong(*testing.B, *DriverConfig) - LookupShort(*testing.B, *DriverConfig) - LookupLong(*testing.B, *DriverConfig) - AddRemoveShort(*testing.B, *DriverConfig) - AddRemoveLong(*testing.B, *DriverConfig) - LookupNonExistShort(*testing.B, *DriverConfig) - LookupNonExistLong(*testing.B, *DriverConfig) - RemoveNonExistShort(*testing.B, *DriverConfig) - RemoveNonExistLong(*testing.B, *DriverConfig) - - Add1KShort(*testing.B, *DriverConfig) - Add1KLong(*testing.B, *DriverConfig) - Lookup1KShort(*testing.B, *DriverConfig) - Lookup1KLong(*testing.B, *DriverConfig) - AddRemove1KShort(*testing.B, *DriverConfig) - AddRemove1KLong(*testing.B, *DriverConfig) - LookupNonExist1KShort(*testing.B, *DriverConfig) - LookupNonExist1KLong(*testing.B, *DriverConfig) - RemoveNonExist1KShort(*testing.B, *DriverConfig) - RemoveNonExist1KLong(*testing.B, *DriverConfig) -} - -var _ StringStoreBenchmarker = &stringStoreBench{} - -type stringStoreBench struct { - // sShort holds differentStrings unique strings of length 10. - sShort [num1KElements]string - // sLong holds differentStrings unique strings of length 1000. - sLong [num1KElements]string - - driver StringStoreDriver -} - -func generateLongStrings() (a [num1KElements]string) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = strings.Repeat(fmt.Sprintf("%x", b), 250) - } - - return -} - -func generateShortStrings() (a [num1KElements]string) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = strings.Repeat(fmt.Sprintf("%x", b), 3)[:10] - } - - return -} - -// PrepareStringStoreBenchmarker prepares a reusable suite for StringStore driver -// benchmarks. -func PrepareStringStoreBenchmarker(driver StringStoreDriver) StringStoreBenchmarker { - return stringStoreBench{ - sShort: generateShortStrings(), - sLong: generateLongStrings(), - driver: driver, - } -} - -type stringStoreSetupFunc func(StringStore) error - -func stringStoreSetupNOP(StringStore) error { return nil } - -type stringStoreBenchFunc func(StringStore, int) error - -func (sb stringStoreBench) runBenchmark(b *testing.B, cfg *DriverConfig, setup stringStoreSetupFunc, execute stringStoreBenchFunc) { - ss, err := sb.driver.New(cfg) - require.Nil(b, err, "Constructor error must be nil") - require.NotNil(b, ss, "String store must not be nil") - - err = setup(ss) - require.Nil(b, err, "Benchmark setup must not fail") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - execute(ss, i) - } - b.StopTimer() - - errChan := ss.Stop() - err = <-errChan - require.Nil(b, err, "StringStore shutdown must not fail") -} - -func (sb stringStoreBench) AddShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sShort[0]) - return nil - }) -} - -func (sb stringStoreBench) AddLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sLong[0]) - return nil - }) -} - -func (sb stringStoreBench) Add1KShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sShort[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) Add1KLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sLong[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) LookupShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, - func(ss StringStore) error { - return ss.PutString(sb.sShort[0]) - }, - func(ss StringStore, i int) error { - ss.HasString(sb.sShort[0]) - return nil - }) -} - -func (sb stringStoreBench) LookupLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, - func(ss StringStore) error { - return ss.PutString(sb.sLong[0]) - }, - func(ss StringStore, i int) error { - ss.HasString(sb.sLong[0]) - return nil - }) -} - -func (sb stringStoreBench) Lookup1KShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, - func(ss StringStore) error { - for i := 0; i < num1KElements; i++ { - err := ss.PutString(sb.sShort[i]) - if err != nil { - return err - } - } - return nil - }, - func(ss StringStore, i int) error { - ss.HasString(sb.sShort[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) Lookup1KLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, - func(ss StringStore) error { - for i := 0; i < num1KElements; i++ { - err := ss.PutString(sb.sLong[i]) - if err != nil { - return err - } - } - return nil - }, - func(ss StringStore, i int) error { - ss.HasString(sb.sLong[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) AddRemoveShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sShort[0]) - ss.RemoveString(sb.sShort[0]) - return nil - }) -} - -func (sb stringStoreBench) AddRemoveLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sLong[0]) - ss.RemoveString(sb.sLong[0]) - return nil - }) -} - -func (sb stringStoreBench) AddRemove1KShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sShort[i%num1KElements]) - ss.RemoveString(sb.sShort[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) AddRemove1KLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.PutString(sb.sLong[i%num1KElements]) - ss.RemoveString(sb.sLong[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) LookupNonExistShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.HasString(sb.sShort[0]) - return nil - }) -} - -func (sb stringStoreBench) LookupNonExistLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.HasString(sb.sLong[0]) - return nil - }) -} - -func (sb stringStoreBench) LookupNonExist1KShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.HasString(sb.sShort[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) LookupNonExist1KLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.HasString(sb.sLong[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) RemoveNonExistShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.RemoveString(sb.sShort[0]) - return nil - }) -} - -func (sb stringStoreBench) RemoveNonExistLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.RemoveString(sb.sLong[0]) - return nil - }) -} - -func (sb stringStoreBench) RemoveNonExist1KShort(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.RemoveString(sb.sShort[i%num1KElements]) - return nil - }) -} - -func (sb stringStoreBench) RemoveNonExist1KLong(b *testing.B, cfg *DriverConfig) { - sb.runBenchmark(b, cfg, stringStoreSetupNOP, - func(ss StringStore, i int) error { - ss.RemoveString(sb.sLong[i%num1KElements]) - return nil - }) -} - -// IPStoreBenchmarker is a collection of benchmarks for IPStore drivers. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type IPStoreBenchmarker interface { - AddV4(*testing.B, *DriverConfig) - AddV6(*testing.B, *DriverConfig) - LookupV4(*testing.B, *DriverConfig) - LookupV6(*testing.B, *DriverConfig) - AddRemoveV4(*testing.B, *DriverConfig) - AddRemoveV6(*testing.B, *DriverConfig) - LookupNonExistV4(*testing.B, *DriverConfig) - LookupNonExistV6(*testing.B, *DriverConfig) - RemoveNonExistV4(*testing.B, *DriverConfig) - RemoveNonExistV6(*testing.B, *DriverConfig) - - AddV4Network(*testing.B, *DriverConfig) - AddV6Network(*testing.B, *DriverConfig) - LookupV4Network(*testing.B, *DriverConfig) - LookupV6Network(*testing.B, *DriverConfig) - AddRemoveV4Network(*testing.B, *DriverConfig) - AddRemoveV6Network(*testing.B, *DriverConfig) - RemoveNonExistV4Network(*testing.B, *DriverConfig) - RemoveNonExistV6Network(*testing.B, *DriverConfig) - - Add1KV4(*testing.B, *DriverConfig) - Add1KV6(*testing.B, *DriverConfig) - Lookup1KV4(*testing.B, *DriverConfig) - Lookup1KV6(*testing.B, *DriverConfig) - AddRemove1KV4(*testing.B, *DriverConfig) - AddRemove1KV6(*testing.B, *DriverConfig) - LookupNonExist1KV4(*testing.B, *DriverConfig) - LookupNonExist1KV6(*testing.B, *DriverConfig) - RemoveNonExist1KV4(*testing.B, *DriverConfig) - RemoveNonExist1KV6(*testing.B, *DriverConfig) - - Add1KV4Network(*testing.B, *DriverConfig) - Add1KV6Network(*testing.B, *DriverConfig) - Lookup1KV4Network(*testing.B, *DriverConfig) - Lookup1KV6Network(*testing.B, *DriverConfig) - AddRemove1KV4Network(*testing.B, *DriverConfig) - AddRemove1KV6Network(*testing.B, *DriverConfig) - RemoveNonExist1KV4Network(*testing.B, *DriverConfig) - RemoveNonExist1KV6Network(*testing.B, *DriverConfig) -} - -func generateV4Networks() (a [num1KElements]string) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = fmt.Sprintf("64.%d.%d.255/24", b[0], b[1]) - } - - return -} - -func generateV6Networks() (a [num1KElements]string) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = fmt.Sprintf("6464:6464:6464:%02x%02x:ffff:ffff:ffff:ffff/64", b[0], b[1]) - } - - return -} - -func generateV4IPs() (a [num1KElements]net.IP) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = net.ParseIP(fmt.Sprintf("64.%d.%d.64", b[0], b[1])).To4() - } - - return -} - -func generateV6IPs() (a [num1KElements]net.IP) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = net.ParseIP(fmt.Sprintf("6464:6464:6464:%02x%02x:6464:6464:6464:6464", b[0], b[1])) - } - - return -} - -type ipStoreBench struct { - v4IPs [num1KElements]net.IP - v6IPs [num1KElements]net.IP - - v4Networks [num1KElements]string - v6Networks [num1KElements]string - - driver IPStoreDriver -} - -// PrepareIPStoreBenchmarker prepares a reusable suite for StringStore driver -// benchmarks. -func PrepareIPStoreBenchmarker(driver IPStoreDriver) IPStoreBenchmarker { - return ipStoreBench{ - v4IPs: generateV4IPs(), - v6IPs: generateV6IPs(), - v4Networks: generateV4Networks(), - v6Networks: generateV6Networks(), - driver: driver, - } -} - -type ipStoreSetupFunc func(IPStore) error - -func ipStoreSetupNOP(IPStore) error { return nil } - -type ipStoreBenchFunc func(IPStore, int) error - -func (ib ipStoreBench) runBenchmark(b *testing.B, cfg *DriverConfig, setup ipStoreSetupFunc, execute ipStoreBenchFunc) { - is, err := ib.driver.New(cfg) - require.Nil(b, err, "Constructor error must be nil") - require.NotNil(b, is, "IP store must not be nil") - - err = setup(is) - require.Nil(b, err, "Benchmark setup must not fail") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - execute(is, i) - } - b.StopTimer() - - errChan := is.Stop() - err = <-errChan - require.Nil(b, err, "IPStore shutdown must not fail") -} - -func (ib ipStoreBench) AddV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) AddV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - return is.AddIP(ib.v4IPs[0]) - }, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - return is.AddIP(ib.v6IPs[0]) - }, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) AddRemoveV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v4IPs[0]) - is.RemoveIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) AddRemoveV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v6IPs[0]) - is.RemoveIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupNonExistV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupNonExistV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExistV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExistV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) AddV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v4Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) AddV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v6Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - return is.AddNetwork(ib.v4Networks[0]) - }, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) LookupV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - return is.AddNetwork(ib.v6Networks[0]) - }, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[0]) - return nil - }) -} - -func (ib ipStoreBench) AddRemoveV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v4Networks[0]) - is.RemoveNetwork(ib.v4Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) AddRemoveV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v6Networks[0]) - is.RemoveNetwork(ib.v6Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExistV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveNetwork(ib.v4Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExistV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveNetwork(ib.v6Networks[0]) - return nil - }) -} - -func (ib ipStoreBench) Add1KV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Add1KV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Lookup1KV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - for i := 0; i < num1KElements; i++ { - err := is.AddIP(ib.v4IPs[i%num1KElements]) - if err != nil { - return err - } - } - return nil - }, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Lookup1KV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - for i := 0; i < num1KElements; i++ { - err := is.AddIP(ib.v6IPs[i%num1KElements]) - if err != nil { - return err - } - } - return nil - }, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) AddRemove1KV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v4IPs[i%num1KElements]) - is.RemoveIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) AddRemove1KV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddIP(ib.v6IPs[i%num1KElements]) - is.RemoveIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) LookupNonExist1KV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) LookupNonExist1KV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExist1KV4(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExist1KV6(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Add1KV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v4Networks[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Add1KV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v6Networks[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Lookup1KV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - for i := 0; i < num1KElements; i++ { - err := is.AddNetwork(ib.v4Networks[i%num1KElements]) - if err != nil { - return err - } - } - return nil - }, - func(is IPStore, i int) error { - is.HasIP(ib.v4IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) Lookup1KV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, - func(is IPStore) error { - for i := 0; i < num1KElements; i++ { - err := is.AddNetwork(ib.v6Networks[i%num1KElements]) - if err != nil { - return err - } - } - return nil - }, - func(is IPStore, i int) error { - is.HasIP(ib.v6IPs[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) AddRemove1KV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v4Networks[i%num1KElements]) - is.RemoveNetwork(ib.v4Networks[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) AddRemove1KV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.AddNetwork(ib.v6Networks[i%num1KElements]) - is.RemoveNetwork(ib.v6Networks[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExist1KV4Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveNetwork(ib.v4Networks[i%num1KElements]) - return nil - }) -} - -func (ib ipStoreBench) RemoveNonExist1KV6Network(b *testing.B, cfg *DriverConfig) { - ib.runBenchmark(b, cfg, ipStoreSetupNOP, - func(is IPStore, i int) error { - is.RemoveNetwork(ib.v6Networks[i%num1KElements]) - return nil - }) -} - -// PeerStoreBenchmarker is a collection of benchmarks for PeerStore drivers. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type PeerStoreBenchmarker interface { - PutSeeder(*testing.B, *DriverConfig) - PutSeeder1KInfohash(*testing.B, *DriverConfig) - PutSeeder1KSeeders(*testing.B, *DriverConfig) - PutSeeder1KInfohash1KSeeders(*testing.B, *DriverConfig) - - PutDeleteSeeder(*testing.B, *DriverConfig) - PutDeleteSeeder1KInfohash(*testing.B, *DriverConfig) - PutDeleteSeeder1KSeeders(*testing.B, *DriverConfig) - PutDeleteSeeder1KInfohash1KSeeders(*testing.B, *DriverConfig) - - DeleteSeederNonExist(*testing.B, *DriverConfig) - DeleteSeederNonExist1KInfohash(*testing.B, *DriverConfig) - DeleteSeederNonExist1KSeeders(*testing.B, *DriverConfig) - DeleteSeederNonExist1KInfohash1KSeeders(*testing.B, *DriverConfig) - - PutGraduateDeleteLeecher(*testing.B, *DriverConfig) - PutGraduateDeleteLeecher1KInfohash(*testing.B, *DriverConfig) - PutGraduateDeleteLeecher1KLeechers(*testing.B, *DriverConfig) - PutGraduateDeleteLeecher1KInfohash1KLeechers(*testing.B, *DriverConfig) - - GraduateLeecherNonExist(*testing.B, *DriverConfig) - GraduateLeecherNonExist1KInfohash(*testing.B, *DriverConfig) - GraduateLeecherNonExist1KLeechers(*testing.B, *DriverConfig) - GraduateLeecherNonExist1KInfohash1KLeechers(*testing.B, *DriverConfig) - - AnnouncePeers(*testing.B, *DriverConfig) - AnnouncePeers1KInfohash(*testing.B, *DriverConfig) - AnnouncePeersSeeder(*testing.B, *DriverConfig) - AnnouncePeersSeeder1KInfohash(*testing.B, *DriverConfig) - - GetSeeders(*testing.B, *DriverConfig) - GetSeeders1KInfohash(*testing.B, *DriverConfig) - - NumSeeders(*testing.B, *DriverConfig) - NumSeeders1KInfohash(*testing.B, *DriverConfig) -} - -type peerStoreBench struct { - infohashes [num1KElements]chihaya.InfoHash - peers [num1KElements]chihaya.Peer - driver PeerStoreDriver -} - -func generateInfohashes() (a [num1KElements]chihaya.InfoHash) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = chihaya.InfoHash([20]byte{b[0], b[1]}) - } - - return -} - -func generatePeers() (a [num1KElements]chihaya.Peer) { - b := make([]byte, 2) - for i := range a { - b[0] = byte(i) - b[1] = byte(i >> 8) - a[i] = chihaya.Peer{ - ID: chihaya.PeerID([20]byte{b[0], b[1]}), - IP: net.ParseIP(fmt.Sprintf("64.%d.%d.64", b[0], b[1])), - Port: uint16(i), - } - } - - return -} - -// PreparePeerStoreBenchmarker prepares a reusable suite for PeerStore driver -// benchmarks. -func PreparePeerStoreBenchmarker(driver PeerStoreDriver) PeerStoreBenchmarker { - return peerStoreBench{ - driver: driver, - infohashes: generateInfohashes(), - peers: generatePeers(), - } -} - -type peerStoreSetupFunc func(PeerStore) error - -func peerStoreSetupNOP(PeerStore) error { return nil } - -type peerStoreBenchFunc func(PeerStore, int) error - -func (pb peerStoreBench) runBenchmark(b *testing.B, cfg *DriverConfig, setup peerStoreSetupFunc, execute peerStoreBenchFunc) { - ps, err := pb.driver.New(cfg) - require.Nil(b, err, "Constructor error must be nil") - require.NotNil(b, ps, "Peer store must not be nil") - - err = setup(ps) - require.Nil(b, err, "Benchmark setup must not fail") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - execute(ps, i) - } - b.StopTimer() - - errChan := ps.Stop() - err = <-errChan - require.Nil(b, err, "PeerStore shutdown must not fail") -} - -func (pb peerStoreBench) PutSeeder(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[0], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutSeeder1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[i%num1KElements], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutSeeder1KSeeders(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[0], pb.peers[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) PutSeeder1KInfohash1KSeeders(b *testing.B, cfg *DriverConfig) { - j := 0 - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - j += 3 - return nil - }) -} - -func (pb peerStoreBench) PutDeleteSeeder(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[0], pb.peers[0]) - ps.DeleteSeeder(pb.infohashes[0], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutDeleteSeeder1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[i%num1KElements], pb.peers[0]) - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutDeleteSeeder1KSeeders(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[0], pb.peers[i%num1KElements]) - ps.DeleteSeeder(pb.infohashes[0], pb.peers[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) PutDeleteSeeder1KInfohash1KSeeders(b *testing.B, cfg *DriverConfig) { - j := 0 - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutSeeder(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - j += 3 - return nil - }) -} - -func (pb peerStoreBench) DeleteSeederNonExist(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.DeleteSeeder(pb.infohashes[0], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) DeleteSeederNonExist1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) DeleteSeederNonExist1KSeeders(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.DeleteSeeder(pb.infohashes[0], pb.peers[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) DeleteSeederNonExist1KInfohash1KSeeders(b *testing.B, cfg *DriverConfig) { - j := 0 - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - j += 3 - return nil - }) -} - -func (pb peerStoreBench) GraduateLeecherNonExist(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.GraduateLeecher(pb.infohashes[0], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) GraduateLeecherNonExist1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.GraduateLeecher(pb.infohashes[i%num1KElements], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) GraduateLeecherNonExist1KLeechers(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.GraduateLeecher(pb.infohashes[0], pb.peers[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) GraduateLeecherNonExist1KInfohash1KLeechers(b *testing.B, cfg *DriverConfig) { - j := 0 - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.GraduateLeecher(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - j += 3 - return nil - }) -} - -func (pb peerStoreBench) PutGraduateDeleteLeecher(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutLeecher(pb.infohashes[0], pb.peers[0]) - ps.GraduateLeecher(pb.infohashes[0], pb.peers[0]) - ps.DeleteSeeder(pb.infohashes[0], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutGraduateDeleteLeecher1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutLeecher(pb.infohashes[i%num1KElements], pb.peers[0]) - ps.GraduateLeecher(pb.infohashes[i%num1KElements], pb.peers[0]) - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[0]) - return nil - }) -} - -func (pb peerStoreBench) PutGraduateDeleteLeecher1KLeechers(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutLeecher(pb.infohashes[0], pb.peers[i%num1KElements]) - ps.GraduateLeecher(pb.infohashes[0], pb.peers[i%num1KElements]) - ps.DeleteSeeder(pb.infohashes[0], pb.peers[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) PutGraduateDeleteLeecher1KInfohash1KLeechers(b *testing.B, cfg *DriverConfig) { - j := 0 - pb.runBenchmark(b, cfg, peerStoreSetupNOP, - func(ps PeerStore, i int) error { - ps.PutLeecher(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - ps.GraduateLeecher(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - ps.DeleteSeeder(pb.infohashes[i%num1KElements], pb.peers[j%num1KElements]) - j += 3 - return nil - }) -} - -func (pb peerStoreBench) AnnouncePeers(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.AnnouncePeers(pb.infohashes[0], false, 50, pb.peers[0], chihaya.Peer{}) - return nil - }) -} - -func (pb peerStoreBench) AnnouncePeers1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.AnnouncePeers(pb.infohashes[i%num1KElements], false, 50, pb.peers[0], chihaya.Peer{}) - return nil - }) -} - -func (pb peerStoreBench) AnnouncePeersSeeder(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.AnnouncePeers(pb.infohashes[0], true, 50, pb.peers[0], chihaya.Peer{}) - return nil - }) -} - -func (pb peerStoreBench) AnnouncePeersSeeder1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.AnnouncePeers(pb.infohashes[i%num1KElements], true, 50, pb.peers[0], chihaya.Peer{}) - return nil - }) -} - -func (pb peerStoreBench) GetSeeders(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.GetSeeders(pb.infohashes[0]) - return nil - }) -} - -func (pb peerStoreBench) GetSeeders1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.GetSeeders(pb.infohashes[i%num1KElements]) - return nil - }) -} - -func (pb peerStoreBench) NumSeeders(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.NumSeeders(pb.infohashes[0]) - return nil - }) -} - -func (pb peerStoreBench) NumSeeders1KInfohash(b *testing.B, cfg *DriverConfig) { - pb.runBenchmark(b, cfg, - func(ps PeerStore) error { - for i := 0; i < num1KElements; i++ { - for j := 0; j < num1KElements; j++ { - var err error - if j < num1KElements/2 { - err = ps.PutLeecher(pb.infohashes[i], pb.peers[j]) - } else { - err = ps.PutSeeder(pb.infohashes[i], pb.peers[j]) - } - if err != nil { - return err - } - } - } - return nil - }, - func(ps PeerStore, i int) error { - ps.NumSeeders(pb.infohashes[i%num1KElements]) - return nil - }) -} diff --git a/server/store/store_tests.go b/server/store/store_tests.go deleted file mode 100644 index 3c513d8..0000000 --- a/server/store/store_tests.go +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "testing" - - "net" - - "time" - - "github.com/chihaya/chihaya" - "github.com/stretchr/testify/require" -) - -// StringStoreTester is a collection of tests for a StringStore driver. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type StringStoreTester interface { - TestStringStore(*testing.T, *DriverConfig) -} - -var _ StringStoreTester = &stringStoreTester{} - -type stringStoreTester struct { - s1, s2 string - driver StringStoreDriver -} - -// PrepareStringStoreTester prepares a reusable suite for StringStore driver -// tests. -func PrepareStringStoreTester(driver StringStoreDriver) StringStoreTester { - return &stringStoreTester{ - s1: "abc", - s2: "def", - driver: driver, - } -} - -func (s *stringStoreTester) TestStringStore(t *testing.T, cfg *DriverConfig) { - ss, err := s.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, ss) - - has, err := ss.HasString(s.s1) - require.Nil(t, err) - require.False(t, has) - - has, err = ss.HasString(s.s2) - require.Nil(t, err) - require.False(t, has) - - err = ss.RemoveString(s.s1) - require.NotNil(t, err) - - err = ss.PutString(s.s1) - require.Nil(t, err) - - has, err = ss.HasString(s.s1) - require.Nil(t, err) - require.True(t, has) - - has, err = ss.HasString(s.s2) - require.Nil(t, err) - require.False(t, has) - - err = ss.PutString(s.s1) - require.Nil(t, err) - - err = ss.PutString(s.s2) - require.Nil(t, err) - - has, err = ss.HasString(s.s1) - require.Nil(t, err) - require.True(t, has) - - has, err = ss.HasString(s.s2) - require.Nil(t, err) - require.True(t, has) - - err = ss.RemoveString(s.s1) - require.Nil(t, err) - - err = ss.RemoveString(s.s2) - require.Nil(t, err) - - has, err = ss.HasString(s.s1) - require.Nil(t, err) - require.False(t, has) - - has, err = ss.HasString(s.s2) - require.Nil(t, err) - require.False(t, has) - - errChan := ss.Stop() - err = <-errChan - require.Nil(t, err, "StringStore shutdown must not fail") -} - -// IPStoreTester is a collection of tests for an IPStore driver. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type IPStoreTester interface { - TestIPStore(*testing.T, *DriverConfig) - TestHasAllHasAny(*testing.T, *DriverConfig) - TestNetworks(*testing.T, *DriverConfig) - TestHasAllHasAnyNetworks(*testing.T, *DriverConfig) -} - -var _ IPStoreTester = &ipStoreTester{} - -type ipStoreTester struct { - v6, v4, v4s net.IP - net1, net2 string - inNet1, inNet2 net.IP - excluded net.IP - driver IPStoreDriver -} - -// PrepareIPStoreTester prepares a reusable suite for IPStore driver -// tests. -func PrepareIPStoreTester(driver IPStoreDriver) IPStoreTester { - return &ipStoreTester{ - v6: net.ParseIP("0c22:384e:0:0c22:384e::68"), - v4: net.ParseIP("12.13.14.15"), - v4s: net.ParseIP("12.13.14.15").To4(), - net1: "192.168.22.255/24", - net2: "192.168.23.255/24", - inNet1: net.ParseIP("192.168.22.22"), - inNet2: net.ParseIP("192.168.23.23"), - excluded: net.ParseIP("10.154.243.22"), - driver: driver, - } -} - -func (s *ipStoreTester) TestIPStore(t *testing.T, cfg *DriverConfig) { - is, err := s.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, is) - - // check default state - found, err := is.HasIP(s.v4) - require.Nil(t, err) - require.False(t, found) - - // check IPv4 - err = is.AddIP(s.v4) - require.Nil(t, err) - - found, err = is.HasIP(s.v4) - require.Nil(t, err) - require.True(t, found) - - found, err = is.HasIP(s.v4s) - require.Nil(t, err) - require.True(t, found) - - found, err = is.HasIP(s.v6) - require.Nil(t, err) - require.False(t, found) - - // check removes - err = is.RemoveIP(s.v6) - require.NotNil(t, err) - - err = is.RemoveIP(s.v4s) - require.Nil(t, err) - - found, err = is.HasIP(s.v4) - require.Nil(t, err) - require.False(t, found) - - // check IPv6 - err = is.AddIP(s.v6) - require.Nil(t, err) - - found, err = is.HasIP(s.v6) - require.Nil(t, err) - require.True(t, found) - - err = is.RemoveIP(s.v6) - require.Nil(t, err) - - found, err = is.HasIP(s.v6) - require.Nil(t, err) - require.False(t, found) - - errChan := is.Stop() - err = <-errChan - require.Nil(t, err, "IPStore shutdown must not fail") -} - -func (s *ipStoreTester) TestHasAllHasAny(t *testing.T, cfg *DriverConfig) { - is, err := s.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, is) - - found, err := is.HasAnyIP(nil) - require.Nil(t, err) - require.False(t, found) - - found, err = is.HasAllIPs(nil) - require.Nil(t, err) - require.True(t, found) - - found, err = is.HasAllIPs([]net.IP{s.v6}) - require.Nil(t, err) - require.False(t, found) - - err = is.AddIP(s.v4) - require.Nil(t, err) - - found, err = is.HasAnyIP([]net.IP{s.v6, s.v4}) - require.Nil(t, err) - require.True(t, found) - - found, err = is.HasAllIPs([]net.IP{s.v6, s.v4}) - require.Nil(t, err) - require.False(t, found) - - found, err = is.HasAllIPs([]net.IP{s.v4}) - require.Nil(t, err) - require.True(t, found) - - err = is.AddIP(s.v6) - require.Nil(t, err) - - found, err = is.HasAnyIP([]net.IP{s.v6, s.v6}) - require.Nil(t, err) - require.True(t, found) - - found, err = is.HasAllIPs([]net.IP{s.v6, s.v6}) - require.Nil(t, err) - require.True(t, found) - - errChan := is.Stop() - err = <-errChan - require.Nil(t, err, "IPStore shutdown must not fail") -} - -func (s *ipStoreTester) TestNetworks(t *testing.T, cfg *DriverConfig) { - is, err := s.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, is) - - match, err := is.HasIP(s.inNet1) - require.Nil(t, err) - require.False(t, match) - - match, err = is.HasIP(s.inNet2) - require.Nil(t, err) - require.False(t, match) - - err = is.AddNetwork("") - require.NotNil(t, err) - - err = is.RemoveNetwork("") - require.NotNil(t, err) - - err = is.AddNetwork(s.net1) - require.Nil(t, err) - - match, err = is.HasIP(s.inNet1) - require.Nil(t, err) - require.True(t, match) - - match, err = is.HasIP(s.inNet2) - require.Nil(t, err) - require.False(t, match) - - err = is.RemoveNetwork(s.net2) - require.NotNil(t, err) - - err = is.RemoveNetwork(s.net1) - require.Nil(t, err) - - match, err = is.HasIP(s.inNet1) - require.Nil(t, err) - require.False(t, match) - - match, err = is.HasIP(s.inNet2) - require.Nil(t, err) - require.False(t, match) - - errChan := is.Stop() - err = <-errChan - require.Nil(t, err, "IPStore shutdown must not fail") -} - -func (s *ipStoreTester) TestHasAllHasAnyNetworks(t *testing.T, cfg *DriverConfig) { - is, err := s.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, s) - - match, err := is.HasAnyIP([]net.IP{s.inNet1, s.inNet2, s.excluded}) - require.Nil(t, err) - require.False(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2, s.excluded}) - require.Nil(t, err) - require.False(t, match) - - err = is.AddNetwork(s.net1) - require.Nil(t, err) - - match, err = is.HasAnyIP([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.True(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.False(t, match) - - err = is.AddNetwork(s.net2) - require.Nil(t, err) - - match, err = is.HasAnyIP([]net.IP{s.inNet1, s.inNet2, s.excluded}) - require.Nil(t, err) - require.True(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.True(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2, s.excluded}) - require.Nil(t, err) - require.False(t, match) - - err = is.RemoveNetwork(s.net1) - require.Nil(t, err) - - match, err = is.HasAnyIP([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.True(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.False(t, match) - - err = is.RemoveNetwork(s.net2) - require.Nil(t, err) - - match, err = is.HasAnyIP([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.False(t, match) - - match, err = is.HasAllIPs([]net.IP{s.inNet1, s.inNet2}) - require.Nil(t, err) - require.False(t, match) - - errChan := is.Stop() - err = <-errChan - require.Nil(t, err, "IPStore shutdown must not fail") -} - -// PeerStoreTester is a collection of tests for a PeerStore driver. -// Every benchmark expects a new, clean storage. Every benchmark should be -// called with a DriverConfig that ensures this. -type PeerStoreTester interface { - // CompareEndpoints sets the function used to compare peers to a - // comparison that only compares endpoints and omits PeerIDs. - CompareEndpoints() - - TestPeerStore(*testing.T, *DriverConfig) -} - -var _ PeerStoreTester = &peerStoreTester{} - -type peerStoreTester struct { - driver PeerStoreDriver - equalityFunc func(a, b chihaya.Peer) bool -} - -// PreparePeerStoreTester prepares a reusable suite for PeerStore driver -// tests. -// The tester will use PeerIDs and endpoints to compare peers. -func PreparePeerStoreTester(driver PeerStoreDriver) PeerStoreTester { - return &peerStoreTester{ - driver: driver, - equalityFunc: func(a, b chihaya.Peer) bool { return a.Equal(b) }, - } -} - -func (pt *peerStoreTester) CompareEndpoints() { - pt.equalityFunc = func(a, b chihaya.Peer) bool { return a.EqualEndpoint(b) } -} - -func (pt *peerStoreTester) peerInSlice(peer chihaya.Peer, peers []chihaya.Peer) bool { - for _, v := range peers { - if pt.equalityFunc(peer, v) { - return true - } - } - return false -} - -func (pt *peerStoreTester) TestPeerStore(t *testing.T, cfg *DriverConfig) { - var ( - hash = chihaya.InfoHash([20]byte{}) - - peers = []struct { - seeder bool - peerID string - ip string - port uint16 - }{ - {false, "-AZ3034-6wfG2wk6wWLc", "250.183.81.177", 5720}, - {false, "-AZ3042-6ozMq5q6Q3NX", "38.241.13.19", 4833}, - {false, "-BS5820-oy4La2MWGEFj", "fd45:7856:3dae::48", 2878}, - {false, "-AR6360-6oZyyMWoOOBe", "fd0a:29a8:8445::38", 3167}, - {true, "-AG2083-s1hiF8vGAAg0", "231.231.49.173", 1453}, - {true, "-AG3003-lEl2Mm4NEO4n", "254.99.84.77", 7032}, - {true, "-MR1100-00HS~T7*65rm", "211.229.100.129", 2614}, - {true, "-LK0140-ATIV~nbEQAMr", "fdad:c435:bf79::12", 4114}, - {true, "-KT2210-347143496631", "fdda:1b35:7d6e::9", 6179}, - {true, "-TR0960-6ep6svaa61r4", "fd7f:78f0:4c77::55", 4727}, - } - ) - s, err := pt.driver.New(cfg) - require.Nil(t, err) - require.NotNil(t, s) - - for _, p := range peers { - // Construct chihaya.Peer from test data. - peer := chihaya.Peer{ - ID: chihaya.PeerIDFromString(p.peerID), - IP: net.ParseIP(p.ip), - Port: p.port, - } - - if p.seeder { - err = s.PutSeeder(hash, peer) - } else { - err = s.PutLeecher(hash, peer) - } - require.Nil(t, err) - } - - leechers1, leechers61, err := s.GetLeechers(hash) - require.Nil(t, err) - require.NotEmpty(t, leechers1) - require.NotEmpty(t, leechers61) - num := s.NumLeechers(hash) - require.Equal(t, len(leechers1)+len(leechers61), num) - - seeders1, seeders61, err := s.GetSeeders(hash) - require.Nil(t, err) - require.NotEmpty(t, seeders1) - require.NotEmpty(t, seeders61) - num = s.NumSeeders(hash) - require.Equal(t, len(seeders1)+len(seeders61), num) - - leechers := append(leechers1, leechers61...) - seeders := append(seeders1, seeders61...) - - for _, p := range peers { - // Construct chihaya.Peer from test data. - peer := chihaya.Peer{ - ID: chihaya.PeerIDFromString(p.peerID), - IP: net.ParseIP(p.ip), - Port: p.port, - } - - if p.seeder { - require.True(t, pt.peerInSlice(peer, seeders)) - } else { - require.True(t, pt.peerInSlice(peer, leechers)) - } - - if p.seeder { - err = s.DeleteSeeder(hash, peer) - } else { - err = s.DeleteLeecher(hash, peer) - } - require.Nil(t, err) - } - - require.Zero(t, s.NumLeechers(hash)) - require.Zero(t, s.NumSeeders(hash)) - - // Re-add all the peers to the peerStore. - for _, p := range peers { - // Construct chihaya.Peer from test data. - peer := chihaya.Peer{ - ID: chihaya.PeerIDFromString(p.peerID), - IP: net.ParseIP(p.ip), - Port: p.port, - } - if p.seeder { - s.PutSeeder(hash, peer) - } else { - s.PutLeecher(hash, peer) - } - } - - // Check that there are 6 seeders, and 4 leechers. - require.Equal(t, 6, s.NumSeeders(hash)) - require.Equal(t, 4, s.NumLeechers(hash)) - peer := chihaya.Peer{ - ID: chihaya.PeerIDFromString(peers[0].peerID), - IP: net.ParseIP(peers[0].ip), - Port: peers[0].port, - } - err = s.GraduateLeecher(hash, peer) - require.Nil(t, err) - // Check that there are 7 seeders, and 3 leechers after graduating a - // leecher to a seeder. - require.Equal(t, 7, s.NumSeeders(hash)) - require.Equal(t, 3, s.NumLeechers(hash)) - - _, _, err = s.AnnouncePeers(hash, true, 5, peer, chihaya.Peer{}) - // Only test if it works, do not test the slices returned. They change - // depending on the driver. - require.Nil(t, err) - - err = s.CollectGarbage(time.Now()) - require.Nil(t, err) - require.Equal(t, 0, s.NumLeechers(hash)) - require.Equal(t, 0, s.NumSeeders(hash)) - - errChan := s.Stop() - err = <-errChan - require.Nil(t, err, "PeerStore shutdown must not fail") -} diff --git a/server/store/string_store.go b/server/store/string_store.go deleted file mode 100644 index 77ce849..0000000 --- a/server/store/string_store.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package store - -import ( - "fmt" - - "github.com/chihaya/chihaya/pkg/stopper" -) - -var stringStoreDrivers = make(map[string]StringStoreDriver) - -// StringStore represents an interface for manipulating strings. -type StringStore interface { - // PutString adds the given string to the StringStore. - PutString(s string) error - - // HasString returns whether or not the StringStore contains the given - // string. - HasString(s string) (bool, error) - - // RemoveString removes the string from the string store. - // Returns ErrResourceDoesNotExist if the given string is not contained - // in the store. - RemoveString(s string) error - - // Stopper provides the Stop method that stops the StringStore. - // Stop should shut down the StringStore in a separate goroutine and send - // an error to the channel if the shutdown failed. If the shutdown - // was successful, the channel is to be closed. - stopper.Stopper -} - -// StringStoreDriver represents an interface for creating a handle to the -// storage of strings. -type StringStoreDriver interface { - New(*DriverConfig) (StringStore, error) -} - -// RegisterStringStoreDriver makes a driver available by the provided name. -// -// If this function is called twice with the same name or if the driver is nil, -// it panics. -func RegisterStringStoreDriver(name string, driver StringStoreDriver) { - if driver == nil { - panic("store: could not register nil StringStoreDriver") - } - if _, dup := stringStoreDrivers[name]; dup { - panic("store: could not register duplicate StringStoreDriver: " + name) - } - stringStoreDrivers[name] = driver -} - -// OpenStringStore returns a StringStore specified by a configuration. -func OpenStringStore(cfg *DriverConfig) (StringStore, error) { - driver, ok := stringStoreDrivers[cfg.Name] - if !ok { - return nil, fmt.Errorf("store: unknown StringStoreDriver %q (forgotten import?)", cfg) - } - - return driver.New(cfg) -} diff --git a/tracker/middleware.go b/tracker/middleware.go deleted file mode 100644 index ba8e133..0000000 --- a/tracker/middleware.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package tracker - -import "github.com/chihaya/chihaya" - -// AnnounceHandler is a function that operates on an AnnounceResponse before it -// has been delivered to a client. -type AnnounceHandler func(*chihaya.TrackerConfig, *chihaya.AnnounceRequest, *chihaya.AnnounceResponse) error - -// AnnounceMiddleware is a higher-order function used to implement the chaining -// of AnnounceHandlers. -type AnnounceMiddleware func(AnnounceHandler) AnnounceHandler - -// AnnounceMiddlewareConstructor is a function that creates a new -// AnnounceMiddleware from a MiddlewareConfig. -type AnnounceMiddlewareConstructor func(chihaya.MiddlewareConfig) (AnnounceMiddleware, error) - -// AnnounceChain is a chain of AnnounceMiddlewares. -type AnnounceChain struct{ mw []AnnounceMiddleware } - -// Append appends AnnounceMiddlewares to the AnnounceChain. -func (c *AnnounceChain) Append(mw ...AnnounceMiddleware) { - c.mw = append(c.mw, mw...) -} - -// Handler builds an AnnounceChain into an AnnounceHandler. -func (c *AnnounceChain) Handler() AnnounceHandler { - final := func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - return nil - } - - for i := len(c.mw) - 1; i >= 0; i-- { - final = c.mw[i](final) - } - return final -} - -var announceMiddlewareConstructors = make(map[string]AnnounceMiddlewareConstructor) - -// RegisterAnnounceMiddlewareConstructor makes a configurable middleware -// globally available under the provided name. -// -// If this function is called twice with the same name or if the constructor is -// nil, it panics. -func RegisterAnnounceMiddlewareConstructor(name string, mw AnnounceMiddlewareConstructor) { - if mw == nil { - panic("tracker: could not register nil AnnounceMiddlewareConstructor") - } - - if _, dup := announceMiddlewareConstructors[name]; dup { - panic("tracker: could not register duplicate AnnounceMiddleware: " + name) - } - - announceMiddlewareConstructors[name] = mw -} - -// RegisterAnnounceMiddleware makes a middleware globally available under the -// provided name. -// -// This function is intended to register middleware that has no configuration. -// If this function is called twice with the same name or if the middleware is -// nil, it panics. -func RegisterAnnounceMiddleware(name string, mw AnnounceMiddleware) { - if mw == nil { - panic("tracker: could not register nil AnnounceMiddleware") - } - - RegisterAnnounceMiddlewareConstructor(name, func(_ chihaya.MiddlewareConfig) (AnnounceMiddleware, error) { - return mw, nil - }) -} - -// ScrapeHandler is a function that operates on a ScrapeResponse before it has -// been delivered to a client. -type ScrapeHandler func(*chihaya.TrackerConfig, *chihaya.ScrapeRequest, *chihaya.ScrapeResponse) error - -// ScrapeMiddleware is higher-order function used to implement the chaining of -// ScrapeHandlers. -type ScrapeMiddleware func(ScrapeHandler) ScrapeHandler - -// ScrapeMiddlewareConstructor is a function that creates a new -// ScrapeMiddleware from a MiddlewareConfig. -type ScrapeMiddlewareConstructor func(chihaya.MiddlewareConfig) (ScrapeMiddleware, error) - -// ScrapeChain is a chain of ScrapeMiddlewares. -type ScrapeChain struct{ mw []ScrapeMiddleware } - -// Append appends ScrapeMiddlewares to the ScrapeChain. -func (c *ScrapeChain) Append(mw ...ScrapeMiddleware) { - c.mw = append(c.mw, mw...) -} - -// Handler builds the ScrapeChain into a ScrapeHandler. -func (c *ScrapeChain) Handler() ScrapeHandler { - final := func(cfg *chihaya.TrackerConfig, req *chihaya.ScrapeRequest, resp *chihaya.ScrapeResponse) error { - return nil - } - for i := len(c.mw) - 1; i >= 0; i-- { - final = c.mw[i](final) - } - return final -} - -var scrapeMiddlewareConstructors = make(map[string]ScrapeMiddlewareConstructor) - -// RegisterScrapeMiddlewareConstructor makes a configurable middleware globally -// available under the provided name. -// -// If this function is called twice with the same name or if the constructor is -// nil, it panics. -func RegisterScrapeMiddlewareConstructor(name string, mw ScrapeMiddlewareConstructor) { - if mw == nil { - panic("tracker: could not register nil ScrapeMiddlewareConstructor") - } - - if _, dup := scrapeMiddlewareConstructors[name]; dup { - panic("tracker: could not register duplicate ScrapeMiddleware: " + name) - } - - scrapeMiddlewareConstructors[name] = mw -} - -// RegisterScrapeMiddleware makes a middleware globally available under the -// provided name. -// -// This function is intended to register middleware that has no configuration. -// If this function is called twice with the same name or if the middleware is -// nil, it panics. -func RegisterScrapeMiddleware(name string, mw ScrapeMiddleware) { - if mw == nil { - panic("tracker: could not register nil ScrapeMiddleware") - } - - RegisterScrapeMiddlewareConstructor(name, func(_ chihaya.MiddlewareConfig) (ScrapeMiddleware, error) { - return mw, nil - }) -} diff --git a/tracker/middleware_test.go b/tracker/middleware_test.go deleted file mode 100644 index 4d5f19d..0000000 --- a/tracker/middleware_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -package tracker - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/chihaya/chihaya" -) - -func testAnnounceMW1(next AnnounceHandler) AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - resp.IPv4Peers = append(resp.IPv4Peers, chihaya.Peer{ - Port: 1, - }) - return next(cfg, req, resp) - } -} - -func testAnnounceMW2(next AnnounceHandler) AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - resp.IPv4Peers = append(resp.IPv4Peers, chihaya.Peer{ - Port: 2, - }) - return next(cfg, req, resp) - } -} - -func testAnnounceMW3(next AnnounceHandler) AnnounceHandler { - return func(cfg *chihaya.TrackerConfig, req *chihaya.AnnounceRequest, resp *chihaya.AnnounceResponse) error { - resp.IPv4Peers = append(resp.IPv4Peers, chihaya.Peer{ - Port: 3, - }) - return next(cfg, req, resp) - } -} - -func TestAnnounceChain(t *testing.T) { - var achain AnnounceChain - achain.Append(testAnnounceMW1) - achain.Append(testAnnounceMW2) - achain.Append(testAnnounceMW3) - handler := achain.Handler() - resp := &chihaya.AnnounceResponse{} - err := handler(nil, &chihaya.AnnounceRequest{}, resp) - assert.Nil(t, err, "the handler should not return an error") - assert.Equal(t, resp.IPv4Peers, []chihaya.Peer{{Port: 1}, {Port: 2}, {Port: 3}}, "the list of peers added from the middleware should be in the same order.") -} diff --git a/tracker/tracker.go b/tracker/tracker.go deleted file mode 100644 index a0b8391..0000000 --- a/tracker/tracker.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2016 The Chihaya Authors. All rights reserved. -// Use of this source code is governed by the BSD 2-Clause license, -// which can be found in the LICENSE file. - -// Package tracker implements a protocol-independent, middleware-composed -// BitTorrent tracker. -package tracker - -import ( - "errors" - "fmt" - - "github.com/chihaya/chihaya" -) - -// ClientError represents an error that should be exposed to the client over -// the BitTorrent protocol implementation. -type ClientError string - -// Error implements the error interface for ClientError. -func (c ClientError) Error() string { return string(c) } - -// Tracker represents a protocol-independent, middleware-composed BitTorrent -// tracker. -type Tracker struct { - cfg *chihaya.TrackerConfig - handleAnnounce AnnounceHandler - handleScrape ScrapeHandler -} - -// NewTracker constructs a newly allocated Tracker composed of the middleware -// in the provided configuration. -func NewTracker(cfg *chihaya.TrackerConfig) (*Tracker, error) { - var achain AnnounceChain - for _, mwConfig := range cfg.AnnounceMiddleware { - mw, ok := announceMiddlewareConstructors[mwConfig.Name] - if !ok { - return nil, errors.New("failed to find announce middleware: " + mwConfig.Name) - } - middleware, err := mw(mwConfig) - if err != nil { - return nil, fmt.Errorf("failed to load announce middleware %q: %s", mwConfig.Name, err.Error()) - } - achain.Append(middleware) - } - - var schain ScrapeChain - for _, mwConfig := range cfg.ScrapeMiddleware { - mw, ok := scrapeMiddlewareConstructors[mwConfig.Name] - if !ok { - return nil, errors.New("failed to find scrape middleware: " + mwConfig.Name) - } - middleware, err := mw(mwConfig) - if err != nil { - return nil, fmt.Errorf("failed to load scrape middleware %q: %s", mwConfig.Name, err.Error()) - } - schain.Append(middleware) - } - - return &Tracker{ - cfg: cfg, - handleAnnounce: achain.Handler(), - handleScrape: schain.Handler(), - }, nil -} - -// HandleAnnounce runs an AnnounceRequest through the Tracker's middleware and -// returns the result. -func (t *Tracker) HandleAnnounce(req *chihaya.AnnounceRequest) (*chihaya.AnnounceResponse, error) { - resp := &chihaya.AnnounceResponse{} - err := t.handleAnnounce(t.cfg, req, resp) - return resp, err -} - -// HandleScrape runs a ScrapeRequest through the Tracker's middleware and -// returns the result. -func (t *Tracker) HandleScrape(req *chihaya.ScrapeRequest) (*chihaya.ScrapeResponse, error) { - resp := &chihaya.ScrapeResponse{ - Files: make(map[chihaya.InfoHash]chihaya.Scrape), - } - err := t.handleScrape(t.cfg, req, resp) - return resp, err -}