Reading — step 1 of 5
Read
HTTP Tracker Protocol
A tracker is a phone book, not a relay. It never touches file data; it keeps, per infohash, a list of who is currently in the swarm. You announce yourself, you get peers back, and you re-announce on an interval so the list stays fresh. The original BEP 3 design is a single HTTP GET:
GET /announce?info_hash=%01%02...&peer_id=-qB4500-abc123&port=6881
&uploaded=0&downloaded=0&left=4200000000&compact=1&event=started
| Param | Why it exists |
|---|---|
info_hash | Which swarm — the 20 raw hash bytes, percent-encoded (see below) |
peer_id | Your 20-byte identity; conventionally -qB4500- + random (client code + version) |
port | The port you listen on — peers will dial you back |
uploaded / downloaded | Session byte counters (stats; ratio enforcement on private trackers) |
left | Bytes you still need. left=0 marks you a seeder |
compact | 1 = send the packed binary peer list (BEP 23) |
event | started on first contact, completed when done, stopped on exit; omitted for periodic re-announces |
The info_hash encoding trap
info_hash is not text. It is 20 raw bytes, and an HTTP query string cannot carry raw bytes — so BEP 3 says: percent-encode per byte. Bytes in the unreserved set [a-zA-Z0-9._~-] pass through literally; every other byte becomes %XX.
Walk the hash 00 11 22 33 44 55 66 77 ... through that rule: 0x00 → %00, 0x11 → %11, 0x22 (the quote character) → %22 — but 0x33 is ASCII 3, unreserved, so it passes through literally, and 0x44 is D. The encoded result %00%11%223DUfw... interleaves escapes with literal characters. It looks broken; it is correct.
The classic bug is sending the 40-char hex string instead. The tracker does not "decode" it — it percent-decodes and sees a 40-byte identifier that matches no torrent. You get an empty peer list or a failure reason, and nothing tells you why. A subtler bug: routing the hash through a string API. Keep it as bytes and build the escape per byte — pushing it through str invites UTF-8 expansion of every byte above 0x7F.
The response
A bencoded dict:
{
'interval': 1800, # seconds until you re-announce
'min interval': 900, # optional floor
'complete': 42, # seeders
'incomplete': 7, # leechers
'peers': <binary blob> # compact format
}
(On error the dict contains a single failure reason string instead.)
With compact=1, peers is one binary string of 6 * N bytes — per peer, 4 IPv4 bytes then a 2-byte big-endian port. Walk c0 a8 01 01 1a e1: IP 192.168.1.1, port 0x1ae1 = 6881. The older verbose form (a bencoded list of dicts) still exists in the spec, but compact is what real trackers send — at 6 bytes per peer, a 50-peer answer fits in ~300 bytes.
Your exercise
BUILD prints the announce URL with parameters in exactly this order: info_hash, peer_id, port, uploaded, downloaded, left, compact=1. Mistakes the grader catches:
- Lowercase escapes. The expected output reads
info_hash=%01...%0A%0B%0C...— uppercase hex (%02Xformatting).%0afails the exact string comparison. - Escaping unreserved bytes. The hidden test's hash contains
0x33and0x44–0x77; the grader expects%00%11%223DUfw%88.... Escaping everything (%00%11%22%33%44...) violates the unreserved-set rule and fails. - PARSE input is hex. The tests feed the bencoded response as one hex string (raw tracker bytes cannot ride a text stdin):
bytes.fromhexfirst, then bencode-decode. Printinterval=,complete=,incomplete=, then apeers:line followed by oneip:portper line — and whenpeersis empty,peers:is the final line with nothing after it.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…