Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Release 0.5.0 #155

Merged
merged 6 commits into from
Nov 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 53 additions & 46 deletions server/announce.go → announce/announce.go
Original file line number Diff line number Diff line change
@@ -1,62 +1,69 @@
package server
package announce

import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
r "github.com/GrappigPanda/notorious/kvStoreInterfaces"
)

func (a *announceData) parseAnnounceData(req *http.Request) (err error) {
func (a *AnnounceData) ParseAnnounceData(req *http.Request) (err error) {
query := req.URL.Query()
a.info_hash = ParseInfoHash(query.Get("info_hash"))
if a.info_hash == "" {
err = fmt.Errorf("No info_hash provided.")

a.RequestContext = requestAppContext{
dbConn: nil,
Whitelist: false,
}

a.InfoHash = ParseInfoHash(query.Get("InfoHash"))
if a.InfoHash == "" {
err = fmt.Errorf("No InfoHash provided.")
return
}
if strings.Contains(req.RemoteAddr, ":") {
a.ip = strings.Split(req.RemoteAddr, ":")[0]
a.IP = strings.Split(req.RemoteAddr, ":")[0]
} else {
a.ip = query.Get(req.RemoteAddr)
a.IP = query.Get(req.RemoteAddr)
}
a.peer_id = query.Get("peer_id")
a.PeerID = query.Get("peer_id")

a.port, err = GetInt(query, "port")
a.Port, err = GetInt(query, "port")
if err != nil {
return fmt.Errorf("Failed to get port")
}
a.downloaded, err = GetInt(query, "downloaded")
a.Downloaded, err = GetInt(query, "downloaded")
if err != nil {
err = fmt.Errorf("Failed to get downloaded byte count.")
return
}
a.uploaded, err = GetInt(query, "uploaded")
a.Uploaded, err = GetInt(query, "uploaded")
if err != nil {
err = fmt.Errorf("Failed to get uploaded byte count.")
return
}
a.left, err = GetInt(query, "left")
a.Left, err = GetInt(query, "left")
if err != nil {
err = fmt.Errorf("Failed to get remaining byte count.")
return
}
a.numwant, err = GetInt(query, "numwant")
a.Numwant, err = GetInt(query, "numwant")
if err != nil {
a.numwant = 0
a.Numwant = 0
}
if x := query.Get("compact"); x != "" {
a.compact, err = strconv.ParseBool(x)
a.Compact, err = strconv.ParseBool(x)
if err != nil {
a.compact = false
a.Compact = false
}
}
a.event = query.Get("event")
if a.event == " " || a.event == "" {
a.event = "started"
a.Event = query.Get("event")
if a.Event == " " || a.Event == "" {
a.Event = "started"
}

a.requestContext.redisClient = OpenClient()
a.RequestContext.redisClient = r.OpenClient()

return
}
Expand All @@ -78,33 +85,33 @@ func GetInt(u url.Values, key string) (ui uint64, err error) {

// StartedEventHandler handles whenever a peer sends the STARTED event to the
// tracker.
func (a *announceData) StartedEventHandler() (err error) {
func (a *AnnounceData) StartedEventHandler() (err error) {
// Called upon announce when a client starts a download or creates a new
// torrent on the tracker. Adds a user to incomplete list in redis.
err = nil

if !a.infoHashExists() && a.requestContext.whitelist {
if !a.infoHashExists() && a.RequestContext.Whitelist {
err = fmt.Errorf("Torrent not authorized for use")
return
} else if !a.infoHashExists() && !a.requestContext.whitelist {
// If the info hash isn't in redis and we're not whitelisting, add it
} else if !a.infoHashExists() && !a.RequestContext.Whitelist {
// If the info hash isn't in redis and we're not Whitelisting, add it
// to Redis.
a.createInfoHashKey()
}

keymember := ""
ipport := ""

if !(a.left == 0) {
keymember = fmt.Sprintf("%s:incomplete", a.info_hash)
ipport = fmt.Sprintf("%s:%d", a.ip, a.port)
if !(a.Left == 0) {
keymember = fmt.Sprintf("%s:incomplete", a.InfoHash)
ipport = fmt.Sprintf("%s:%d", a.IP, a.Port)
} else {
keymember = fmt.Sprintf("%s:complete", a.info_hash)
ipport = fmt.Sprintf("%s:%d", a.ip, a.port)
keymember = fmt.Sprintf("%s:complete", a.InfoHash)
ipport = fmt.Sprintf("%s:%d", a.IP, a.Port)
}

RedisSetKeyVal(a.requestContext.redisClient, keymember, ipport)
if RedisSetKeyIfNotExists(a.requestContext.redisClient, keymember, ipport) {
r.RedisSetKeyVal(a.RequestContext.redisClient, keymember, ipport)
if r.RedisSetKeyIfNotExists(a.RequestContext.redisClient, keymember, ipport) {
fmt.Printf("Adding host %s to %s\n", ipport, keymember)
}

Expand All @@ -116,10 +123,10 @@ func (a *announceData) StartedEventHandler() (err error) {
// TODO(ian): This is what happened whenever the torrent client shuts down
// gracefully, so we need to call the mysql backend and store the info and
// remove the ipport from completed/incomplete redis kvs
func (a *announceData) StoppedEventHandler() {
func (a *AnnounceData) StoppedEventHandler() {

if a.infoHashExists() {
a.removeFromKVStorage(a.event)
a.removeFromKVStorage(a.Event)
} else {
return
}
Expand All @@ -128,37 +135,37 @@ func (a *announceData) StoppedEventHandler() {
// CompletedEventHandler Called upon announce when a client finishes a download. Removes the
// client from incomplete in redis and places their peer info into
// complete.
func (a *announceData) CompletedEventHandler() {
func (a *AnnounceData) CompletedEventHandler() {

if !a.infoHashExists() {
a.createInfoHashKey()
} else {
a.removeFromKVStorage("incomplete")
}

keymember := fmt.Sprintf("%s:complete", a.info_hash)
keymember := fmt.Sprintf("%s:complete", a.InfoHash)
// TODO(ian): DRY!
ipport := fmt.Sprintf("%s:%s", a.ip, a.port)
if RedisSetKeyIfNotExists(a.requestContext.redisClient, keymember, ipport) {
fmt.Printf("Adding host %s to %s:complete\n", ipport, a.info_hash)
ipport := fmt.Sprintf("%s:%s", a.IP, a.Port)
if r.RedisSetKeyIfNotExists(a.RequestContext.redisClient, keymember, ipport) {
fmt.Printf("Adding host %s to %s:complete\n", ipport, a.InfoHash)
}
}

func (a *announceData) removeFromKVStorage(subkey string) {
func (a *AnnounceData) removeFromKVStorage(subkey string) {
// Remove the subkey from the kv storage.
ipport := fmt.Sprintf("%s:%d", a.ip, a.port)
keymember := fmt.Sprintf("%s:%s", a.info_hash, subkey)
ipport := fmt.Sprintf("%s:%d", a.IP, a.Port)
keymember := fmt.Sprintf("%s:%s", a.InfoHash, subkey)

fmt.Printf("Removing host %s from %v\n", ipport, keymember)
RedisRemoveKeysValue(a.requestContext.redisClient, keymember, ipport)
r.RedisRemoveKeysValue(a.RequestContext.redisClient, keymember, ipport)
}

func (a *announceData) infoHashExists() bool {
return RedisGetBoolKeyVal(a.requestContext.redisClient, a.info_hash)
func (a *AnnounceData) infoHashExists() bool {
return r.RedisGetBoolKeyVal(a.InfoHash)
}

func (a *announceData) createInfoHashKey() {
CreateNewTorrentKey(a.requestContext.redisClient, a.info_hash)
func (a *AnnounceData) createInfoHashKey() {
r.CreateNewTorrentKey(a.InfoHash)
}

// ParseInfoHash parses the encoded info hash. Such a simple solution for a
Expand Down
2 changes: 1 addition & 1 deletion server/server_test.go → announce/announce_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package server
package announce

import (
"fmt"
Expand Down
46 changes: 46 additions & 0 deletions announce/definitions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package announce

import (
"github.com/jinzhu/gorm"
"gopkg.in/redis.v3"
)

const (
RATIOLESS = iota
SEMIRATIOLESS
NORMALRATIO
)

type AnnounceData struct {
InfoHash string //20 byte sha1 hash
PeerID string //max len 20
IP string //optional
Event string // TorrentEvent

Port uint64 // port number the peer is listening
// on

Uploaded uint64 // base10 ascii amount uploaded so far
Downloaded uint64 // base10 ascii amount downloaded so
// far

Left uint64 // # of bytes left to download
// (base 10 ascii)

Numwant uint64 // Number of peers requested by client.

Compact bool // Bep23 peer list compression
// decision: True -> compress bep23

RequestContext requestAppContext // The request-specific connections
}

// requestAppContext First of all naming things is the hardest part of
// programming real talk. Second of all, this essentially houses
// request-specific data like db connections and in the future the redisClient.
// Things that should persist only within the duration of a request.
type requestAppContext struct {
dbConn *gorm.DB
redisClient *redis.Client // The redis client connection handler to use.
Whitelist bool
}
54 changes: 30 additions & 24 deletions server/redis.go → kvStoreInterfaces/redis.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package server
package kvStoreInterface

import (
"gopkg.in/redis.v3"
"bytes"
"fmt"
"gopkg.in/redis.v3"
"strings"
"time"
)
Expand All @@ -24,16 +24,16 @@ func OpenClient() (client *redis.Client) {
}

// RedisSetIPMember sets a key as a member of an infohash and sets a timeout.
func RedisSetIPMember(data *announceData) (retval int) {
c := data.requestContext.redisClient
func RedisSetIPMember(infoHash, ipPort string) (retval int) {
c := OpenClient()

keymember := concatenateKeyMember(data.info_hash, "ip")
keymember := concatenateKeyMember(infoHash, "ip")

currTime := int64(time.Now().UTC().AddDate(0, 0, 1).Unix())

ipPort := fmt.Sprintf("%s:%v", createIPPortPair(data), currTime)
key := fmt.Sprintf("%s:%v", ipPort, currTime)

if err := c.SAdd(keymember, ipPort).Err(); err != nil {
if err := c.SAdd(keymember, key).Err(); err != nil {
retval = 0
panic("Failed to add key")

Expand Down Expand Up @@ -61,30 +61,34 @@ func RedisSetKeyVal(c *redis.Client, keymember string, value string) {
}

// RedisGetKeyVal Lookup a peer in the specified infohash at `key`
func RedisGetKeyVal(data *announceData, key string) []string {
func RedisGetKeyVal(key string) []string {
c := OpenClient()

// RedisGetKeyVal retrieves a value from the Redis store by looking up the
// provided key. If the key does not yet exist, we create the key in the KV
// provided key. If the key does not yet exist, we Create the key in the KV
// storage or if the value is empty, we add the current requester to the
// list.
keymember := concatenateKeyMember(key, "complete")

val, err := data.requestContext.redisClient.SMembers(keymember).Result()
val, err := c.SMembers(keymember).Result()
if err != nil {
// Fail because the key doesn't exist in the KV storage.
CreateNewTorrentKey(data.requestContext.redisClient, keymember)
CreateNewTorrentKey(keymember)
}

return val
}

// RedisGetAllPeers fetches all peers from the info_hash at `key`
func RedisGetAllPeers(data *announceData, key string) []string {
func RedisGetAllPeers(key string) []string {
c := OpenClient()

keymember := concatenateKeyMember(key, "complete")

val, err := data.requestContext.redisClient.SRandMemberN(keymember, 30).Result()
val, err := c.SRandMemberN(keymember, 30).Result()
if err != nil {
// Fail because the key doesn't exist in the KV storage.
CreateNewTorrentKey(data.requestContext.redisClient, keymember)
CreateNewTorrentKey(keymember)
}

if len(val) == 30 {
Expand All @@ -93,7 +97,7 @@ func RedisGetAllPeers(data *announceData, key string) []string {

keymember = concatenateKeyMember(key, "incomplete")

val2, err := data.requestContext.redisClient.SRandMemberN(keymember, int64(30-len(val))).Result()
val2, err := c.SRandMemberN(keymember, int64(30-len(val))).Result()
if err != nil {
panic("Failed to get incomplete peers for")
} else {
Expand All @@ -120,15 +124,16 @@ func RedisGetCount(c *redis.Client, info_hash string, member string) (retval int
}

// RedisGetBoolKeyVal Checks if a `key` exists
func RedisGetBoolKeyVal(client *redis.Client, key string) bool {
ret, _ := client.Exists(key).Result()
func RedisGetBoolKeyVal(key string) bool {
c := OpenClient()
ret, _ := c.Exists(key).Result()

return ret
}

// RedisSetKeyIfNotExists Set a key if it doesn't exist.
func RedisSetKeyIfNotExists(c *redis.Client, keymember string, value string) (rv bool) {
rv = RedisGetBoolKeyVal(c, keymember)
rv = RedisGetBoolKeyVal(keymember)
if !rv {
RedisSetKeyVal(c, keymember, value)
}
Expand All @@ -142,11 +147,12 @@ func RedisRemoveKeysValue(c *redis.Client, key string, value string) {
c.SRem(key, value)
}

// CreateNewTorrentKey creates a new key. By default, it adds a member
// CreateNewTorrentKey Creates a new key. By default, it adds a member
// ":ip". I don't think this ought to ever be generalized, as I just want
// Redis to function in one specific way in notorious.
func CreateNewTorrentKey(client *redis.Client, key string) {
client.SAdd(key, "complete", "incomplete")
func CreateNewTorrentKey(key string) {
c := OpenClient()
c.SAdd(key, "complete", "incomplete")

}

Expand All @@ -162,8 +168,8 @@ func concatenateKeyMember(key string, member string) string {
return buffer.String()
}

// createIPPortPair creates a string formatted ("%s:%s", value.ip,
// CreateIPPortPair Creates a string formatted ("%s:%s", value.ip,
// value.port) looking like so: "127.0.0.1:6886" and returns this value.
func createIPPortPair(value *announceData) string {
return fmt.Sprintf("%v:%v", value.ip, value.port)
func createIPPortPair(ip, port string) string {
return fmt.Sprintf("%v:%v", ip, port)
}
Loading