Created TorrentPeerCommunicator class and implemented all basic send methods for the Peer Wire Protocol

This commit is contained in:
Ben Davis
2017-07-08 17:41:02 +02:00
parent 92119b984e
commit eea97fc27d
12 changed files with 540 additions and 22 deletions
+23 -3
View File
@@ -17,6 +17,11 @@
B51638971F0EEC2B009E563E /* GCDAsyncSocketStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51638961F0EEC2B009E563E /* GCDAsyncSocketStub.swift */; };
B51D6C091F0C180600E1E3AB /* TorrentUDPTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51D6C031F0C17AE00E1E3AB /* TorrentUDPTrackerTests.swift */; };
B51D6C0A1F0C180D00E1E3AB /* TorrentUDPTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51D6C061F0C17C000E1E3AB /* TorrentUDPTracker.swift */; };
B53553361F0FEB8800A6DCBE /* TorrentPeerCommunicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53553351F0FEB8800A6DCBE /* TorrentPeerCommunicator.swift */; };
B535533A1F0FEB9700A6DCBE /* TorrentPeerCommunicatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53553381F0FEB9300A6DCBE /* TorrentPeerCommunicatorTests.swift */; };
B535533C1F0FEE6300A6DCBE /* TCPConnectionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B535533B1F0FEE6300A6DCBE /* TCPConnectionStub.swift */; };
B535533E1F11261200A6DCBE /* BitField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B535533D1F11261200A6DCBE /* BitField.swift */; };
B53553401F112BDB00A6DCBE /* BitFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B535533F1F112BDB00A6DCBE /* BitFieldTests.swift */; };
B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = B537CF051F03148B0084089B /* HTTPConnectionStub.swift */; };
B537CF461F031AD20084089B /* TestText.torrent in Resources */ = {isa = PBXBuildFile; fileRef = B5E9B0E31F02F9E700EF58E3 /* TestText.torrent */; };
B537CF491F031C3D0084089B /* BEncode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B54D0C1A1CA53983004343BD /* BEncode.framework */; };
@@ -130,10 +135,15 @@
B51638961F0EEC2B009E563E /* GCDAsyncSocketStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCDAsyncSocketStub.swift; sourceTree = "<group>"; };
B51D6C031F0C17AE00E1E3AB /* TorrentUDPTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorrentUDPTrackerTests.swift; sourceTree = "<group>"; };
B51D6C061F0C17C000E1E3AB /* TorrentUDPTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorrentUDPTracker.swift; sourceTree = "<group>"; };
B53553351F0FEB8800A6DCBE /* TorrentPeerCommunicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorrentPeerCommunicator.swift; sourceTree = "<group>"; };
B53553381F0FEB9300A6DCBE /* TorrentPeerCommunicatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TorrentPeerCommunicatorTests.swift; sourceTree = "<group>"; };
B535533B1F0FEE6300A6DCBE /* TCPConnectionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TCPConnectionStub.swift; sourceTree = "<group>"; };
B535533D1F11261200A6DCBE /* BitField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitField.swift; sourceTree = "<group>"; };
B535533F1F112BDB00A6DCBE /* BitFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitFieldTests.swift; sourceTree = "<group>"; };
B537CF051F03148B0084089B /* HTTPConnectionStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HTTPConnectionStub.swift; path = "HTTP Networking/HTTPConnectionStub.swift"; sourceTree = "<group>"; };
B54D0C141CA53983004343BD /* BEncode.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BEncode.xcodeproj; path = Submodules/BEncodeSwift/BEncode.xcodeproj; sourceTree = "<group>"; };
B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TorrentMetaInfo.swift; path = Models/TorrentMetaInfo.swift; sourceTree = "<group>"; };
B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TorrentMetaInfoTests.swift; path = Models/TorrentMetaInfoTests.swift; sourceTree = "<group>"; };
B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TorrentMetaInfo.swift; sourceTree = "<group>"; };
B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TorrentMetaInfoTests.swift; sourceTree = "<group>"; };
B54D0C2B1CA5787E004343BD /* Data+sha1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Data+sha1.swift"; path = "Utilities/Data+sha1.swift"; sourceTree = "<group>"; };
B54D0C581CA58722004343BD /* CommonCrypto.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommonCrypto.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B54D0C6F1CA58749004343BD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -243,6 +253,7 @@
children = (
B51638921F0EE9B6009E563E /* TCPConnection.swift */,
B51638941F0EEAA4009E563E /* TCPConnectionTests.swift */,
B535533B1F0FEE6300A6DCBE /* TCPConnectionStub.swift */,
B51638961F0EEC2B009E563E /* GCDAsyncSocketStub.swift */,
);
path = "TCP Networking";
@@ -281,8 +292,10 @@
children = (
B54D0C231CA56A22004343BD /* TorrentMetaInfo.swift */,
B54D0C261CA56ADB004343BD /* TorrentMetaInfoTests.swift */,
B535533D1F11261200A6DCBE /* BitField.swift */,
B535533F1F112BDB00A6DCBE /* BitFieldTests.swift */,
);
name = Models;
path = Models;
sourceTree = "<group>";
};
B54D0C291CA5785F004343BD /* Utilities */ = {
@@ -405,6 +418,8 @@
isa = PBXGroup;
children = (
B5F81E481F0436D600B25C70 /* TorrentPeerInfo.swift */,
B53553351F0FEB8800A6DCBE /* TorrentPeerCommunicator.swift */,
B53553381F0FEB9300A6DCBE /* TorrentPeerCommunicatorTests.swift */,
);
path = Peer;
sourceTree = "<group>";
@@ -765,6 +780,8 @@
B54D0C7A1CA69FAD004343BD /* TorrentMetaInfo.swift in Sources */,
B5F81E4B1F04399800B25C70 /* TorrentTrackerResponse.swift in Sources */,
B54D0C7B1CA69FD8004343BD /* Data+sha1.swift in Sources */,
B53553361F0FEB8800A6DCBE /* TorrentPeerCommunicator.swift in Sources */,
B535533E1F11261200A6DCBE /* BitField.swift in Sources */,
B5530DB21F03063300F71CCD /* HTTPConnection.swift in Sources */,
B50B24F91F0A554A00C23E7C /* UDPConnection.swift in Sources */,
B51D6C0A1F0C180D00E1E3AB /* TorrentUDPTracker.swift in Sources */,
@@ -776,14 +793,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B535533A1F0FEB9700A6DCBE /* TorrentPeerCommunicatorTests.swift in Sources */,
B51638971F0EEC2B009E563E /* GCDAsyncSocketStub.swift in Sources */,
B51D6C091F0C180600E1E3AB /* TorrentUDPTrackerTests.swift in Sources */,
B537CF061F03148B0084089B /* HTTPConnectionStub.swift in Sources */,
B5BD7FD61F03032400621BC2 /* TorrentHTTPTrackerTests.swift in Sources */,
B556D5AE1F0BA1FD00277B8D /* GCDAsyncUdpSocketStub.swift in Sources */,
B5530DB51F03063E00F71CCD /* HTTPConnectionTests.swift in Sources */,
B535533C1F0FEE6300A6DCBE /* TCPConnectionStub.swift in Sources */,
B56A8A071C83539300426AC8 /* TestHelpers.swift in Sources */,
B585AB841C3833450093FA41 /* BitTorrentTests.swift in Sources */,
B53553401F112BDB00A6DCBE /* BitFieldTests.swift in Sources */,
B54D0C271CA56ADB004343BD /* TorrentMetaInfoTests.swift in Sources */,
B51638951F0EEAA4009E563E /* TCPConnectionTests.swift in Sources */,
B55317E01F02FE1500909ADF /* URLEncodeTests.swift in Sources */,
+64
View File
@@ -0,0 +1,64 @@
//
// BitField.swift
// BitTorrent
//
// Created by Ben Davis on 08/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import Foundation
struct BitField {
let size: Int
var value: [Bool]
init(size: Int) {
self.size = size
self.value = Array(repeating: false, count: size)
}
mutating func set(at index: Int) {
value[index] = true
}
mutating func unset(at index: Int) {
value[index] = false
}
func toData() -> Data {
let numberOfBytes: Int
if (size % 8) == 0 {
numberOfBytes = size / 8
} else {
numberOfBytes = (size / 8) + 1
}
var bytes: [UInt8] = []
for i in 0..<numberOfBytes {
let startIndex = i * 8
var byte: UInt8
if size >= startIndex + 8 {
byte = UInt8(bits: (value[startIndex],
value[startIndex + 1],
value[startIndex + 2],
value[startIndex + 3],
value[startIndex + 4],
value[startIndex + 5],
value[startIndex + 6],
value[startIndex + 7]))
} else {
byte = UInt8(bits: (value[startIndex],
(size > startIndex + 1) ? value[startIndex + 1] : false,
(size > startIndex + 2) ? value[startIndex + 2] : false,
(size > startIndex + 3) ? value[startIndex + 3] : false,
(size > startIndex + 4) ? value[startIndex + 4] : false,
(size > startIndex + 5) ? value[startIndex + 5] : false,
(size > startIndex + 6) ? value[startIndex + 6] : false,
false))
}
bytes.append(byte)
}
return Data(bytes: bytes)
}
}
+60
View File
@@ -0,0 +1,60 @@
//
// BitFieldTests.swift
// BitTorrentTests
//
// Created by Ben Davis on 08/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import XCTest
@testable import BitTorrent
class BitFieldTests: XCTestCase {
func test_create() {
let sut = BitField(size: 5)
XCTAssertEqual(sut.size, 5)
XCTAssertEqual(sut.value, [false, false, false, false, false])
}
func test_canSetValueAtIndex() {
var sut = BitField(size: 5)
sut.set(at: 0)
sut.set(at: 2)
sut.set(at: 4)
XCTAssertEqual(sut.value, [true, false, true, false, true])
}
func test_canUnsetValueAtIndex() {
var sut = BitField(size: 5)
sut.set(at: 0)
sut.set(at: 2)
sut.set(at: 4)
sut.unset(at: 4)
XCTAssertEqual(sut.value, [true, false, true, false, false])
}
func test_canConvertToData_bitsAlignToBytes() {
var sut = BitField(size: 16)
sut.set(at: 7)
sut.set(at: 15)
sut.set(at: 14)
let result = sut.toData()
XCTAssertEqual(result, Data(bytes:[1, 3]))
}
func test_canConvertToData_bitsDoNotAlignToBytes() {
var sut = BitField(size: 13)
sut.set(at: 7)
sut.set(at: 12)
let result = sut.toData()
XCTAssertEqual(result, Data(bytes:[1, 8]))
}
func test_canConvertToData_lessThan8Bits() {
var sut = BitField(size: 5)
sut.set(at: 3)
let result = sut.toData()
XCTAssertEqual(result, Data(bytes:[16]))
}
}
@@ -0,0 +1,122 @@
//
// TorrentPeerCommunicator.swift
// BitTorrent
//
// Created by Ben Davis on 07/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import Foundation
class TorrentPeerCommunicator {
enum Message: UInt8 {
case choke = 0
case unchoke = 1
case interested = 2
case notInterested = 3
case have = 4
case bitfield = 5
case request = 6
case piece = 7
case cancel = 8
case port = 9
}
let defaultTimeout: TimeInterval = 10
private let peerInfo: TorrentPeerInfo
private let connection: TCPConnectionProtocol
init(peerInfo: TorrentPeerInfo, tcpConnection: TCPConnectionProtocol = TCPConnection()) {
self.peerInfo = peerInfo
self.connection = tcpConnection
}
func connect() throws {
try connection.connect(to: peerInfo.ip, onPort: peerInfo.port)
}
func sendHandshake(for infoHash: Data, clientId: Data) {
let protocolString = "BitTorrent protocol"
let protocolStringLength = UInt8(protocolString.count)
let payload =
protocolStringLength.toData() + // pstrlen (Protocol string length)
protocolString.data(using: .ascii)! + // pstr (Protocol string)
Data(bytes: [0,0,0,0,0,0,0,0]) + // reserved (8 reserved bytes)
infoHash + // info_hash
clientId // peer_id of the current user
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
private let keepAlivePayload = Data(bytes: [0, 0, 0, 0]) // 0 length message
func sendKeepAlive() {
connection.write(keepAlivePayload, withTimeout: defaultTimeout, tag: 0)
}
func sendChoke() {
let payload = makePayload(forMessage: .choke)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendUnchoke() {
let payload = makePayload(forMessage: .unchoke)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendInterested() {
let payload = makePayload(forMessage: .interested)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendNotInterested() {
let payload = makePayload(forMessage: .notInterested)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendHavePiece(at index: Int) {
let data = UInt32(index).toData()
let payload = makePayload(forMessage: .have, data: data)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendBitField(_ bitField: BitField) {
let data = bitField.toData()
let payload = makePayload(forMessage: .bitfield, data: data)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendRequest(fromPieceAtIndex index: Int, begin: Int, length: Int) {
let data = UInt32(index).toData() + UInt32(begin).toData() + UInt32(length).toData()
let payload = makePayload(forMessage: .request, data: data)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendPiece(fromPieceAtIndex index: Int, begin: Int, block: Data) {
let data = UInt32(index).toData() + UInt32(begin).toData() + block
let payload = makePayload(forMessage: .piece, data: data)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendCancel(forPieceAtIndex index: Int, begin: Int, length: Int) {
let data = UInt32(index).toData() + UInt32(begin).toData() + UInt32(length).toData()
let payload = makePayload(forMessage: .cancel, data: data)
connection.write(payload, withTimeout: defaultTimeout, tag: 0)
}
func sendPort(_ listenPort: UInt16) {
// TODO: implement with DHT peer discovery
}
// MARK -
func makePayload(forMessage message: Message, data: Data? = nil) -> Data {
let data = data ?? Data()
let length = UInt32(data.count + 1)
return length.toData() + message.rawValue.toData() + data
}
}
@@ -0,0 +1,194 @@
//
// TorrentPeerCommunicatorTests.swift
// BitTorrent
//
// Created by Ben Davis on 07/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import XCTest
@testable import BitTorrent
class TorrentPeerComminicatorTests: XCTestCase {
var tcpConnection: TCPConnectionStub!
var sut: TorrentPeerCommunicator!
let ip = "127.0.0.1"
let port: UInt16 = 123
let peerId = "-BD0000-bxa]N#IRKqv`".data(using: .ascii)!
let expectedTimeout: TimeInterval = 10
override func setUp() {
super.setUp()
let peer = TorrentPeerInfo(ip: ip, port: port, peerId: peerId)
tcpConnection = TCPConnectionStub()
sut = TorrentPeerCommunicator(peerInfo: peer, tcpConnection: tcpConnection)
}
func test_canConnect() {
try! sut.connect()
XCTAssert(tcpConnection.connectCalled)
XCTAssertEqual(tcpConnection.connectParameters?.host, ip)
XCTAssertEqual(tcpConnection.connectParameters?.port, port)
}
func test_sendHandshake() {
let infoHash = Data(bytes: [1, 2, 3])
let clientId = Data(bytes: [4, 5, 6])
sut.sendHandshake(for: infoHash, clientId: clientId)
let expectedPayload =
Data(bytes: [19]) + // pstrlen (Protocol string length)
"BitTorrent protocol".data(using: .ascii)! + // pstr (Protocol string)
Data(bytes: [0,0,0,0,0,0,0,0]) + // reserved (8 reserved bytes)
infoHash + // info_hash
clientId // peer_id of the current user
assertDataSent(expectedPayload)
}
func test_sendKeepAlive() {
sut.sendKeepAlive()
let expectedPayload = Data(bytes: [0, 0, 0, 0]) // Length prefix of 0
assertDataSent(expectedPayload)
}
func test_sendChoke() {
sut.sendChoke()
let expectedPayload = Data(bytes: [
0, 0, 0, 1, // Length 1
0 // Id 0
])
assertDataSent(expectedPayload)
}
func test_sendUnchoke() {
sut.sendUnchoke()
let expectedPayload = Data(bytes: [
0, 0, 0, 1, // Length 1
1 // Id 1
])
assertDataSent(expectedPayload)
}
func testSendInterested() {
sut.sendInterested()
let expectedPayload = Data(bytes: [
0, 0, 0, 1, // Length 1
2 // Id 2
])
assertDataSent(expectedPayload)
}
func testSendNotInterested() {
sut.sendNotInterested()
let expectedPayload = Data(bytes: [
0, 0, 0, 1, // Length 1
3 // Id 3
])
assertDataSent(expectedPayload)
}
func test_sendHave() {
let pieceIndex = 456
sut.sendHavePiece(at: pieceIndex)
let expectedPayload = Data(bytes: [0, 0, 0, 5, // Length 5
4 // Id 4
]) + UInt32(pieceIndex).toData() // Piece index
assertDataSent(expectedPayload)
}
func test_sendBitField() {
// Given
var bitField = BitField(size: 10)
bitField.set(at: 2)
bitField.set(at: 5)
bitField.set(at: 9)
// When
sut.sendBitField(bitField)
// Then
let expectedPayload = Data(bytes: [0, 0, 0, 3, // Length 3
5 // Id 5
]) + bitField.toData() // Piece index
assertDataSent(expectedPayload)
}
func test_sendRequest() {
let index = 123
let begin = 456
let length = 789
sut.sendRequest(fromPieceAtIndex: index, begin: begin, length: length)
let expectedPayload = Data(bytes: [0, 0, 0, 13, // Length 13
6 // Id 6
]) + UInt32(index).toData() + // index
UInt32(begin).toData() + // begin
UInt32(length).toData() // length
assertDataSent(expectedPayload)
}
func test_sendPiece() {
let index = 123
let begin = 456
let block = Data(bytes: [1,2,3])
sut.sendPiece(fromPieceAtIndex: index, begin: begin, block: block)
let expectedPayload = Data(bytes: [0, 0, 0, 12, // Length 12
7 // Id 7
]) + UInt32(index).toData() + // index
UInt32(begin).toData() + // begin
block // block
assertDataSent(expectedPayload)
}
func test_sendCancel() {
let index = 123
let begin = 456
let length = 789
sut.sendCancel(forPieceAtIndex: index, begin: begin, length: length)
let expectedPayload = Data(bytes: [0, 0, 0, 13, // Length 13
8 // Id 8
]) + UInt32(index).toData() + // index
UInt32(begin).toData() + // begin
UInt32(length).toData() // length
assertDataSent(expectedPayload)
}
func test_sendPort() {
// TODO: implement with DHT peer discovery
}
// MARK: -
func assertDataSent(_ data: Data) {
XCTAssert(tcpConnection.writeDataCalled)
XCTAssertEqual(tcpConnection.writeDataParameters?.timeout, expectedTimeout)
XCTAssertEqual(tcpConnection.writeDataParameters?.data, data)
}
}
+5 -6
View File
@@ -10,10 +10,10 @@ import Foundation
struct TorrentPeerInfo {
let ip: String
let port: Int
let port: UInt16
let peerId: Data?
init(ip: String, port: Int, peerId: Data?) {
init(ip: String, port: UInt16, peerId: Data?) {
self.ip = ip
self.port = port
self.peerId = peerId
@@ -28,7 +28,7 @@ struct TorrentPeerInfo {
}
self.ip = ip
self.port = port
self.port = UInt16(port)
self.peerId = dictionary["peer id"] as? Data
}
@@ -42,11 +42,10 @@ struct TorrentPeerInfo {
let ip4 = Int(data[i*6 + 3])
let portBytes = [data[i*6 + 5], data[i*6 + 4]]
let portu16 = UnsafePointer(portBytes).withMemoryRebound(to: UInt16.self, capacity: 1) {
let port = UnsafePointer(portBytes).withMemoryRebound(to: UInt16.self, capacity: 1) {
$0.pointee
}
let port = Int(portu16)
let peer = TorrentPeerInfo(ip: "\(ip1).\(ip2).\(ip3).\(ip4)", port: port, peerId: nil)
result.append(peer)
}
+18 -5
View File
@@ -9,6 +9,19 @@
import Foundation
import CocoaAsyncSocket
protocol TCPConnectionProtocol {
weak var delegate: TCPConnectionDelegate? { set get }
var connectedHost: String? { get }
var connectedPort: UInt16? { get }
func connect(to host: String, onPort port: UInt16) throws
func disconnect()
func readData(withTimeout timeout: TimeInterval, tag: Int)
func write(_ data: Data, withTimeout timeout: TimeInterval, tag: Int)
}
protocol TCPConnectionDelegate: class {
func tcpConnection(_ sender: TCPConnection, didConnectToHost host: String, port: UInt16)
func tcpConnection(_ sender: TCPConnection, didRead data: Data, withTag tag: Int)
@@ -19,7 +32,7 @@ protocol TCPConnectionDelegate: class {
/// This class is a thin wrapper around the socket library to protect against changes
/// in its interface, and to allow me to replace CocoaAsyncSocket with a swift framework
/// one day.
class TCPConnection: NSObject {
class TCPConnection: NSObject, TCPConnectionProtocol {
weak var delegate: TCPConnectionDelegate?
@@ -47,15 +60,15 @@ class TCPConnection: NSObject {
try socket.connect(toHost: host, onPort: port)
}
func readData(withTimeout timout: TimeInterval, tag: Int) {
socket.readData(withTimeout: timout, tag: tag)
}
func disconnect() {
socket.delegate = nil
socket.disconnect()
}
func readData(withTimeout timeout: TimeInterval, tag: Int) {
socket.readData(withTimeout: timeout, tag: tag)
}
func write(_ data: Data, withTimeout timeout: TimeInterval, tag: Int) {
socket.write(data, withTimeout: timeout, tag: tag)
}
@@ -0,0 +1,45 @@
//
// TCPConnectionStub.swift
// BitTorrentTests
//
// Created by Ben Davis on 07/07/2017.
// Copyright © 2017 Ben Davis. All rights reserved.
//
import Foundation
@testable import BitTorrent
class TCPConnectionStub: TCPConnectionProtocol {
weak var delegate: TCPConnectionDelegate?
var connectedHost: String?
var connectedPort: UInt16?
var connectCalled = false
var connectParameters: (host: String, port: UInt16)?
func connect(to host: String, onPort port: UInt16) throws {
connectCalled = true
connectParameters = (host, port)
}
var disconnectCalled = false
func disconnect() {
disconnectCalled = true
}
var readDataCalled = false
var readDataParameters: (timeout: TimeInterval, tag: Int)?
func readData(withTimeout timeout: TimeInterval, tag: Int) {
readDataCalled = true
readDataParameters = (timeout, tag)
}
var writeDataCalled = false
var writeDataParameters: (data: Data, timeout: TimeInterval, tag: Int)?
func write(_ data: Data, withTimeout timeout: TimeInterval, tag: Int) {
writeDataCalled = true
writeDataParameters = (data, timeout, tag)
}
}
@@ -68,7 +68,7 @@ class TCPConnectionTests: XCTestCase {
// Issue with solution: Cannot call delegate methods using this protocol as parameter
// (delegate methods are defined as taking the concrete class)
// For now just assert they are both nil
// TODO: Test properly. For now just assert they are both nil
XCTAssertNil(sut.connectedHost)
XCTAssertNil(sut.connectedPort)
}
@@ -162,10 +162,10 @@ class TorrentHTTPTrackerTests: XCTestCase {
let peer1Id = "peerId1-------------"
let peer1IP = "127.0.0.1"
let peer1Port = 15383
let peer1Port: UInt16 = 15383
let peer2Id = "peerId2-------------"
let peer2IP = "127.0.0.1"
let peer2Port = 6881
let peer2Port: UInt16 = 6881
let peer1 = "d7:peer id20:\(peer1Id)2:ip9:\(peer1IP)4:porti\(peer1Port)ee"
let peer2 = "d7:peer id20:\(peer2Id)2:ip9:\(peer2IP)4:porti\(peer2Port)ee"
@@ -27,10 +27,11 @@ class InternetProtocolTests: XCTestCase {
XCTAssertNil(result)
}
func test_canDecodeGoogle() {
let result = InternetProtocol.getIPAddress(of: "google.com")
XCTAssertNotNil(result)
}
// Bad Test
// func test_canDecodeGoogle() {
// let result = InternetProtocol.getIPAddress(of: "google.com")
// XCTAssertNotNil(result)
// }
func test_canDecodeIPv4AddressFromData() {
let data = Data(bytes: [16,2,122,105,127,0,0,1,0,0,0,0,0,0,0,0])