torrenter 0.0.6 → 0.0.7

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.
@@ -0,0 +1,41 @@
1
+ module Torrenter
2
+ class Tracker
3
+
4
+ attr_reader :response, :address
5
+
6
+ class HTTPTracker < Tracker
7
+ attr_reader :response
8
+ def initialize(url, params)
9
+ @address = URI(url)
10
+ @params = params
11
+ end
12
+
13
+ def connect
14
+ @address.query = URI.encode_www_form(@params)
15
+ begin
16
+ @response = BEncode.load(Net::HTTP.get(@address))
17
+ rescue Exception => e
18
+ false
19
+ end
20
+
21
+ return self
22
+ end
23
+
24
+ def peer_list
25
+ format_peers(peers)
26
+ end
27
+
28
+ def connect_interval
29
+ @response['min interval']
30
+ end
31
+
32
+ def peers
33
+ @response['peers']
34
+ end
35
+
36
+ def connected?
37
+ @response
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,107 @@
1
+ module Torrenter
2
+ class Tracker
3
+ class UDPTracker < Tracker
4
+
5
+ attr_reader :socket, :response, :interval
6
+
7
+ def initialize(tracker, params)
8
+ @url = tracker[/(?<=udp\:\/\/).+(?=\:\d+)/]
9
+ @port = tracker[/\d+$/].to_i
10
+ @socket = UDPSocket.new
11
+ @connection_id = [0x41727101980].pack("Q>")
12
+ @params = params
13
+ end
14
+
15
+ def interval?
16
+ interval % Time.now.to_i == 0
17
+ end
18
+
19
+ def segment(e, enc="I>")
20
+ [e].pack(enc)
21
+ end
22
+
23
+ def connect
24
+ @transaction_id = segment(rand(10000), "I>")
25
+ @socket.connect(ip_addr, @port)
26
+ begin
27
+ send_msg(connect_msg)
28
+ read_response
29
+ bound_peers
30
+ rescue
31
+ false
32
+ end
33
+ return self
34
+ end
35
+
36
+ def send_msg(msg)
37
+ begin
38
+ @socket.send(msg, 0)
39
+ rescue
40
+ false
41
+ end
42
+ end
43
+
44
+ def connected?
45
+ @response
46
+ end
47
+
48
+ def ip_addr
49
+ Socket.getaddrinfo(@url, @port)[0][3]
50
+ end
51
+
52
+ def bound_peers
53
+ @connection_id = @response[-8..-1]
54
+ @transaction_id = [rand(10000)].pack("I>")
55
+ send_msg(announce_msg)
56
+
57
+ read_response
58
+
59
+ parse_announce if @response[0..3] == segment(1)
60
+ end
61
+
62
+ def peer_list
63
+ format_peers(@response)
64
+ end
65
+
66
+ def parse_announce
67
+ if @response[4..7] == @transaction_id
68
+ @interval = @response[8..11].unpack("I>").first
69
+ res = @response.slice!(0..11)
70
+ @leechers = @response.slice!(0..3).unpack("I>").first
71
+ @seeders = @response.slice!(0..3).unpack("I>").first
72
+ end
73
+ end
74
+
75
+ def send_message
76
+ begin
77
+ @socket.send(@msg, 0)
78
+ rescue *EXCEPTIONS
79
+ end
80
+ end
81
+
82
+ def read_response
83
+ begin
84
+ @response = @socket.recv(1028)
85
+ rescue Exception => e
86
+ e
87
+ end
88
+ end
89
+
90
+ def connect_match?
91
+ data[0] == (segment(0) + @transaction_id + @connection_id)
92
+ end
93
+
94
+ def announce_input
95
+ @connection_id + segment(1) + @transaction_id + @params[:info_hash] + PEER_ID
96
+ end
97
+
98
+ def connect_msg
99
+ @connection_id + segment(0) + @transaction_id
100
+ end
101
+
102
+ def announce_msg
103
+ announce_input + (segment(0, "Q>") * 3) + (segment(0) * 3) + segment(-1) + segment(@socket.addr[1], ">S")
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,3 +1,3 @@
1
1
  module Torrenter
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torrenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - wismer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-26 00:00:00.000000000 Z
11
+ date: 2014-07-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: BitTorrent Client written in Ruby
14
14
  email:
@@ -20,19 +20,17 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - bin/torrenter
22
22
  - lib/torrenter.rb
23
- - lib/torrenter/file_creator.rb
24
- - lib/torrenter/http_tracker.rb
25
- - lib/torrenter/message/message_types.rb
26
- - lib/torrenter/message/messager.rb
23
+ - lib/torrenter/constants.rb
27
24
  - lib/torrenter/peer.rb
28
- - lib/torrenter/peer_state.rb
25
+ - lib/torrenter/peer/buffer_state.rb
29
26
  - lib/torrenter/reactor.rb
30
- - lib/torrenter/setup.rb
31
- - lib/torrenter/test_msg.rb
32
- - lib/torrenter/test_reactor.rb
33
27
  - lib/torrenter/torrent_reader.rb
28
+ - lib/torrenter/torrent_reader/piece.rb
29
+ - lib/torrenter/torrent_reader/piece_constructor.rb
30
+ - lib/torrenter/torrent_reader/piece_index.rb
34
31
  - lib/torrenter/tracker.rb
35
- - lib/torrenter/udp_tracker.rb
32
+ - lib/torrenter/tracker/http_tracker.rb
33
+ - lib/torrenter/tracker/udp_tracker.rb
36
34
  - lib/torrenter/version.rb
37
35
  homepage: http://wismer.github.io
38
36
  licenses:
File without changes
@@ -1,44 +0,0 @@
1
- module Torrenter
2
- class HTTPTracker < TorrentReader
3
- attr_reader :response
4
- def initialize(tracker, stream)
5
- super(stream)
6
- @address = URI(tracker)
7
- end
8
-
9
- def connect
10
- @address.query = URI.encode_www_form(tracker_params)
11
- @response =
12
- begin
13
- BEncode.load(Net::HTTP.get(@address))
14
- rescue
15
- false
16
- end
17
- end
18
-
19
- def connect_interval
20
- @response['min_interval']
21
- end
22
-
23
- def address_hashes
24
- @response['peers']
25
- end
26
-
27
- def tracker_params
28
- {
29
- :info_hash => info_hash,
30
- :peer_id => PEER_ID,
31
- :left => piece_length,
32
- :pieces => file_list
33
- }
34
- end
35
-
36
- def bound_peers
37
- peer_list(@response['peers'])
38
- end
39
-
40
- def connected?
41
- @response
42
- end
43
- end
44
- end
@@ -1,164 +0,0 @@
1
- module Torrenter
2
- # KEEP_ALIVE = "\x00\x00\x00\x00"
3
- # BLOCK = 2**14
4
- # these methods get mixed in with the Peer class as a way to help
5
- # organize and parse the byte-encoded data. The intention is to shorten
6
- # and shrink the complexity of the Peer class.
7
- # the following methods are responsible solely for data retrieval and data transmission
8
-
9
- def state(master_index, remaining, &block)
10
- @remaining = remaining
11
-
12
- recv_data if @buffer.size <= 3
13
- if @buffer[4].nil?
14
- @buffer.slice!(0..3) if @buffer[0..3] == KEEP_ALIVE
15
- else
16
- @length = @buffer[0..3].unpack("N*").last
17
- @buffer_id = @buffer[4]
18
- case @buffer_id
19
- when BITFIELD
20
- process_bitfield(master_index.size, clear_msg)
21
- when HAVE
22
- process_have(clear_msg)
23
- when INTERESTED
24
- @buffer = ''
25
-
26
- return :interested
27
- when PIECE
28
- process_piece { block.call @blocks.join(''), @index }
29
- when CHOKE
30
- choke_message
31
- when HANDSHAKE
32
- process_handshake
33
- end
34
- end
35
- end
36
-
37
- def completed?
38
- piece_data.bytesize == @piece_length || piece_data.bytesize == @remaining
39
- end
40
-
41
- def piece_data
42
- @blocks.join('')
43
- end
44
-
45
- def choke_message
46
- @peer_state = false
47
- end
48
-
49
- def clear_msg
50
- @buffer.slice!(0..@length + 3)
51
- end
52
-
53
- def request_message(bytes=BLOCK)
54
- @msg = "index: #{@index} offset: #{@blocks.size * BLOCK} bytes: #{bytes}"
55
- send_data(pack(13) + "\x06" + pack(@index) + pack(@blocks.size * BLOCK) + pack(bytes))
56
- end
57
-
58
- def pack(msg)
59
- [msg].pack("I>")
60
- end
61
-
62
- def process_bitfield(size, msg)
63
- index = msg[5..-1].unpack("B#{size}").join.split('')
64
- @piece_index = index.map { |bit| bit == '1' ? :free : :unavailable }
65
- send_interested if @buffer.empty?
66
- end
67
-
68
- def process_have(msg)
69
- @piece_index[msg[5..-1].unpack("C*").last] = :free
70
- send_interested if @buffer.empty?
71
- end
72
-
73
- def request_piece(index)
74
- @index = index
75
- @piece_index[@index] = :downloading
76
-
77
- request_message
78
- end
79
-
80
- def send_interested
81
- send_data("\x00\x00\x00\x01\x02")
82
- end
83
-
84
- def buffer_length?
85
- @buffer.bytesize >= @length + 4
86
- end
87
-
88
- def process_piece(&block)
89
- binding.pry if @length < BLOCK
90
- if buffer_length?
91
- if buffer_complete?
92
- pack_buffer
93
- end
94
-
95
- if @blocks.join('').bytesize < @piece_length
96
- diff = @remaining - @blocks.join('').bytesize
97
- if diff < BLOCK
98
- request_message(diff)
99
- else
100
- request_message
101
- end
102
- end
103
-
104
- block.call if completed?
105
- end
106
- recv_data
107
- end
108
-
109
- def mark_complete
110
- @blocks = []
111
- @piece_index[@index] = :downloaded
112
- end
113
-
114
- def pack_buffer
115
- @blocks << @buffer.slice!(13..-1)
116
- @buffer.clear
117
- end
118
-
119
- def buffer_complete?
120
- (@buffer.bytesize - 13) == BLOCK || (@buffer.bytesize - 13) == @length - 9
121
- end
122
-
123
- # will need a last piece sort of thing inserted in here
124
-
125
- def process_handshake
126
- if hash_check?
127
- @buffer.slice!(0..67)
128
- else
129
- @peer_state = false
130
- end
131
- end
132
-
133
- def hash_check?
134
- @buffer[28..47] == @info_hash
135
- end
136
-
137
- def send_data(msg)
138
- begin
139
- Timeout::timeout(2) { @socket.sendmsg_nonblock(msg) }
140
- rescue Timeout::Error
141
- ''
142
- rescue IO::EAGAINWaitReadable
143
- ''
144
- rescue *EXCEPTIONS
145
- ''
146
- rescue Errno::ETIMEDOUT
147
- @peer_state = false
148
- end
149
- end
150
-
151
- def recv_data(bytes=BLOCK)
152
- begin
153
- Timeout::timeout(5) { @buffer << @socket.recv_nonblock(bytes) }
154
- rescue Timeout::Error
155
- ''
156
- rescue IO::EAGAINWaitReadable
157
- ''
158
- rescue *EXCEPTIONS
159
- ''
160
- rescue Errno::ETIMEDOUT
161
- @peer_state = false
162
- end
163
- end
164
- end
@@ -1,57 +0,0 @@
1
- module Torrenter
2
- # The buffer state should be responsible ONLY for the handling of non-metadata bytes.
3
- # the messaging behavior should and ought to remain with the peer class, though
4
- # in truth, it may be better if I didnt do that.
5
-
6
- # So, if the state of the buffer reaches a certain step in its process involving the piece
7
- # the buffer state should be fired off.
8
-
9
- # instead of initialiazing it with the buffer,
10
-
11
- class BufferState
12
-
13
- def intialize(socket)
14
- @socket = socket
15
- @buffer = ''
16
- end
17
-
18
- # include Torrenter
19
-
20
- def state(buffer, master_index)
21
- buffer_id = buffer[4]
22
- if buffer_id.nil?
23
- buffer.slice!(0..3)
24
- else
25
- @length = buffer[0..3].unpack("N>")
26
- case buffer_id
27
- when BITFIELD then process_bitfield
28
- when HAVE then process_have
29
- when INTERESTED then process_interested
30
- when PIECE then process_piece
31
- when CHOKE then choke_message
32
- when HANDSHAKE then process_handshake
33
- end
34
- end
35
- end
36
-
37
- def process_bitfield
38
-
39
- end
40
-
41
- def process_have
42
-
43
- end
44
-
45
- def process_piece
46
-
47
- end
48
-
49
- def process_handshake
50
-
51
- end
52
-
53
- def recv_data
54
-
55
- end
56
- end
57
- end