Merge branch 'master' into udp

This commit is contained in:
Jimmy Zelinskie 2015-03-27 22:05:48 -04:00
commit f25464a02b
7 changed files with 178 additions and 42 deletions

120
CONFIGURATION.md Normal file
View 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
View 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"]

View file

@ -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"`

View file

@ -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{

View file

@ -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,

View file

@ -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.

View file

@ -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.