torckapi 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -2
- data/lib/torckapi/errors.rb +1 -1
- data/lib/torckapi/response/announce.rb +8 -7
- data/lib/torckapi/response/base.rb +21 -0
- data/lib/torckapi/response/error.rb +4 -1
- data/lib/torckapi/response/scrape.rb +14 -11
- data/lib/torckapi/tracker/base.rb +10 -3
- data/lib/torckapi/tracker/http.rb +2 -2
- data/lib/torckapi/tracker/udp.rb +5 -8
- data/lib/torckapi/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23d4a93deca3957dc10d4e056c505b5f57d92bd2
|
4
|
+
data.tar.gz: d4b1fe9fcb70d7ffa604209da48a3018702108dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2994677c358450c2a6e9a4d3a8ef2ef559e04b30963c37ae2b4ae96a793edef7c78d5abf653b61b3708e9db64c8ff8322bf0752600e3eb48bd1535eee0763851
|
7
|
+
data.tar.gz: 31f3e8806ab54efc32fa48db835f0b7ff1df1d7c64b67aa0908961121c44dddaee734dc022f4ea20d813d7dcef5e6b770fbb963a7506f6015c64c9755a00817b
|
data/README.md
CHANGED
data/lib/torckapi/errors.rb
CHANGED
@@ -9,7 +9,7 @@ module Torckapi
|
|
9
9
|
class CommunicationError < Error; end
|
10
10
|
class CommunicationFailedError < CommunicationError; end
|
11
11
|
class CommunicationTimeoutError < CommunicationError; end
|
12
|
-
class MalformedResponseError <
|
12
|
+
class MalformedResponseError < Error; end
|
13
13
|
class TransactionIdMismatchError < CommunicationError; end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'ipaddr'
|
2
|
-
require '
|
2
|
+
require 'torckapi/response/base'
|
3
3
|
|
4
4
|
module Torckapi
|
5
5
|
module Response
|
6
|
-
|
6
|
+
# Announce response
|
7
|
+
class Announce < Base
|
7
8
|
# @!attribute [r] info_hash
|
8
9
|
# @return [String] 40-char hexadecimal string
|
9
10
|
# @!attribute [r] leechers
|
@@ -12,6 +13,7 @@ module Torckapi
|
|
12
13
|
# @return [Array<IPAddr, Fixnum>] list of peers
|
13
14
|
# @!attribute [r] seeders
|
14
15
|
# @return [Fixnum] number of seeders
|
16
|
+
|
15
17
|
attr_reader :info_hash, :leechers, :peers, :seeders
|
16
18
|
|
17
19
|
# Construct response object from udp response data
|
@@ -25,12 +27,11 @@ module Torckapi
|
|
25
27
|
# Construct response object from http response data
|
26
28
|
# @param info_hash [String] 40-char hexadecimal string
|
27
29
|
# @param data [String] HTTP response data (bencoded)
|
28
|
-
# @param compact [true, false] is peer data in compact format?
|
29
30
|
# @return [Torckapi::Response::Announce] response
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
new info_hash, *bdecoded_data.values_at(
|
31
|
+
# @raise [Torckapi::Tracker::MalformedResponseError]
|
32
|
+
def self.from_http info_hash, data
|
33
|
+
bdecoded_data = bdecode_and_check data, 'peers'
|
34
|
+
new info_hash, *bdecoded_data.values_at('incomplete', 'complete'), peers_from_compact(bdecoded_data['peers'])
|
34
35
|
end
|
35
36
|
|
36
37
|
private
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bencode'
|
2
|
+
|
3
|
+
module Torckapi
|
4
|
+
module Response
|
5
|
+
class Base
|
6
|
+
protected
|
7
|
+
|
8
|
+
def self.bdecode_and_check data, key
|
9
|
+
begin
|
10
|
+
bdecoded_data = BEncode.load(data)
|
11
|
+
rescue BEncode::DecodeError
|
12
|
+
raise Torckapi::Tracker::MalformedResponseError
|
13
|
+
end
|
14
|
+
|
15
|
+
raise Torckapi::Tracker::MalformedResponseError unless bdecoded_data.is_a? Hash and bdecoded_data.has_key? key
|
16
|
+
|
17
|
+
bdecoded_data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'torckapi/response/base'
|
2
2
|
|
3
3
|
module Torckapi
|
4
4
|
module Response
|
5
|
-
|
6
5
|
# Scrape response
|
7
|
-
class Scrape
|
6
|
+
class Scrape < Base
|
8
7
|
# @return [Hash<String, Hash>] scrape data
|
9
8
|
attr_reader :data
|
10
9
|
|
@@ -12,29 +11,33 @@ module Torckapi
|
|
12
11
|
# @param info_hashes [Array<String>] list of 40-char hexadecimal strings
|
13
12
|
# @param data [String] UDP response data (omit action and transaction_id)
|
14
13
|
# @return [Torckapi::Response::Scrape] response
|
14
|
+
# @raise [Torckapi::Tracker::MalformedResponseError]
|
15
15
|
def self.from_udp info_hashes, data
|
16
|
-
raise Torckapi::MalformedResponseError if data.length != info_hashes.count * 12
|
16
|
+
raise Torckapi::Tracker::MalformedResponseError if data.length != info_hashes.count * 12
|
17
17
|
new Hash[info_hashes.zip(data.unpack('a12' * info_hashes.count).map { |i| counts_unpacked(i) })]
|
18
18
|
end
|
19
19
|
|
20
|
+
# Construct response object from http response data
|
21
|
+
# @param data [String] HTTP response data (bencoded)
|
22
|
+
# @return [Torckapi::Response::Scrape] response
|
23
|
+
# @raise [Torckapi::Tracker::MalformedResponseError]
|
20
24
|
def self.from_http data
|
21
|
-
bdecoded_data =
|
22
|
-
|
23
|
-
new Hash[bdecoded_data["files"].map { |info_hash, counts| [info_hash.unpack('H*').join, counts_translated(counts) ]}]
|
25
|
+
bdecoded_data = bdecode_and_check data, 'files'
|
26
|
+
new Hash[bdecoded_data['files'].map { |info_hash, counts| [info_hash.unpack('H*').join, counts_translated(counts) ]}]
|
24
27
|
end
|
25
28
|
|
26
29
|
private
|
27
30
|
|
28
31
|
def self.counts_unpacked data
|
29
|
-
counts_with_block(data
|
32
|
+
counts_with_block(data) { |data| data.unpack('L>3').map(&:to_i) }
|
30
33
|
end
|
31
34
|
|
32
35
|
def self.counts_translated data
|
33
|
-
counts_with_block(data
|
36
|
+
counts_with_block(data) { |data| data.values_at('complete', 'downloaded', 'incomplete') }
|
34
37
|
end
|
35
38
|
|
36
|
-
def self.counts_with_block data, block
|
37
|
-
Hash[[:seeders, :completed, :leechers].zip(
|
39
|
+
def self.counts_with_block data, &block
|
40
|
+
Hash[[:seeders, :completed, :leechers].zip(yield data)]
|
38
41
|
end
|
39
42
|
|
40
43
|
def initialize data
|
@@ -3,16 +3,23 @@ module Torckapi
|
|
3
3
|
|
4
4
|
# Public interface for torrent trackers
|
5
5
|
class Base
|
6
|
-
# Announce
|
6
|
+
# Announce request
|
7
7
|
# @param info_hash [String] 40-char hexadecimal string
|
8
|
+
# @param peer_id [String] 20-byte binary string
|
8
9
|
# @return [Torckapi::Response::Announce] a response object
|
9
|
-
|
10
|
+
# @raise [Torckapi::InvalidInfohashError] when supplied with invalid info_hash
|
11
|
+
# @raise [Torckapi::Tracker::CommunicationFailedError] when tracker haven't responded at all
|
12
|
+
# @raise [Torckapi::Tracker::MalformedResponseError] when tracker returned junk
|
13
|
+
def announce info_hash, peer_id=SecureRandom.random_bytes(20)
|
10
14
|
raise Torckapi::InvalidInfohashError if info_hash !~ /\A[0-9a-f]{40}\z/i
|
11
15
|
end
|
12
16
|
|
13
|
-
# Scrape
|
17
|
+
# Scrape request
|
14
18
|
# @param info_hashes [Array<String>] An array of 40-char hexadecimal strings
|
15
19
|
# @return [Torckapi::Response::Scrape] a response object
|
20
|
+
# @raise [Torckapi::InvalidInfohashError] when supplied with invalid info_hash
|
21
|
+
# @raise [Torckapi::Tracker::CommunicationFailedError] when tracker haven't responded at all
|
22
|
+
# @raise [Torckapi::Tracker::MalformedResponseError] when tracker returned junk
|
16
23
|
def scrape info_hashes=[]
|
17
24
|
raise Torckapi::InvalidInfohashError if info_hashes.any? { |i| i !~ /\A[0-9a-f]{40}\z/i }
|
18
25
|
end
|
@@ -5,8 +5,6 @@ module Torckapi
|
|
5
5
|
module Tracker
|
6
6
|
|
7
7
|
class HTTP < Base
|
8
|
-
REQUEST_ACTIONS = [Announce = 1, Scrape = 2].freeze
|
9
|
-
|
10
8
|
# (see Base#announce)
|
11
9
|
def announce info_hash
|
12
10
|
super info_hash
|
@@ -21,6 +19,8 @@ module Torckapi
|
|
21
19
|
|
22
20
|
private
|
23
21
|
|
22
|
+
REQUEST_ACTIONS = [Announce = 1, Scrape = 2].freeze
|
23
|
+
|
24
24
|
def initialize url, options={}
|
25
25
|
super url, options
|
26
26
|
@url.query ||= ""
|
data/lib/torckapi/tracker/udp.rb
CHANGED
@@ -5,21 +5,13 @@ require 'torckapi/tracker/base'
|
|
5
5
|
|
6
6
|
module Torckapi
|
7
7
|
module Tracker
|
8
|
-
|
9
8
|
# Implementation of http://www.bittorrent.org/beps/bep_0015.html
|
10
9
|
class UDP < Base
|
11
|
-
CONNECTION_TIMEOUT = 60
|
12
|
-
REQUEST_ACTIONS = [Connect = 0, Announce = 1, Scrape = 2].freeze
|
13
|
-
RESPONSE_CLASSES = [nil, Torckapi::Response::Announce, Torckapi::Response::Scrape, Torckapi::Response::Error].freeze
|
14
|
-
RESPONSE_MIN_LENGTHS = [16, 20, 8, 8].freeze
|
15
|
-
|
16
|
-
# (see Base#announce)
|
17
10
|
def announce info_hash, peer_id=SecureRandom.random_bytes(20)
|
18
11
|
super info_hash
|
19
12
|
perform_request Announce, announce_request_data(info_hash, peer_id), info_hash
|
20
13
|
end
|
21
14
|
|
22
|
-
# (see Base#scrape)
|
23
15
|
def scrape info_hashes=[]
|
24
16
|
super info_hashes
|
25
17
|
perform_request Scrape, scrape_request_data(info_hashes), info_hashes
|
@@ -27,6 +19,11 @@ module Torckapi
|
|
27
19
|
|
28
20
|
private
|
29
21
|
|
22
|
+
CONNECTION_TIMEOUT = 60
|
23
|
+
REQUEST_ACTIONS = [Connect = 0, Announce = 1, Scrape = 2].freeze
|
24
|
+
RESPONSE_CLASSES = [nil, Torckapi::Response::Announce, Torckapi::Response::Scrape, Torckapi::Response::Error].freeze
|
25
|
+
RESPONSE_MIN_LENGTHS = [16, 20, 8, 8].freeze
|
26
|
+
|
30
27
|
def perform_request action, data, *args
|
31
28
|
connect
|
32
29
|
response = communicate action, data
|
data/lib/torckapi/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: torckapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dennis Krupenik
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- lib/torckapi.rb
|
67
67
|
- lib/torckapi/errors.rb
|
68
68
|
- lib/torckapi/response/announce.rb
|
69
|
+
- lib/torckapi/response/base.rb
|
69
70
|
- lib/torckapi/response/error.rb
|
70
71
|
- lib/torckapi/response/scrape.rb
|
71
72
|
- lib/torckapi/tracker/base.rb
|