Merge branch 'master' into udp
This commit is contained in:
commit
f25464a02b
7 changed files with 178 additions and 42 deletions
120
CONFIGURATION.md
Normal file
120
CONFIGURATION.md
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
Chihaya's behaviour is customized by setting up a JSON configuration file.
|
||||||
|
Available keys are as follows:
|
||||||
|
|
||||||
|
##### `http_listen_addr`
|
||||||
|
|
||||||
|
type: string
|
||||||
|
default: ":6881"
|
||||||
|
|
||||||
|
The listen address for the HTTP server. If only a port is specified, the tracker will listen on all interfaces.
|
||||||
|
|
||||||
|
##### `private_enabled`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
Whether this is a public or private tracker.
|
||||||
|
|
||||||
|
##### `create_on_announce`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
Whether to register new torrents with the tracker when any client announces (`true`), or to return an error if the torrent doesn't exist (`false`). This should be set to `false` for private trackers in most cases.
|
||||||
|
|
||||||
|
##### `purge_inactive_torrents`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
If torrents should be forgotten when there are no active peers. This should be set to `false` for private trackers.
|
||||||
|
|
||||||
|
##### `announce`
|
||||||
|
|
||||||
|
type: duration
|
||||||
|
default: "30m"
|
||||||
|
|
||||||
|
The announce `interval` value sent to clients. This specifies how long clients should wait between regular announces.
|
||||||
|
|
||||||
|
##### `min_announce`
|
||||||
|
|
||||||
|
type: duration
|
||||||
|
default: "30m"
|
||||||
|
|
||||||
|
The announce `min_interval` value sent to clients. This theoretically specifies the minimum allowed time between announces, but most clients don't really respect it.
|
||||||
|
|
||||||
|
##### `default_num_want`
|
||||||
|
|
||||||
|
type: integer
|
||||||
|
default: 50
|
||||||
|
|
||||||
|
The default maximum number of peers to return if the client has not requested a specific number.
|
||||||
|
|
||||||
|
##### `allow_ip_spoofing`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
Whether peers are allowed to set their own IP via the various supported methods or if these are ignored. This must be enabled for dual-stack IP support, since there is no other way to determine both IPs of a peer otherwise.
|
||||||
|
|
||||||
|
##### `dual_stacked_peers`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
|
||||||
|
True if peers may have both an IPv4 and IPv6 address, otherwise only one IP per peer will be used.
|
||||||
|
|
||||||
|
##### `real_ip_header`
|
||||||
|
|
||||||
|
type: string
|
||||||
|
default: blank
|
||||||
|
|
||||||
|
An optional HTTP header indicating the upstream IP, for example `X-Forwarded-For` or `X-Real-IP`. Use this when running the tracker behind a reverse proxy.
|
||||||
|
|
||||||
|
##### `respect_af`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
Whether responses should only include peers of the same address family as the announcing peer, or if peers of any family may be returned (i.e. both IPv4 and IPv6).
|
||||||
|
|
||||||
|
##### `client_whitelist_enabled`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
Enables the peer ID whitelist.
|
||||||
|
|
||||||
|
##### `client_whitelist`
|
||||||
|
|
||||||
|
type: array of strings
|
||||||
|
default: []
|
||||||
|
|
||||||
|
List of peer ID prefixes to allow if `client_whitelist_enabled` is set to true.
|
||||||
|
|
||||||
|
##### `freeleech_enabled`
|
||||||
|
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
|
||||||
|
For private trackers only, whether download stats should be counted or ignored for users.
|
||||||
|
|
||||||
|
##### `torrent_map_shards`
|
||||||
|
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
|
||||||
|
Number of internal torrent maps to use. Leave this at 1 in general, however it can potentially improve performance when there are many unique torrents and few peers per torrent.
|
||||||
|
|
||||||
|
- `http_request_timeout: "10s"`
|
||||||
|
- `http_read_timeout: "10s"`
|
||||||
|
- `http_write_timeout: "10s"`
|
||||||
|
- `http_listen_limit: 0`
|
||||||
|
- `driver: "noop"`
|
||||||
|
- `stats_buffer_size: 0`
|
||||||
|
- `include_mem_stats: true`
|
||||||
|
- `verbose_mem_stats: false`
|
||||||
|
- `mem_stats_interval: "5s"`
|
||||||
|
|
24
Dockerfile
Normal file
24
Dockerfile
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# vim: ft=dockerfile
|
||||||
|
FROM golang
|
||||||
|
MAINTAINER Jimmy Zelinskie <jimmyzelinskie@gmail.com>
|
||||||
|
|
||||||
|
# Add files
|
||||||
|
WORKDIR /go/src/github.com/chihaya/chihaya/
|
||||||
|
RUN mkdir -p /go/src/github.com/chihaya/chihaya/
|
||||||
|
ADD chihaya.go /go/src/github.com/chihaya/chihaya/
|
||||||
|
ADD backend /go/src/github.com/chihaya/chihaya/backend
|
||||||
|
ADD cmd /go/src/github.com/chihaya/chihaya/cmd
|
||||||
|
ADD config /go/src/github.com/chihaya/chihaya/config
|
||||||
|
ADD http /go/src/github.com/chihaya/chihaya/http
|
||||||
|
ADD stats /go/src/github.com/chihaya/chihaya/stats
|
||||||
|
ADD tracker /go/src/github.com/chihaya/chihaya/tracker
|
||||||
|
ADD Godeps /go/src/github.com/chihaya/chihaya/Godeps
|
||||||
|
|
||||||
|
# Install
|
||||||
|
RUN go get ./...
|
||||||
|
RUN go install
|
||||||
|
|
||||||
|
# docker run -p 6881:6881 -v $PATH_TO_DIR_WITH_CONF_FILE:/config quay.io/jzelinskie/chihaya
|
||||||
|
VOLUME ["/config"]
|
||||||
|
EXPOSE 6881
|
||||||
|
CMD ["chihaya", "-config=/config/config.json", "-logtostderr=true"]
|
35
README.md
35
README.md
|
@ -1,4 +1,8 @@
|
||||||
# Chihaya [![Build Status](https://api.travis-ci.org/chihaya/chihaya.svg?branch=master)](https://travis-ci.org/chihaya/chihaya)
|
# Chihaya
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/chihaya/chihaya?status.svg)](https://godoc.org/github.com/chihaya/chihaya)
|
||||||
|
[![Build Status](https://api.travis-ci.org/chihaya/chihaya.svg?branch=master)](https://travis-ci.org/chihaya/chihaya)
|
||||||
|
[![Docker Repository on Quay.io](https://quay.io/repository/jzelinskie/chihaya/status "Docker Repository on Quay.io")](https://quay.io/repository/jzelinskie/chihaya)
|
||||||
|
|
||||||
Chihaya is a high-performance [BitTorrent tracker] written in the Go
|
Chihaya is a high-performance [BitTorrent tracker] written in the Go
|
||||||
programming language. It is still heavily under development and the current
|
programming language. It is still heavily under development and the current
|
||||||
|
@ -32,7 +36,7 @@ use-cases).
|
||||||
|
|
||||||
## Building & Installing
|
## Building & Installing
|
||||||
|
|
||||||
Chihaya requires Go 1.4, [Godep], and a [Go environment] previously setup.
|
Chihaya requires 64-bit Go 1.4, [Godep], and a [Go environment] previously set up.
|
||||||
|
|
||||||
[Godep]: https://github.com/tools/godep
|
[Godep]: https://github.com/tools/godep
|
||||||
[Go environment]: https://golang.org/doc/code.html
|
[Go environment]: https://golang.org/doc/code.html
|
||||||
|
@ -65,30 +69,5 @@ $ godep go test -v ./... -bench .
|
||||||
|
|
||||||
Copy [`example_config.json`](https://github.com/chihaya/chihaya/blob/master/example_config.json)
|
Copy [`example_config.json`](https://github.com/chihaya/chihaya/blob/master/example_config.json)
|
||||||
to your choice of location, and update the values as required.
|
to your choice of location, and update the values as required.
|
||||||
The available keys and their default values are as follows:
|
An explanation of the available keys can be found in [CONFIGURATION.md](https://github.com/chihaya/chihaya/blob/master/CONFIGURATION.md).
|
||||||
|
|
||||||
- `private_enabled: false` – if this is a private tracker
|
|
||||||
- `freeleech_enabled: false` – for private trackers, whether download stats should be counted for users
|
|
||||||
- `purge_inactive_torrents: true` – if torrents should be forgotten after some time
|
|
||||||
- `announce: "30m"` – the announce "interval" value sent to clients
|
|
||||||
- `min_announce: "15m"` – the announce "min_interval" value sent to clients
|
|
||||||
- `default_num_want: 50` – the default number of peers to return if the client has not specified
|
|
||||||
- `torrent_map_shards: 1` – number of torrent maps to use (leave this at 1 in general)
|
|
||||||
- `allow_ip_spoofing: true` – if peers are allowed to set their own IP, this must be enabled for dual-stack IP support
|
|
||||||
- `dual_stacked_peers: true` – if peers may have both an IPv4 and IPv6 address, otherwise only one IP per peer will be used
|
|
||||||
- `real_ip_header: ""` – optionally an HTTP header where the upstream IP is stored, for example `X-Forwarded-For` or `X-Real-IP`
|
|
||||||
- `respect_af: false` – if responses should only include peers of the same address family as the announcing peer
|
|
||||||
- `client_whitelist_enabled: false` – if peer IDs should be matched against the whitelist
|
|
||||||
- `client_whitelist: []` – list of peer ID prefixes to allow
|
|
||||||
- `http_listen_addr: ""` – listen address for the HTTP server
|
|
||||||
- `http_request_timeout: "10s"`
|
|
||||||
- `http_read_timeout: "10s"`
|
|
||||||
- `http_write_timeout: "10s"`
|
|
||||||
- `http_listen_limit: 0`
|
|
||||||
- `udp_listen_addr: ""` – listen address for the UDP server
|
|
||||||
- `udp_read_buffer_size: undefined` – size of the UDP socket's kernel read buffer
|
|
||||||
- `driver: "noop"`
|
|
||||||
- `stats_buffer_size: 0`
|
|
||||||
- `include_mem_stats: true`
|
|
||||||
- `verbose_mem_stats: false`
|
|
||||||
- `mem_stats_interval: "5s"`
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ type WhitelistConfig struct {
|
||||||
|
|
||||||
// TrackerConfig is the configuration for tracker functionality.
|
// TrackerConfig is the configuration for tracker functionality.
|
||||||
type TrackerConfig struct {
|
type TrackerConfig struct {
|
||||||
|
CreateOnAnnounce bool `json:"create_on_announce"`
|
||||||
PrivateEnabled bool `json:"private_enabled"`
|
PrivateEnabled bool `json:"private_enabled"`
|
||||||
FreeleechEnabled bool `json:"freeleech_enabled"`
|
FreeleechEnabled bool `json:"freeleech_enabled"`
|
||||||
PurgeInactiveTorrents bool `json:"purge_inactive_torrents"`
|
PurgeInactiveTorrents bool `json:"purge_inactive_torrents"`
|
||||||
|
@ -114,6 +115,7 @@ type Config struct {
|
||||||
// DefaultConfig is a configuration that can be used as a fallback value.
|
// DefaultConfig is a configuration that can be used as a fallback value.
|
||||||
var DefaultConfig = Config{
|
var DefaultConfig = Config{
|
||||||
TrackerConfig: TrackerConfig{
|
TrackerConfig: TrackerConfig{
|
||||||
|
CreateOnAnnounce: true,
|
||||||
PrivateEnabled: false,
|
PrivateEnabled: false,
|
||||||
FreeleechEnabled: false,
|
FreeleechEnabled: false,
|
||||||
PurgeInactiveTorrents: true,
|
PurgeInactiveTorrents: true,
|
||||||
|
@ -134,14 +136,14 @@ var DefaultConfig = Config{
|
||||||
},
|
},
|
||||||
|
|
||||||
HTTPConfig: HTTPConfig{
|
HTTPConfig: HTTPConfig{
|
||||||
HTTPListenAddr: "",
|
HTTPListenAddr: ":6881",
|
||||||
HTTPRequestTimeout: Duration{10 * time.Second},
|
HTTPRequestTimeout: Duration{10 * time.Second},
|
||||||
HTTPReadTimeout: Duration{10 * time.Second},
|
HTTPReadTimeout: Duration{10 * time.Second},
|
||||||
HTTPWriteTimeout: Duration{10 * time.Second},
|
HTTPWriteTimeout: Duration{10 * time.Second},
|
||||||
},
|
},
|
||||||
|
|
||||||
UDPConfig: UDPConfig{
|
UDPConfig: UDPConfig{
|
||||||
UDPListenAddr: "",
|
UDPListenAddr: ":6882",
|
||||||
},
|
},
|
||||||
|
|
||||||
DriverConfig: DriverConfig{
|
DriverConfig: DriverConfig{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"create_on_announce": true,
|
||||||
"private_enabled": false,
|
"private_enabled": false,
|
||||||
"freeleech_enabled": false,
|
"freeleech_enabled": false,
|
||||||
"purge_inactive_torrents": true,
|
"purge_inactive_torrents": true,
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (tkr *Tracker) HandleAnnounce(ann *models.Announce, w Writer) (err error) {
|
||||||
|
|
||||||
torrent, err := tkr.FindTorrent(ann.Infohash)
|
torrent, err := tkr.FindTorrent(ann.Infohash)
|
||||||
|
|
||||||
if err == models.ErrTorrentDNE && !tkr.Config.PrivateEnabled {
|
if err == models.ErrTorrentDNE && tkr.Config.CreateOnAnnounce {
|
||||||
torrent = &models.Torrent{
|
torrent = &models.Torrent{
|
||||||
Infohash: ann.Infohash,
|
Infohash: ann.Infohash,
|
||||||
Seeders: models.NewPeerMap(true, tkr.Config),
|
Seeders: models.NewPeerMap(true, tkr.Config),
|
||||||
|
@ -231,20 +231,13 @@ func (tkr *Tracker) handlePeerEvent(ann *models.Announce, p *models.Peer) (snatc
|
||||||
}
|
}
|
||||||
|
|
||||||
case ann.Event == "completed":
|
case ann.Event == "completed":
|
||||||
v4seed := t.Seeders.Contains(p.Key())
|
|
||||||
v6seed := t.Seeders.Contains(p.Key())
|
|
||||||
|
|
||||||
if t.Leechers.Contains(p.Key()) {
|
if t.Leechers.Contains(p.Key()) {
|
||||||
err = tkr.leecherFinished(t, p)
|
err = tkr.leecherFinished(t, p)
|
||||||
} else {
|
} else {
|
||||||
err = models.ErrBadRequest
|
err = models.ErrBadRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the dual-stacked peers is already a seeder, they have
|
snatched = true
|
||||||
// already snatched.
|
|
||||||
if !(v4seed || v6seed) {
|
|
||||||
snatched = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case t.Leechers.Contains(p.Key()) && ann.Left == 0:
|
case t.Leechers.Contains(p.Key()) && ann.Left == 0:
|
||||||
// A leecher completed but the event was never received.
|
// A leecher completed but the event was never received.
|
||||||
|
|
|
@ -8,6 +8,7 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -52,13 +53,18 @@ func IsPublicError(err error) bool {
|
||||||
return cl || nf || pc
|
return cl || nf || pc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerList represents a list of peers: either seeders or leechers.
|
||||||
type PeerList []Peer
|
type PeerList []Peer
|
||||||
|
|
||||||
|
// PeerKey is the key used to uniquely identify a peer in a swarm.
|
||||||
type PeerKey string
|
type PeerKey string
|
||||||
|
|
||||||
func NewPeerKey(peerID string, ip net.IP) PeerKey {
|
// NewPeerKey creates a properly formatted PeerKey.
|
||||||
return PeerKey(peerID + "//" + ip.String())
|
func NewPeerKey(peerID string, ip net.IP, port string) PeerKey {
|
||||||
|
return PeerKey(peerID + "//" + ip.String() + ":" + port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IP parses and returns the IP address for a given PeerKey.
|
||||||
func (pk PeerKey) IP() net.IP {
|
func (pk PeerKey) IP() net.IP {
|
||||||
ip := net.ParseIP(strings.Split(string(pk), "//")[1])
|
ip := net.ParseIP(strings.Split(string(pk), "//")[1])
|
||||||
if rval := ip.To4(); rval != nil {
|
if rval := ip.To4(); rval != nil {
|
||||||
|
@ -67,10 +73,16 @@ func (pk PeerKey) IP() net.IP {
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerID returns the PeerID section of a PeerKey.
|
||||||
func (pk PeerKey) PeerID() string {
|
func (pk PeerKey) PeerID() string {
|
||||||
return strings.Split(string(pk), "//")[0]
|
return strings.Split(string(pk), "//")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Port returns the port section of the PeerKey.
|
||||||
|
func (pk PeerKey) Port() string {
|
||||||
|
return strings.Split(string(pk), "//")[2]
|
||||||
|
}
|
||||||
|
|
||||||
// Peer is a participant in a swarm.
|
// Peer is a participant in a swarm.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
@ -88,16 +100,21 @@ type Peer struct {
|
||||||
LastAnnounce int64 `json:"last_announce"`
|
LastAnnounce int64 `json:"last_announce"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasIPv4 determines if a peer's IP address can be represented as an IPv4
|
||||||
|
// address.
|
||||||
func (p *Peer) HasIPv4() bool {
|
func (p *Peer) HasIPv4() bool {
|
||||||
return !p.HasIPv6()
|
return !p.HasIPv6()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasIPv6 determines if a peer's IP address can be represented as an IPv6
|
||||||
|
// address.
|
||||||
func (p *Peer) HasIPv6() bool {
|
func (p *Peer) HasIPv6() bool {
|
||||||
return len(p.IP) == net.IPv6len
|
return len(p.IP) == net.IPv6len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Key returns a PeerKey for the given peer.
|
||||||
func (p *Peer) Key() PeerKey {
|
func (p *Peer) Key() PeerKey {
|
||||||
return NewPeerKey(p.ID, p.IP)
|
return NewPeerKey(p.ID, p.IP, strconv.FormatUint(p.Port, 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Torrent is a swarm for a given torrent file.
|
// Torrent is a swarm for a given torrent file.
|
||||||
|
|
Loading…
Add table
Reference in a new issue