2005-10-19 22:02:37 +00:00
|
|
|
/* PeerState - Keeps track of the Peer state through connection callbacks.
|
|
|
|
Copyright (C) 2003 Mark J. Wielaard
|
|
|
|
|
|
|
|
This file is part of Snark.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.klomp.snark;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2010-11-26 00:44:00 +00:00
|
|
|
import java.util.HashSet;
|
2005-10-19 22:02:37 +00:00
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
2010-11-26 00:44:00 +00:00
|
|
|
import java.util.Set;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2010-04-12 19:07:53 +00:00
|
|
|
import net.i2p.I2PAppContext;
|
2005-12-19 13:34:52 +00:00
|
|
|
import net.i2p.util.Log;
|
|
|
|
|
2010-11-21 21:19:12 +00:00
|
|
|
class PeerState implements DataLoader
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
2010-04-12 19:07:53 +00:00
|
|
|
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class);
|
2010-11-26 00:44:00 +00:00
|
|
|
private final Peer peer;
|
2010-12-21 23:43:13 +00:00
|
|
|
/** Fixme, used by Peer.disconnect() to get to the coordinator */
|
2005-10-19 22:02:37 +00:00
|
|
|
final PeerListener listener;
|
2010-12-21 23:43:13 +00:00
|
|
|
private MetaInfo metainfo;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
// Interesting and choking describes whether we are interested in or
|
|
|
|
// are choking the other side.
|
2012-06-13 19:04:11 +00:00
|
|
|
volatile boolean interesting;
|
|
|
|
volatile boolean choking = true;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
// Interested and choked describes whether the other side is
|
|
|
|
// interested in us or choked us.
|
2012-06-13 19:04:11 +00:00
|
|
|
volatile boolean interested;
|
|
|
|
volatile boolean choked = true;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2010-11-26 00:44:00 +00:00
|
|
|
/** the pieces the peer has */
|
2005-10-19 22:02:37 +00:00
|
|
|
BitField bitfield;
|
|
|
|
|
|
|
|
// Package local for use by Peer.
|
|
|
|
final PeerConnectionIn in;
|
|
|
|
final PeerConnectionOut out;
|
|
|
|
|
|
|
|
// Outstanding request
|
2010-10-29 20:31:07 +00:00
|
|
|
private final List<Request> outstandingRequests = new ArrayList();
|
2010-10-30 15:28:29 +00:00
|
|
|
/** the tail (NOT the head) of the request queue */
|
2005-10-19 22:02:37 +00:00
|
|
|
private Request lastRequest = null;
|
|
|
|
|
2010-11-27 14:34:08 +00:00
|
|
|
// FIXME if piece size < PARTSIZE, pipeline could be bigger
|
2009-04-22 13:54:59 +00:00
|
|
|
private final static int MAX_PIPELINE = 5; // this is for outbound requests
|
2008-05-18 21:45:54 +00:00
|
|
|
private final static int MAX_PIPELINE_BYTES = 128*1024; // this is for inbound requests
|
2009-04-22 13:54:59 +00:00
|
|
|
public final static int PARTSIZE = 16*1024; // outbound request
|
2007-03-08 18:55:17 +00:00
|
|
|
private final static int MAX_PARTSIZE = 64*1024; // Don't let anybody request more than this
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2010-12-20 00:05:03 +00:00
|
|
|
/**
|
|
|
|
* @param metainfo null if in magnet mode
|
|
|
|
*/
|
2005-10-19 22:02:37 +00:00
|
|
|
PeerState(Peer peer, PeerListener listener, MetaInfo metainfo,
|
|
|
|
PeerConnectionIn in, PeerConnectionOut out)
|
|
|
|
{
|
|
|
|
this.peer = peer;
|
|
|
|
this.listener = listener;
|
|
|
|
this.metainfo = metainfo;
|
|
|
|
|
|
|
|
this.in = in;
|
|
|
|
this.out = out;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE Methods that inspect or change the state synchronize (on this).
|
|
|
|
|
|
|
|
void keepAliveMessage()
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv alive");
|
2005-10-19 22:02:37 +00:00
|
|
|
/* XXX - ignored */
|
|
|
|
}
|
|
|
|
|
|
|
|
void chokeMessage(boolean choke)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv " + (choke ? "" : "un") + "choked");
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2010-10-27 13:29:27 +00:00
|
|
|
boolean resend = choked && !choke;
|
2005-10-19 22:02:37 +00:00
|
|
|
choked = choke;
|
|
|
|
|
|
|
|
listener.gotChoke(peer, choke);
|
|
|
|
|
2010-10-27 13:29:27 +00:00
|
|
|
if (interesting && !choked)
|
|
|
|
request(resend);
|
2010-11-26 00:44:00 +00:00
|
|
|
|
|
|
|
if (choked) {
|
2010-11-27 14:34:08 +00:00
|
|
|
out.cancelRequestMessages();
|
|
|
|
// old Roberts thrash us here, choke+unchoke right together
|
2010-11-29 13:08:03 +00:00
|
|
|
// The only problem with returning the partials to the coordinator
|
|
|
|
// is that chunks above a missing request are lost.
|
|
|
|
// Future enhancements to PartialPiece could keep track of the holes.
|
2012-05-19 13:27:02 +00:00
|
|
|
List<Request> pcs = returnPartialPieces();
|
2010-11-27 14:34:08 +00:00
|
|
|
if (!pcs.isEmpty()) {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " got choked, returning partial pieces to the PeerCoordinator: " + pcs);
|
|
|
|
listener.savePartialPieces(this.peer, pcs);
|
|
|
|
}
|
2010-11-26 00:44:00 +00:00
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void interestedMessage(boolean interest)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv " + (interest ? "" : "un")
|
|
|
|
+ "interested");
|
2005-10-19 22:02:37 +00:00
|
|
|
interested = interest;
|
|
|
|
listener.gotInterest(peer, interest);
|
|
|
|
}
|
|
|
|
|
|
|
|
void haveMessage(int piece)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv have(" + piece + ")");
|
2010-12-20 00:05:03 +00:00
|
|
|
// FIXME we will lose these until we get the metainfo
|
|
|
|
if (metainfo == null)
|
|
|
|
return;
|
2005-10-19 22:02:37 +00:00
|
|
|
// Sanity check
|
|
|
|
if (piece < 0 || piece >= metainfo.getPieces())
|
|
|
|
{
|
|
|
|
// XXX disconnect?
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Got strange 'have: " + piece + "' message from " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized(this)
|
|
|
|
{
|
|
|
|
// Can happen if the other side never send a bitfield message.
|
|
|
|
if (bitfield == null)
|
|
|
|
bitfield = new BitField(metainfo.getPieces());
|
|
|
|
|
|
|
|
bitfield.set(piece);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (listener.gotHave(peer, piece))
|
|
|
|
setInteresting(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bitfieldMessage(byte[] bitmap)
|
|
|
|
{
|
|
|
|
synchronized(this)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv bitfield");
|
2005-10-19 22:02:37 +00:00
|
|
|
if (bitfield != null)
|
|
|
|
{
|
|
|
|
// XXX - Be liberal in what you except?
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Got unexpected bitfield message from " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX - Check for weird bitfield and disconnect?
|
2010-12-20 00:05:03 +00:00
|
|
|
// FIXME will have to regenerate the bitfield after we know exactly
|
|
|
|
// how many pieces there are, as we don't know how many spare bits there are.
|
|
|
|
if (metainfo == null)
|
|
|
|
bitfield = new BitField(bitmap, bitmap.length * 8);
|
|
|
|
else
|
|
|
|
bitfield = new BitField(bitmap, metainfo.getPieces());
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
2010-12-20 00:05:03 +00:00
|
|
|
if (metainfo == null)
|
|
|
|
return;
|
2010-04-10 15:26:23 +00:00
|
|
|
boolean interest = listener.gotBitField(peer, bitfield);
|
|
|
|
setInteresting(interest);
|
|
|
|
if (bitfield.complete() && !interest) {
|
|
|
|
// They are seeding and we are seeding,
|
|
|
|
// why did they contact us? (robert)
|
|
|
|
// Dump them quick before we send our whole bitmap
|
|
|
|
if (_log.shouldLog(Log.WARN))
|
2010-04-12 19:07:53 +00:00
|
|
|
_log.warn("Disconnecting seed that connects to seeds: " + peer);
|
2010-04-10 15:26:23 +00:00
|
|
|
peer.disconnect(true);
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void requestMessage(int piece, int begin, int length)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " rcv request("
|
|
|
|
+ piece + ", " + begin + ", " + length + ") ");
|
2010-12-20 00:05:03 +00:00
|
|
|
if (metainfo == null)
|
|
|
|
return;
|
2005-10-19 22:02:37 +00:00
|
|
|
if (choking)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Request received, but choking " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sanity check
|
|
|
|
if (piece < 0
|
|
|
|
|| piece >= metainfo.getPieces()
|
|
|
|
|| begin < 0
|
|
|
|
|| begin > metainfo.getPieceLength(piece)
|
|
|
|
|| length <= 0
|
2007-03-08 18:55:17 +00:00
|
|
|
|| length > MAX_PARTSIZE)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
|
|
|
// XXX - Protocol error -> disconnect?
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Got strange 'request: " + piece
|
2005-10-19 22:02:37 +00:00
|
|
|
+ ", " + begin
|
|
|
|
+ ", " + length
|
2005-12-19 13:34:52 +00:00
|
|
|
+ "' message from " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-05-18 21:45:54 +00:00
|
|
|
// Limit total pipelined requests to MAX_PIPELINE bytes
|
|
|
|
// to conserve memory and prevent DOS
|
2010-04-12 19:07:53 +00:00
|
|
|
// Todo: limit number of requests also? (robert 64 x 4KB)
|
2008-05-18 21:45:54 +00:00
|
|
|
if (out.queuedBytes() + length > MAX_PIPELINE_BYTES)
|
|
|
|
{
|
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Discarding request over pipeline limit from " + peer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-11 19:38:33 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Queueing (" + piece + ", " + begin + ", "
|
2010-11-21 21:19:12 +00:00
|
|
|
+ length + ")" + " to " + peer);
|
|
|
|
|
|
|
|
// don't load the data into mem now, let PeerConnectionOut do it
|
|
|
|
out.sendPiece(piece, begin, length, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the callback that PeerConnectionOut calls
|
|
|
|
*
|
|
|
|
* @return bytes or null for errors
|
|
|
|
* @since 0.8.2
|
|
|
|
*/
|
|
|
|
public byte[] loadData(int piece, int begin, int length) {
|
2007-01-21 01:43:31 +00:00
|
|
|
byte[] pieceBytes = listener.gotRequest(peer, piece, begin, length);
|
2005-10-19 22:02:37 +00:00
|
|
|
if (pieceBytes == null)
|
|
|
|
{
|
|
|
|
// XXX - Protocol error-> diconnect?
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Got request for unknown piece: " + piece);
|
2010-11-21 21:19:12 +00:00
|
|
|
return null;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// More sanity checks
|
2007-01-21 01:43:31 +00:00
|
|
|
if (length != pieceBytes.length)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
|
|
|
// XXX - Protocol error-> disconnect?
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Got out of range 'request: " + piece
|
2005-10-19 22:02:37 +00:00
|
|
|
+ ", " + begin
|
|
|
|
+ ", " + length
|
2005-12-19 13:34:52 +00:00
|
|
|
+ "' message from " + peer);
|
2010-11-21 21:19:12 +00:00
|
|
|
return null;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2012-06-11 19:38:33 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Sending (" + piece + ", " + begin + ", "
|
2005-12-19 13:34:52 +00:00
|
|
|
+ length + ")" + " to " + peer);
|
2010-11-21 21:19:12 +00:00
|
|
|
return pieceBytes;
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when some bytes have left the outgoing connection.
|
|
|
|
* XXX - Should indicate whether it was a real piece or overhead.
|
|
|
|
*/
|
|
|
|
void uploaded(int size)
|
|
|
|
{
|
2010-12-21 23:43:13 +00:00
|
|
|
peer.uploaded(size);
|
2005-10-19 22:02:37 +00:00
|
|
|
listener.uploaded(peer, size);
|
|
|
|
}
|
|
|
|
|
2006-09-06 06:32:53 +00:00
|
|
|
// This is used to flag that we have to back up from the firstOutstandingRequest
|
|
|
|
// when calculating how far we've gotten
|
2010-11-24 15:04:52 +00:00
|
|
|
private Request pendingRequest;
|
2006-09-06 06:32:53 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
/**
|
2010-11-24 15:04:52 +00:00
|
|
|
* Called when a full chunk (i.e. a piece message) has been received by
|
2005-10-19 22:02:37 +00:00
|
|
|
* PeerConnectionIn.
|
2010-11-24 15:04:52 +00:00
|
|
|
*
|
|
|
|
* This may block quite a while if it is the last chunk for a piece,
|
|
|
|
* as it calls the listener, who stores the piece and then calls
|
|
|
|
* havePiece for every peer on the torrent (including us).
|
|
|
|
*
|
2005-10-19 22:02:37 +00:00
|
|
|
*/
|
|
|
|
void pieceMessage(Request req)
|
|
|
|
{
|
|
|
|
int size = req.len;
|
2010-12-21 23:43:13 +00:00
|
|
|
peer.downloaded(size);
|
2005-10-19 22:02:37 +00:00
|
|
|
listener.downloaded(peer, size);
|
|
|
|
|
2010-11-24 15:04:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("got end of Chunk("
|
2012-05-19 13:27:02 +00:00
|
|
|
+ req.getPiece() + "," + req.off + "," + req.len + ") from "
|
2010-11-24 15:04:52 +00:00
|
|
|
+ peer);
|
2006-09-06 06:32:53 +00:00
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
// Last chunk needed for this piece?
|
2012-05-22 18:18:30 +00:00
|
|
|
// FIXME if priority changed to skip, we will think we're done when we aren't
|
2012-05-19 13:27:02 +00:00
|
|
|
if (getFirstOutstandingRequest(req.getPiece()) == -1)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
2010-11-24 15:04:52 +00:00
|
|
|
// warning - may block here for a while
|
2012-05-19 13:27:02 +00:00
|
|
|
if (listener.gotPiece(peer, req.getPartialPiece()))
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
2012-05-19 13:27:02 +00:00
|
|
|
_log.debug("Got " + req.getPiece() + ": " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
2012-05-19 13:27:02 +00:00
|
|
|
_log.warn("Got BAD " + req.getPiece() + " from " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
2010-11-24 15:04:52 +00:00
|
|
|
|
|
|
|
// ok done with this one
|
|
|
|
synchronized(this) {
|
|
|
|
pendingRequest = null;
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-26 00:44:00 +00:00
|
|
|
/**
|
|
|
|
* @return index in outstandingRequests or -1
|
|
|
|
*/
|
2005-10-19 22:02:37 +00:00
|
|
|
synchronized private int getFirstOutstandingRequest(int piece)
|
2010-11-26 00:44:00 +00:00
|
|
|
{
|
2005-10-19 22:02:37 +00:00
|
|
|
for (int i = 0; i < outstandingRequests.size(); i++)
|
2012-05-19 13:27:02 +00:00
|
|
|
if (outstandingRequests.get(i).getPiece() == piece)
|
2005-10-19 22:02:37 +00:00
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when a piece message is being processed by the incoming
|
2010-11-24 15:04:52 +00:00
|
|
|
* connection. That is, when the header of the piece message was received.
|
|
|
|
* Returns null when there was no such request. It also
|
2005-10-19 22:02:37 +00:00
|
|
|
* requeues/sends requests when it thinks that they must have been
|
|
|
|
* lost.
|
|
|
|
*/
|
|
|
|
Request getOutstandingRequest(int piece, int begin, int length)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
2010-11-24 15:04:52 +00:00
|
|
|
_log.debug("got start of Chunk("
|
|
|
|
+ piece + "," + begin + "," + length + ") from "
|
2005-12-19 13:34:52 +00:00
|
|
|
+ peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
int r = getFirstOutstandingRequest(piece);
|
|
|
|
|
|
|
|
// Unrequested piece number?
|
|
|
|
if (r == -1)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Unrequested 'piece: " + piece + ", "
|
2005-10-19 22:02:37 +00:00
|
|
|
+ begin + ", " + length + "' received from "
|
2005-12-19 13:34:52 +00:00
|
|
|
+ peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup the correct piece chunk request from the list.
|
|
|
|
Request req;
|
|
|
|
synchronized(this)
|
|
|
|
{
|
2010-10-29 20:31:07 +00:00
|
|
|
req = outstandingRequests.get(r);
|
2012-05-19 13:27:02 +00:00
|
|
|
while (req.getPiece() == piece && req.off != begin
|
2005-10-19 22:02:37 +00:00
|
|
|
&& r < outstandingRequests.size() - 1)
|
|
|
|
{
|
|
|
|
r++;
|
2010-10-29 20:31:07 +00:00
|
|
|
req = outstandingRequests.get(r);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Something wrong?
|
2012-05-19 13:27:02 +00:00
|
|
|
if (req.getPiece() != piece || req.off != begin || req.len != length)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.INFO))
|
|
|
|
_log.info("Unrequested or unneeded 'piece: "
|
2005-10-19 22:02:37 +00:00
|
|
|
+ piece + ", "
|
|
|
|
+ begin + ", "
|
|
|
|
+ length + "' received from "
|
2005-12-19 13:34:52 +00:00
|
|
|
+ peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
return null;
|
|
|
|
}
|
2010-11-24 15:04:52 +00:00
|
|
|
|
|
|
|
// note that this request is being read
|
|
|
|
pendingRequest = req;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
// Report missing requests.
|
|
|
|
if (r != 0)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Some requests dropped, got " + req
|
|
|
|
+ ", wanted for peer: " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
for (int i = 0; i < r; i++)
|
|
|
|
{
|
2010-10-29 20:31:07 +00:00
|
|
|
Request dropReq = outstandingRequests.remove(0);
|
2005-10-19 22:02:37 +00:00
|
|
|
outstandingRequests.add(dropReq);
|
2006-09-06 06:32:53 +00:00
|
|
|
if (!choked)
|
2005-10-19 22:02:37 +00:00
|
|
|
out.sendRequest(dropReq);
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("dropped " + dropReq + " with peer " + peer);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
outstandingRequests.remove(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Request more if necessary to keep the pipeline filled.
|
|
|
|
addRequest();
|
|
|
|
|
|
|
|
return req;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-11-26 00:44:00 +00:00
|
|
|
/**
|
|
|
|
* @return lowest offset of any request for the piece
|
|
|
|
* @since 0.8.2
|
|
|
|
*/
|
|
|
|
synchronized private Request getLowestOutstandingRequest(int piece) {
|
|
|
|
Request rv = null;
|
|
|
|
int lowest = Integer.MAX_VALUE;
|
|
|
|
for (Request r : outstandingRequests) {
|
2012-05-19 13:27:02 +00:00
|
|
|
if (r.getPiece() == piece && r.off < lowest) {
|
2010-11-26 00:44:00 +00:00
|
|
|
lowest = r.off;
|
|
|
|
rv = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pendingRequest != null &&
|
2012-05-19 13:27:02 +00:00
|
|
|
pendingRequest.getPiece() == piece && pendingRequest.off < lowest)
|
2010-11-26 00:44:00 +00:00
|
|
|
rv = pendingRequest;
|
|
|
|
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " lowest for " + piece + " is " + rv + " out of " + pendingRequest + " and " + outstandingRequests);
|
|
|
|
return rv;
|
2006-09-06 06:32:53 +00:00
|
|
|
}
|
|
|
|
|
2010-11-24 15:04:52 +00:00
|
|
|
/**
|
2010-11-27 14:34:08 +00:00
|
|
|
* Get partial pieces, give them back to PeerCoordinator.
|
|
|
|
* Clears the request queue.
|
2010-11-26 00:44:00 +00:00
|
|
|
* @return List of PartialPieces, even those with an offset == 0, or empty list
|
|
|
|
* @since 0.8.2
|
2010-11-24 15:04:52 +00:00
|
|
|
*/
|
2012-05-19 13:27:02 +00:00
|
|
|
synchronized List<Request> returnPartialPieces()
|
2006-09-13 23:02:07 +00:00
|
|
|
{
|
2010-11-26 00:44:00 +00:00
|
|
|
Set<Integer> pcs = getRequestedPieces();
|
2012-05-19 13:27:02 +00:00
|
|
|
List<Request> rv = new ArrayList(pcs.size());
|
2010-11-26 00:44:00 +00:00
|
|
|
for (Integer p : pcs) {
|
|
|
|
Request req = getLowestOutstandingRequest(p.intValue());
|
2012-05-19 13:27:02 +00:00
|
|
|
if (req != null) {
|
|
|
|
req.getPartialPiece().setDownloaded(req.off);
|
|
|
|
rv.add(req);
|
|
|
|
}
|
2006-09-13 23:02:07 +00:00
|
|
|
}
|
2010-11-27 14:34:08 +00:00
|
|
|
outstandingRequests.clear();
|
|
|
|
pendingRequest = null;
|
|
|
|
lastRequest = null;
|
2010-11-26 00:44:00 +00:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return all pieces we are currently requesting, or empty Set
|
|
|
|
*/
|
2011-01-05 20:21:29 +00:00
|
|
|
synchronized private Set<Integer> getRequestedPieces() {
|
2010-11-26 00:44:00 +00:00
|
|
|
Set<Integer> rv = new HashSet(outstandingRequests.size() + 1);
|
|
|
|
for (Request req : outstandingRequests) {
|
2012-05-19 13:27:02 +00:00
|
|
|
rv.add(Integer.valueOf(req.getPiece()));
|
2010-11-26 00:44:00 +00:00
|
|
|
if (pendingRequest != null)
|
2012-05-19 13:27:02 +00:00
|
|
|
rv.add(Integer.valueOf(pendingRequest.getPiece()));
|
2010-11-26 00:44:00 +00:00
|
|
|
}
|
|
|
|
return rv;
|
2006-09-13 23:02:07 +00:00
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
void cancelMessage(int piece, int begin, int length)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Got cancel message ("
|
|
|
|
+ piece + ", " + begin + ", " + length + ")");
|
2005-10-19 22:02:37 +00:00
|
|
|
out.cancelRequest(piece, begin, length);
|
|
|
|
}
|
|
|
|
|
2010-11-21 21:19:12 +00:00
|
|
|
/** @since 0.8.2 */
|
|
|
|
void extensionMessage(int id, byte[] bs)
|
|
|
|
{
|
2012-02-18 17:58:54 +00:00
|
|
|
if (metainfo != null && metainfo.isPrivate() &&
|
|
|
|
(id == ExtensionHandler.ID_METADATA || id == ExtensionHandler.ID_PEX)) {
|
|
|
|
// shouldn't get this since we didn't advertise it but they could send it anyway
|
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Private torrent, ignoring ext msg " + id);
|
|
|
|
return;
|
|
|
|
}
|
2010-12-21 23:43:13 +00:00
|
|
|
ExtensionHandler.handleMessage(peer, listener, id, bs);
|
2010-12-21 03:04:10 +00:00
|
|
|
// Peer coord will get metadata from MagnetState,
|
|
|
|
// verify, and then call gotMetaInfo()
|
2010-12-19 15:37:11 +00:00
|
|
|
listener.gotExtension(peer, id, bs);
|
|
|
|
}
|
|
|
|
|
2010-12-21 03:04:10 +00:00
|
|
|
/**
|
2011-03-19 18:46:18 +00:00
|
|
|
* Switch from magnet mode to normal mode.
|
|
|
|
* If we already have the metainfo, this does nothing.
|
|
|
|
* @param meta non-null
|
2010-12-21 03:04:10 +00:00
|
|
|
* @since 0.8.4
|
|
|
|
*/
|
2010-12-21 23:43:13 +00:00
|
|
|
public void setMetaInfo(MetaInfo meta) {
|
2011-03-19 18:46:18 +00:00
|
|
|
if (metainfo != null)
|
|
|
|
return;
|
2010-12-21 23:43:13 +00:00
|
|
|
BitField oldBF = bitfield;
|
|
|
|
if (oldBF != null) {
|
|
|
|
if (oldBF.size() != meta.getPieces())
|
|
|
|
// fix bitfield, it was too big by 1-7 bits
|
|
|
|
bitfield = new BitField(oldBF.getFieldBytes(), meta.getPieces());
|
|
|
|
// else no extra
|
|
|
|
} else {
|
|
|
|
// it will be initialized later
|
|
|
|
//bitfield = new BitField(meta.getPieces());
|
|
|
|
}
|
|
|
|
metainfo = meta;
|
2011-03-19 18:46:18 +00:00
|
|
|
if (bitfield != null && bitfield.count() > 0)
|
2010-12-27 17:13:24 +00:00
|
|
|
setInteresting(true);
|
2010-12-21 03:04:10 +00:00
|
|
|
}
|
|
|
|
|
2012-06-02 18:52:46 +00:00
|
|
|
/**
|
|
|
|
* Unused
|
|
|
|
* @since 0.8.4
|
|
|
|
*/
|
2010-12-19 15:37:11 +00:00
|
|
|
void portMessage(int port)
|
|
|
|
{
|
2012-06-02 18:52:46 +00:00
|
|
|
// for compatibility with old DHT PORT message
|
|
|
|
listener.gotPort(peer, port, port + 1);
|
2010-11-21 21:19:12 +00:00
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
void unknownMessage(int type, byte[] bs)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.WARN))
|
|
|
|
_log.warn("Warning: Ignoring unknown message type: " + type
|
|
|
|
+ " length: " + bs.length);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2010-10-29 20:31:07 +00:00
|
|
|
/**
|
|
|
|
* We now have this piece.
|
|
|
|
* Tell the peer and cancel any requests for the piece.
|
|
|
|
*/
|
2005-10-19 22:02:37 +00:00
|
|
|
void havePiece(int piece)
|
|
|
|
{
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Tell " + peer + " havePiece(" + piece + ")");
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
// Tell the other side that we are no longer interested in any of
|
|
|
|
// the outstanding requests for this piece.
|
2010-10-29 20:31:07 +00:00
|
|
|
cancelPiece(piece);
|
|
|
|
|
|
|
|
// Tell the other side that we really have this piece.
|
|
|
|
out.sendHave(piece);
|
|
|
|
|
|
|
|
// Request something else if necessary.
|
|
|
|
addRequest();
|
|
|
|
|
2010-11-27 14:34:08 +00:00
|
|
|
/**** taken care of in addRequest()
|
2010-10-29 20:31:07 +00:00
|
|
|
synchronized(this)
|
|
|
|
{
|
|
|
|
// Is the peer still interesting?
|
|
|
|
if (lastRequest == null)
|
|
|
|
setInteresting(false);
|
|
|
|
}
|
2010-11-27 14:34:08 +00:00
|
|
|
****/
|
2010-10-29 20:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tell the other side that we are no longer interested in any of
|
|
|
|
* the outstanding requests (if any) for this piece.
|
|
|
|
* @since 0.8.1
|
|
|
|
*/
|
|
|
|
synchronized void cancelPiece(int piece) {
|
2012-05-19 13:27:02 +00:00
|
|
|
if (lastRequest != null && lastRequest.getPiece() == piece)
|
2005-10-19 22:02:37 +00:00
|
|
|
lastRequest = null;
|
|
|
|
|
2010-10-29 20:31:07 +00:00
|
|
|
Iterator<Request> it = outstandingRequests.iterator();
|
2005-10-19 22:02:37 +00:00
|
|
|
while (it.hasNext())
|
|
|
|
{
|
2010-10-29 20:31:07 +00:00
|
|
|
Request req = it.next();
|
2012-05-19 13:27:02 +00:00
|
|
|
if (req.getPiece() == piece)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
|
|
|
it.remove();
|
|
|
|
// Send cancel even when we are choked to make sure that it is
|
|
|
|
// really never ever send.
|
|
|
|
out.sendCancel(req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-01 14:35:01 +00:00
|
|
|
/**
|
|
|
|
* Are we currently requesting the piece?
|
2011-01-05 20:21:29 +00:00
|
|
|
* @deprecated deadlocks
|
2010-11-01 14:35:01 +00:00
|
|
|
* @since 0.8.1
|
|
|
|
*/
|
|
|
|
synchronized boolean isRequesting(int piece) {
|
2012-05-19 13:27:02 +00:00
|
|
|
if (pendingRequest != null && pendingRequest.getPiece() == piece)
|
2010-11-24 15:04:52 +00:00
|
|
|
return true;
|
2010-11-01 14:35:01 +00:00
|
|
|
for (Request req : outstandingRequests) {
|
2012-05-19 13:27:02 +00:00
|
|
|
if (req.getPiece() == piece)
|
2010-11-01 14:35:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-10-29 20:31:07 +00:00
|
|
|
/**
|
|
|
|
* Starts or resumes requesting pieces.
|
|
|
|
* @param resend should we resend outstanding requests?
|
|
|
|
*/
|
2010-10-27 13:29:27 +00:00
|
|
|
private void request(boolean resend)
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
|
|
|
// Are there outstanding requests that have to be resend?
|
|
|
|
if (resend)
|
|
|
|
{
|
2005-12-30 20:57:53 +00:00
|
|
|
synchronized (this) {
|
2010-11-27 14:34:08 +00:00
|
|
|
if (!outstandingRequests.isEmpty()) {
|
|
|
|
out.sendRequests(outstandingRequests);
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug("Resending requests to " + peer + outstandingRequests);
|
|
|
|
}
|
2005-12-30 20:57:53 +00:00
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add/Send some more requests if necessary.
|
|
|
|
addRequest();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a new request to the outstanding requests list.
|
2010-10-29 20:31:07 +00:00
|
|
|
* Then send interested if we weren't.
|
|
|
|
* Then send new requests if not choked.
|
|
|
|
* If nothing to request, send not interested if we were.
|
2010-11-27 14:34:08 +00:00
|
|
|
*
|
|
|
|
* This is called from several places:
|
|
|
|
*<pre>
|
|
|
|
* By getOustandingRequest() when the first part of a chunk comes in
|
|
|
|
* By havePiece() when somebody got a new piece completed
|
|
|
|
* By chokeMessage() when we receive an unchoke
|
|
|
|
* By setInteresting() when we are now interested
|
|
|
|
* By PeerCoordinator.updatePiecePriorities()
|
|
|
|
*</pre>
|
2005-10-19 22:02:37 +00:00
|
|
|
*/
|
2010-10-29 20:31:07 +00:00
|
|
|
synchronized void addRequest()
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
2010-11-28 04:01:20 +00:00
|
|
|
// no bitfield yet? nothing to request then.
|
|
|
|
if (bitfield == null)
|
|
|
|
return;
|
2010-12-20 00:05:03 +00:00
|
|
|
if (metainfo == null)
|
|
|
|
return;
|
2005-10-19 22:02:37 +00:00
|
|
|
boolean more_pieces = true;
|
|
|
|
while (more_pieces)
|
|
|
|
{
|
2006-09-29 23:54:17 +00:00
|
|
|
more_pieces = outstandingRequests.size() < MAX_PIPELINE;
|
2005-10-19 22:02:37 +00:00
|
|
|
// We want something and we don't have outstanding requests?
|
2010-11-27 14:34:08 +00:00
|
|
|
if (more_pieces && lastRequest == null) {
|
|
|
|
// we have nothing in the queue right now
|
|
|
|
if (!interesting) {
|
|
|
|
// If we need something, set interesting but delay pulling
|
|
|
|
// a request from the PeerCoordinator until unchoked.
|
|
|
|
if (listener.needPiece(this.peer, bitfield)) {
|
|
|
|
setInteresting(true);
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " addRequest() we need something, setting interesting, delaying requestNextPiece()");
|
|
|
|
} else {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " addRequest() needs nothing");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (choked) {
|
|
|
|
// If choked, delay pulling
|
|
|
|
// a request from the PeerCoordinator until unchoked.
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " addRequest() we are choked, delaying requestNextPiece()");
|
|
|
|
return;
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
more_pieces = requestNextPiece();
|
2010-11-27 14:34:08 +00:00
|
|
|
} else if (more_pieces) // We want something
|
2005-10-19 22:02:37 +00:00
|
|
|
{
|
|
|
|
int pieceLength;
|
|
|
|
boolean isLastChunk;
|
2012-05-19 13:27:02 +00:00
|
|
|
pieceLength = metainfo.getPieceLength(lastRequest.getPiece());
|
2006-09-29 23:54:17 +00:00
|
|
|
isLastChunk = lastRequest.off + lastRequest.len == pieceLength;
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
// Last part of a piece?
|
|
|
|
if (isLastChunk)
|
|
|
|
more_pieces = requestNextPiece();
|
|
|
|
else
|
|
|
|
{
|
2012-05-19 13:27:02 +00:00
|
|
|
PartialPiece nextPiece = lastRequest.getPartialPiece();
|
2005-10-19 22:02:37 +00:00
|
|
|
int nextBegin = lastRequest.off + PARTSIZE;
|
|
|
|
int maxLength = pieceLength - nextBegin;
|
|
|
|
int nextLength = maxLength > PARTSIZE ? PARTSIZE
|
|
|
|
: maxLength;
|
|
|
|
Request req
|
2012-05-19 13:27:02 +00:00
|
|
|
= new Request(nextPiece,nextBegin, nextLength);
|
2005-10-19 22:02:37 +00:00
|
|
|
outstandingRequests.add(req);
|
|
|
|
if (!choked)
|
|
|
|
out.sendRequest(req);
|
|
|
|
lastRequest = req;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-27 14:34:08 +00:00
|
|
|
// failsafe
|
|
|
|
if (interesting && lastRequest == null && outstandingRequests.isEmpty())
|
|
|
|
setInteresting(false);
|
|
|
|
|
2005-12-19 13:34:52 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " requests " + outstandingRequests);
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
|
2010-07-05 14:20:34 +00:00
|
|
|
/**
|
|
|
|
* Starts requesting first chunk of next piece. Returns true if
|
|
|
|
* something has been added to the requests, false otherwise.
|
2010-10-27 13:29:27 +00:00
|
|
|
* Caller should synchronize.
|
2010-07-05 14:20:34 +00:00
|
|
|
*/
|
2005-10-19 22:02:37 +00:00
|
|
|
private boolean requestNextPiece()
|
|
|
|
{
|
|
|
|
// Check that we already know what the other side has.
|
2010-11-27 14:34:08 +00:00
|
|
|
if (bitfield != null) {
|
2006-09-06 06:32:53 +00:00
|
|
|
// Check for adopting an orphaned partial piece
|
2010-11-26 00:44:00 +00:00
|
|
|
PartialPiece pp = listener.getPartialPiece(peer, bitfield);
|
|
|
|
if (pp != null) {
|
|
|
|
// Double-check that r not already in outstandingRequests
|
|
|
|
if (!getRequestedPieces().contains(Integer.valueOf(pp.getPiece()))) {
|
|
|
|
Request r = pp.getRequest();
|
2006-09-24 18:30:22 +00:00
|
|
|
outstandingRequests.add(r);
|
|
|
|
if (!choked)
|
|
|
|
out.sendRequest(r);
|
|
|
|
lastRequest = r;
|
|
|
|
return true;
|
2010-11-26 00:44:00 +00:00
|
|
|
}
|
2006-09-06 06:32:53 +00:00
|
|
|
}
|
2010-11-24 15:04:52 +00:00
|
|
|
|
2010-11-27 14:34:08 +00:00
|
|
|
/******* getPartialPiece() does it all now
|
2010-11-24 15:04:52 +00:00
|
|
|
// Note that in addition to the bitfield, PeerCoordinator uses
|
|
|
|
// its request tracking and isRequesting() to determine
|
|
|
|
// what piece to give us next.
|
2005-10-19 22:02:37 +00:00
|
|
|
int nextPiece = listener.wantPiece(peer, bitfield);
|
2010-10-30 15:28:29 +00:00
|
|
|
if (nextPiece != -1
|
2012-05-19 13:27:02 +00:00
|
|
|
&& (lastRequest == null || lastRequest.getPiece() != nextPiece)) {
|
2010-10-30 15:28:29 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " want piece " + nextPiece);
|
2010-07-05 14:20:34 +00:00
|
|
|
// Fail safe to make sure we are interested
|
|
|
|
// When we transition into the end game we may not be interested...
|
|
|
|
if (!interesting) {
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " transition to end game, setting interesting");
|
|
|
|
interesting = true;
|
|
|
|
out.sendInterest(true);
|
|
|
|
}
|
|
|
|
|
2005-10-19 22:02:37 +00:00
|
|
|
int piece_length = metainfo.getPieceLength(nextPiece);
|
2007-01-14 19:49:33 +00:00
|
|
|
//Catch a common place for OOMs esp. on 1MB pieces
|
|
|
|
byte[] bs;
|
|
|
|
try {
|
|
|
|
bs = new byte[piece_length];
|
|
|
|
} catch (OutOfMemoryError oom) {
|
|
|
|
_log.warn("Out of memory, can't request piece " + nextPiece, oom);
|
|
|
|
return false;
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
|
|
|
|
int length = Math.min(piece_length, PARTSIZE);
|
|
|
|
Request req = new Request(nextPiece, bs, 0, length);
|
|
|
|
outstandingRequests.add(req);
|
|
|
|
if (!choked)
|
|
|
|
out.sendRequest(req);
|
|
|
|
lastRequest = req;
|
|
|
|
return true;
|
2010-11-27 14:34:08 +00:00
|
|
|
} else {
|
2010-10-30 15:28:29 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " no more pieces to request");
|
2010-11-27 14:34:08 +00:00
|
|
|
}
|
|
|
|
*******/
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
|
2010-10-30 15:28:29 +00:00
|
|
|
// failsafe
|
|
|
|
if (outstandingRequests.isEmpty())
|
|
|
|
lastRequest = null;
|
|
|
|
|
2010-10-27 13:29:27 +00:00
|
|
|
// If we are not in the end game, we may run out of things to request
|
|
|
|
// because we are asking other peers. Set not-interesting now rather than
|
|
|
|
// wait for those other requests to be satisfied via havePiece()
|
|
|
|
if (interesting && lastRequest == null) {
|
|
|
|
interesting = false;
|
|
|
|
out.sendInterest(false);
|
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " nothing more to request, now uninteresting");
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized void setInteresting(boolean interest)
|
|
|
|
{
|
|
|
|
if (interest != interesting)
|
|
|
|
{
|
2010-11-27 14:34:08 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " setInteresting(" + interest + ")");
|
2005-10-19 22:02:37 +00:00
|
|
|
interesting = interest;
|
|
|
|
out.sendInterest(interest);
|
|
|
|
|
|
|
|
if (interesting && !choked)
|
2010-10-27 13:29:27 +00:00
|
|
|
request(true); // we shouldnt have any pending requests, but if we do, resend them
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized void setChoking(boolean choke)
|
|
|
|
{
|
|
|
|
if (choking != choke)
|
|
|
|
{
|
2010-11-27 14:34:08 +00:00
|
|
|
if (_log.shouldLog(Log.DEBUG))
|
|
|
|
_log.debug(peer + " setChoking(" + choke + ")");
|
2005-10-19 22:02:37 +00:00
|
|
|
choking = choke;
|
|
|
|
out.sendChoke(choke);
|
|
|
|
}
|
|
|
|
}
|
2006-09-06 06:32:53 +00:00
|
|
|
|
2006-09-07 23:03:18 +00:00
|
|
|
void keepAlive()
|
2006-09-06 06:32:53 +00:00
|
|
|
{
|
|
|
|
out.sendAlive();
|
|
|
|
}
|
2006-09-16 21:07:28 +00:00
|
|
|
|
|
|
|
synchronized void retransmitRequests()
|
|
|
|
{
|
|
|
|
if (interesting && !choked)
|
|
|
|
out.retransmitRequests(outstandingRequests);
|
|
|
|
}
|
2010-10-27 13:29:27 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* debug
|
|
|
|
* @return string or null
|
|
|
|
* @since 0.8.1
|
|
|
|
*/
|
|
|
|
synchronized String getRequests() {
|
|
|
|
if (outstandingRequests.isEmpty())
|
|
|
|
return null;
|
|
|
|
else
|
|
|
|
return outstandingRequests.toString();
|
|
|
|
}
|
2005-10-19 22:02:37 +00:00
|
|
|
}
|