// Commented for the Learning branch package com.limegroup.bittorrent.messages; import java.nio.ByteBuffer; import java.nio.ByteOrder; import com.limegroup.bittorrent.BTInterval; /** * A BitTorrent Request message lets a BitTorrent program request a part of a file from a peer. * A Request message is 17 bytes long: * * LLLLTPPPPOOOOSSSS * * LLLL, PPPP, OOOO, and SSSS are ints written in big endian order. * L is 13, the length of the bytes beyond. * T is 0x06, the byte code for a Request message. * P is the piece number, like 0 for the first piece. * The Request message requests S bytes a distance O into the piece. * * For instance, here's a request for the 4 bytes that are 8 bytes into piece number 5: * * PPPP 5 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb * OOOO 8 -------> * SSSS 4 rrrr */ public class BTRequest extends BTMessage { /** 65536 bytes, 65 KB, the maximum amount of data a remote computer can request from us. */ private static final int MAX_REQUEST_SIZE = 65536; /** The piece number and range of bytes in the piece that this Request message is asking for. */ private BTInterval in; /** A byte buffer with the payload of the message, just the "PPPPOOOOSSSS" part. */ private ByteBuffer _payload; /** * Make a new BTRequest object that represents a BitTorrent Request message. * * @param in A BTInterval object that has the piece number and range of data within that piece */ public BTRequest(BTInterval in) { // Save the type, 0x06 for a Request message super(REQUEST); // Save the given BTInterval object, it has all the information for the payload this.in = in; } /** * Make a BTRequest object that represents a BitTorrent Request message we've read from the network. * We got it because a remote computer is requesting a part of a file from us. * This is the message parser. * * The whole message looks like "LLLLTPPPPOOOOSSSS". * Call readMessage() with the payload ByteBuffer clipped beyond the length and type, around the 12 bytes "PPPPOOOOSSSS". * P is the piece number, O is the length into the piece where the range starts, and S is the size from that point. * * @param payload A ByteBuffer with position and limit clipped around the 12-byte payload beyond the length int and type byte * @return A new BTRequest object that represents the Request message */ public static BTRequest readMessage(ByteBuffer payload) throws BadBTMessageException { // Make sure we were given exactly 12 bytes if (payload.remaining() != 12) throw new BadBTMessageException("unexpected payload in request message: " + new String(payload.array())); // Tell the ByteBuffer to give us ints in big endian order payload.order(ByteOrder.BIG_ENDIAN); // First, read the piece number int pieceNum = payload.getInt(); // Reads the first 4 bytes as a big endian int, moving position past them if (pieceNum < 0) throw new BadBTMessageException("invalid piece number in request message: " + pieceNum); // Read the start distance int offset = payload.getInt(); if (offset < 0) throw new BadBTMessageException("negative offset in mesage"); // Read the size int length = payload.getInt(); if (length <= 0 || length > MAX_REQUEST_SIZE) throw new BadBTMessageException("invalid requested length in request message: " + length); // Make a BTInterval object, and use it to make and return the new BTRequest object return new BTRequest(new BTInterval( offset, // (2) The distance in the piece to the start of the range the Request message will ask for offset + length - 1, // (3) The number of bytes there the message is requesting, subtract 1 because BTInterval includes the last byte pieceNum)); // (1) The piece number } /** * Get the range of data this Request message is asking for. * Returns a BTInterval object, which uses index of first byte and index of last byte, not distance and size. * * @return A BTInterval object that has the piece number and range within it */ public BTInterval getInterval() { // Return the BTInterval object inside this BTRequest object return in; } /** * Compose the payload of this BitTorrent Request message. * * An entire Request message is 18 bytes arranged like "LLLLTPPPPOOOOSSSS". * getPayload() returns a 12-byte array with the payload after the length and type, just "PPPPOOOOSSSS". * PPPP, OOOO, and SSSS are ints written in big endian order. * PPPP is the piece number. * OOOO is the distance in bytes from the start of the piece to the range this Request message is requesting. * SSSS is the number of bytes at OOOO this Request message is requesting. * * @return A 12-byte ByteBuffer with "PPPPOOOOSSSS", with position at the start and limit at the end */ public ByteBuffer getPayload() { // If we haven't composed this Request message's payload yet if (_payload == null) { // Compose the 12 bytes of payload data ByteBuffer buf = ByteBuffer.allocate(12); // Make a ByteBuffer that can hold 12 bytes buf.order(ByteOrder.BIG_ENDIAN); // Have the putInt() method write ints in big endian order buf.putInt(in.getId()); // Write the piece number, distance, and size buf.putInt(in.low); buf.putInt(in.high - in.low + 1); // Add 1 because high points to the last byte in the range, not beyond it _payload = buf.asReadOnlyBuffer(); // Don't let the caller change the buffer we'll return } // Put position at the start and limit at the end, and return a reference to the buffer with the payload data _payload.clear(); return _payload; } /** * Express this BTRequest object as text. * Makes a string like "BTRequest (25:5-10)". * 25 is the piece number, and 5 is the distance in bytes into that piece that the range starts. * 10 isn't the size, and it's not the end of the range either, it's the distance to the last byte in the range. * * @return A String like "BTRequest (25:5-10)" */ public String toString() { // Compose the text, calling BTInterval.toString() by adding in to a String return "BTRequest (" + in + ")"; } }