class Torckapi::Tracker::UDP

Implementation of www.bittorrent.org/beps/bep_0015.html

Constants

CONNECTION_TIMEOUT
REQUEST_ACTIONS
RESPONSE_CLASSES
RESPONSE_CODES
RESPONSE_MIN_LENGTHS

Public Class Methods

new(url, options={}) click to toggle source
Calls superclass method Torckapi::Tracker::Base::new
# File lib/torckapi/tracker/udp.rb, line 28
def initialize url, options={}
  super
  @state = nil
  @connection_id = nil
  @communicated_at = 0
end

Public Instance Methods

announce(info_hash, peer_id=SecureRandom.random_bytes(20)) click to toggle source
Calls superclass method Torckapi::Tracker::Base#announce
# File lib/torckapi/tracker/udp.rb, line 10
def announce info_hash, peer_id=SecureRandom.random_bytes(20)
  super
  perform_request Announce, announce_request_data(info_hash, peer_id), info_hash
end
scrape(info_hashes=[]) click to toggle source
Calls superclass method Torckapi::Tracker::Base#scrape
# File lib/torckapi/tracker/udp.rb, line 15
def scrape info_hashes=[]
  super
  perform_request Scrape, scrape_request_data(info_hashes), info_hashes
end

Private Instance Methods

announce_request_data(info_hash, peer_id) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 49
def announce_request_data info_hash, peer_id
  [[info_hash].pack('H*'), peer_id, [0, 0, 0, 0, 0, 0, -1, 0].pack('Q>3L>4S>')].join
end
check_response_length(response, response_code) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 113
def check_response_length response, response_code
  raise MalformedResponseError, response if RESPONSE_MIN_LENGTHS[response_code] > response.length
end
check_transaction_id(response, transaction_id) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 99
def check_transaction_id response, transaction_id
  raise TransactionIdMismatchError, response if transaction_id != response[4..7]
end
communicate(action, data=nil) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 65
def communicate action, data=nil
  @socket ||= UDPSocket.new

  tries = 0
  response = nil

  begin
    timeout = @options[:timeout] * (2 ** tries)
    connect
    transaction_id = SecureRandom.random_bytes(4)
    packet = [@connection_id, [action].pack('L>'), transaction_id, data].join

    Timeout::timeout(timeout, CommunicationTimeoutError) do
      @socket.send(packet, 0, @url.host, @url.port)
      response = process_response @socket.recvfrom(65536)[0], transaction_id
      @communicated_at = Time.now
    end
  rescue CommunicationTimeoutError, LittleEndianResponseError => e
    retry if (tries += 1) <= @options[:tries]
  end

  raise CommunicationFailedError unless response

  response
end
connect() click to toggle source
# File lib/torckapi/tracker/udp.rb, line 57
def connect
  return if connected? || connecting?

  @state, @connection_id = :connecting, [0x041727101980].pack('Q>')
  response = communicate Connect
  @state, @connection_id = nil, response[:data]
end
connected?() click to toggle source
# File lib/torckapi/tracker/udp.rb, line 35
def connected?
  @connection_id && @communicated_at.to_i >= Time.now.to_i - CONNECTION_TIMEOUT
end
connecting?() click to toggle source
# File lib/torckapi/tracker/udp.rb, line 39
def connecting?
  @state == :connecting
end
extract_response_code(response) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 103
def extract_response_code response
  response_code, response_code_le = [response[0..3]].flat_map { |x| [x.unpack('L>')[0], x.unpack('L<')[0]] }

  unless RESPONSE_CODES.include?(response_code)
    raise (RESPONSE_CODES.include?(response_code_le) ? LittleEndianResponseError : MalformedResponseError), response
  end

  response_code
end
perform_request(action, data, *args) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 43
def perform_request action, data, *args
  response = communicate action, data

  RESPONSE_CLASSES[response[:code]].from_udp(*args, response[:data])
end
process_response(response, transaction_id) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 91
def process_response response, transaction_id
  check_transaction_id response, transaction_id
  response_code = extract_response_code response
  check_response_length response, response_code

  {code: response_code, data: response[8..-1]}
end
scrape_request_data(info_hashes) click to toggle source
# File lib/torckapi/tracker/udp.rb, line 53
def scrape_request_data info_hashes
  info_hashes.map { |i| [i].pack('H*') }.join
end