torckapi 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29d24fdd398ac1eb525d2f7e549b28758503aaf8
4
- data.tar.gz: 2166897e0f941f25da66e84c1c8a2fc8dd6ac9e0
3
+ metadata.gz: b2132ac0c587ca136fb4e0069018f02973d6d5e6
4
+ data.tar.gz: d72518b11c78f359da5857dfdc6491e96a636604
5
5
  SHA512:
6
- metadata.gz: bc75dd6ed40fe0d9ee30cad1edb8552fb143d089856e86f26a6266ac0d91de5a5b9dd08e81f28121329bd23d8e1362b7079acfad8f2bc91f17d5d38feac46487
7
- data.tar.gz: 0fb9ac7f55e5aafb757d566c57b452b74c90c49c645c3ec20904f43c9d7990cef164248a27b8e5f24d91004bade06dd7275c745fbfbfbba3f196ec7f85611fae
6
+ metadata.gz: cdd4f816b4e1526dc975cc8439a9e64cf17897fd1406343b5c202076bb724c38ca44e487d5a43aabe1f0158c68172b9b7df6b3caf52de1b07d6ad03b3dc7d099
7
+ data.tar.gz: 89779cb521dfa7e3497909e11c9713dcb7ef650d44a85e17cc318a4e820be1db8243b8960262d554757c0f3077a5734bb8fe3039cc23116683696e33d94f4ef3
data/Gemfile CHANGED
@@ -1 +1,3 @@
1
1
  source 'https://rubygems.org'
2
+
3
+ gem 'bencode'
data/Gemfile.lock CHANGED
@@ -1,8 +1,10 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ bencode (0.8.0)
4
5
 
5
6
  PLATFORMS
6
7
  ruby
7
8
 
8
9
  DEPENDENCIES
10
+ bencode
@@ -1,4 +1,5 @@
1
1
  require 'ipaddr'
2
+ require 'bencode'
2
3
 
3
4
  module Torckapi
4
5
  module Response
@@ -19,19 +20,34 @@ module Torckapi
19
20
  # @return [Torckapi::Response::Announce] response
20
21
  def self.from_udp info_hash, data
21
22
  leechers, seeders = data[0..7].unpack('L>2')
22
- peers = data[8..-1].unpack('a6' * (leechers + seeders)).map { |i| [IPAddr.ntop(i[0..3]), i[4..5].unpack('S>')[0]] }
23
-
24
- new info_hash, leechers, seeders, peers
23
+
24
+ new info_hash, leechers, seeders, peers_from_compact(data[8..-1], leechers + seeders)
25
+ end
26
+
27
+ # Construct response object from http response data
28
+ # @param info_hash [String] 40-char hexadecimal string
29
+ # @param data [String] HTTP response data (bencoded)
30
+ # @param compact [true, false] is peer data in compact format?
31
+ # @return [Torckapi::Response::Announce] response
32
+ def self.from_http info_hash, data, compact=true
33
+ bdecoded_data = BEncode.load(data)
34
+ leechers, seeders = bdecoded_data.values_at("incomplete", "complete")
35
+
36
+ new info_hash, leechers, seeders, peers_from_compact(bdecoded_data["peers"], [50, leechers + seeders].min)
25
37
  end
26
-
38
+
27
39
  private
28
-
40
+
29
41
  def initialize info_hash, leechers, seeders, peers
30
42
  @info_hash = info_hash
31
43
  @leechers = leechers
32
44
  @seeders = seeders
33
45
  @peers = peers
34
46
  end
47
+
48
+ def self.peers_from_compact data, peer_count
49
+ data.unpack('a6' * peer_count).map { |i| [IPAddr.ntop(i[0..3]), i[4..5].unpack('S>')[0]] }
50
+ end
35
51
  end
36
52
  end
37
53
  end
@@ -1,11 +1,11 @@
1
1
  module Torckapi
2
2
  module Response
3
-
3
+
4
4
  # Scrape response
5
5
  class Scrape
6
6
  # @return [Hash<String, Hash>] scrape data
7
7
  attr_reader :data
8
-
8
+
9
9
  # Construct response object from udp response data
10
10
  # @param info_hashes [Array<String>] list of 40-char hexadecimal strings
11
11
  # @param data [String] UDP response data (omit action and transaction_id)
@@ -13,9 +13,9 @@ module Torckapi
13
13
  def self.from_udp info_hashes, data
14
14
  new Hash[info_hashes.zip(data.unpack('a12' * (info_hashes.count)).map { |i| Hash[[:seeders, :completed, :leechers].zip i.unpack('L>3').map(&:to_i)] })]
15
15
  end
16
-
16
+
17
17
  private
18
-
18
+
19
19
  def initialize data
20
20
  @data = data
21
21
  end
@@ -1,6 +1,6 @@
1
1
  module Torckapi
2
2
  module Tracker
3
-
3
+
4
4
  # Public interface for torrent trackers
5
5
  class Base
6
6
  # Announce Request
@@ -16,9 +16,9 @@ module Torckapi
16
16
  def scrape info_hashes=[]
17
17
  raise Torckapi::InvalidInfohashError if [*info_hashes].any? { |i| i !~ /\A[0-9a-f]{40}\z/i }
18
18
  end
19
-
19
+
20
20
  private
21
-
21
+
22
22
  def initialize url, options={}
23
23
  @url = url
24
24
  @options = {timeout: 15, tries: 3}.merge(options)
@@ -1,10 +1,28 @@
1
+ require 'net/http'
1
2
  require_relative 'base'
2
3
 
3
4
  module Torckapi
4
5
  module Tracker
5
-
6
- # @todo implement
6
+
7
7
  class HTTP < Base
8
+
9
+ # (see Base#announce)
10
+ def announce info_hash
11
+ super info_hash
12
+
13
+ @url.query ||= ""
14
+ @url.query += "info_hash=%s&num_want=500" % URI.encode([info_hash].pack('H*'))
15
+
16
+ Torckapi::Response::Announce.from_http info_hash, Net::HTTP.get(@url)
17
+ end
18
+
19
+ def scrape info_hashes=[]
20
+ super info_hashes
21
+
22
+ raise NotImplementedError
23
+ end
24
+
25
+ private
8
26
  end
9
27
  end
10
28
  end
@@ -5,53 +5,47 @@ require_relative 'base'
5
5
 
6
6
  module Torckapi
7
7
  module Tracker
8
-
8
+
9
9
  # Implementation of http://www.bittorrent.org/beps/bep_0015.html
10
10
  class UDP < Base
11
- # connection_id is valid for 1 minute as per protocol
12
11
  CONNECTION_TIMEOUT = 60
13
-
12
+
14
13
  # @see Base#announce
15
14
  def announce info_hash
16
15
  super info_hash
17
-
18
- data = [[info_hash].pack('H*'), SecureRandom.random_bytes(20), [0, 0, 0, 0, 0, 0, -1, 0].pack('Q>3L>4s>')].join
16
+
17
+ data = [[info_hash].pack('H*'), SecureRandom.random_bytes(20), [0, 0, 0, 0, 0, 0, -1, 0].pack('Q>3L>4S>')].join
19
18
 
20
19
  connect
21
20
  response_body = communicate(1, data)[0] # announce
22
21
 
23
22
  Torckapi::Response::Announce.from_udp info_hash, response_body[12..-1]
24
23
  end
25
-
24
+
26
25
  def scrape info_hashes=[]
27
26
  super info_hashes
28
27
 
29
28
  data = [*info_hashes].map { |i| [i].pack('H*') }.join
30
-
29
+
31
30
  connect
32
31
  response_body = communicate(2, data)[0] # scrape
33
-
32
+
34
33
  Torckapi::Response::Scrape.from_udp [*info_hashes], response_body[8..-1]
35
34
  end
36
35
 
37
36
  private
38
37
 
39
- def initialize *args
40
- super *args
41
- end
42
-
43
38
  def connect
44
39
  return if @connection_id && @communicated_at.to_i >= Time.now.to_i - CONNECTION_TIMEOUT
45
-
40
+
46
41
  @connection_id = [0x41727101980].pack('Q>')
47
42
  response_body = communicate(0)[0] # connect
48
43
  @connection_id = response_body[8..15]
49
- puts "connection_id = #{@connection_id.inspect}" if Torckapi::debug
50
44
  end
51
-
45
+
52
46
  def communicate action, data=nil
53
47
  @socket ||= UDPSocket.new
54
-
48
+
55
49
  transaction_id = SecureRandom.random_bytes(4)
56
50
  packet = [@connection_id, [action].pack('L>'), transaction_id, data].join
57
51
 
@@ -59,17 +53,15 @@ module Torckapi
59
53
  response = nil
60
54
  begin
61
55
  Timeout::timeout(@options[:timeout], CommunicationTimeoutError) do
62
- puts "--> #{packet.inspect}" if Torckapi::debug
63
56
  @socket.send(packet, 0, @url.host, @url.port)
64
57
  response = @socket.recvfrom(65536)
65
- puts "<-- #{response.inspect}" if Torckapi::debug
66
58
  raise TransactionIdMismatchError if transaction_id != response[0][4..7]
67
59
  @communicated_at = Time.now
68
60
  end
69
61
  rescue CommunicationTimeoutError
70
- retry if (tries += 1) <= @options[:tries]
62
+ retry if (tries += 1) <= @options[:tries]
71
63
  end
72
-
64
+
73
65
  response
74
66
  end
75
67
  end
@@ -1,3 +1,3 @@
1
1
  module Torckapi
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/torckapi.rb CHANGED
@@ -7,16 +7,6 @@ require 'torckapi/response/announce'
7
7
  require 'torckapi/response/scrape'
8
8
 
9
9
  module Torckapi
10
- @@debug = false
11
-
12
- def self.debug
13
- @@debug
14
- end
15
-
16
- def self.debug= debug
17
- @@debug = !!debug
18
- end
19
-
20
10
  # Creates a tracker interface instance
21
11
  # @param tracker_url [String] tracker announce url
22
12
  # @param options [Hash] defaults to \\{timeout: 15, tries: 3}
data/torckapi.gemspec CHANGED
@@ -16,5 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Torckapi::VERSION
17
17
 
18
18
  gem.add_development_dependency 'rake'
19
- gem.add_development_dependency 'minitest'
19
+ gem.add_development_dependency 'minitest'
20
+
21
+ gem.add_runtime_dependency 'bencode'
20
22
  end
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.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dennis Krupenik
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bencode
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: torrent tracker api
42
56
  email:
43
57
  - dennis@krupenik.com