Recently, I’ve been looking into the technology and implementation behind BitTorrent, a peer-to-peer technology that has risen to extreme popularity in the last few years. Adobe’s new cross-platform runtime codenamed Apollo just came out, and I needed a project to get my feet wet. I decided to built a simple application that allows you to read the contents of a .torrent file or even create a new torrents from files directly on your hard drive to upload to your favorite tracker. It’s a fun little project that has already come in handy for a couple people.
Let’s start by taking a look at some of the implementation details related to the BitTorrent specification. Particularly of note is the class Bencoder. BitTorrent uses a special encoding system that you might find similar to JSON. It’s a very light-weight way to encode data, and it only a supports a few types: numbers, strings, dictionaries (objects), and lists (arrays). Consider the following Bencoded object for an idea of how it looks:
d4:name4:Josh9:highscorei12100ee
In ActionScript, you would declare the same object like this:
{name: "Josh", highscore: 12100}
The Bencoder class handles encoding and decoding ActionScript objects with this format.
When reading .torrent files, the readTorrentData
function is where the magic happens. An interesting thing about this file format is that it isn’t all plain-text, as you’d expect it to be with the Bencode specification I described above. Torrent files have a section of binary data that is hashed with the SHA-1 algorithm, and this is stored in binary. If you try to read this section with ByteArray.readUTFBytes()
, the function may stop early and you won’t get the correct data. So, this function reads the first string-based part of the file, then the binary section which is encoded like a string with the length at the beginning, then the final string-based portion.
private function readTorrentData(torrentStream:FileStream):void { //get the first part of the file var fileData:String = this.readDataBeforeHashes(torrentStream); //read the hash pieces var piecesLength:int = this.readPiecesLength(torrentStream); var hashes:ByteArray = new ByteArray(); torrentStream.readBytes(hashes, 0, int(piecesLength)); //add the readable hash length to the file fileData += (piecesLength * 2) + ":"; //add the readable hex hashes to the file fileData += ByteUtil.byteArrayToHexString(hashes); //add the end of the file fileData += torrentStream.readUTFBytes(torrentStream.bytesAvailable); torrentStream.close(); this.parseTorrentData(fileData); }
You’ll notice a call to the byteArrayToHexString
function in my ByteUtil
class. This function converts the raw bytes into human-readable hex strings. The class also includes a corresponding function that will convert a hex string back to ByteArray
.
Grab the AIR installation file for Torrent Utility, and give it a try. If you’d like a closer look, download the MIT-licensed source code as well. Please note that you will need the latest build of Adobe’s corelib, version 0.90, to build the source. The version includes the enhancements to the SHA1 class that I wrote specifically when I needed them for this application.
You will, of course, need the alpha release of the Apollo runtime, which you can download on Adobe Labs. Please remember, Apollo is pre-release developer software that will contain bugs, may erase your hard drive, and it might even rape your dog and call your grandma offensive names. In other words, use with caution and don’t expect it to be polished.