Package freenet.node

Source Code of freenet.node.CHKInsertHandler$DataReceiver

/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.node;

import static java.util.concurrent.TimeUnit.SECONDS;

import freenet.io.comm.ByteCounter;
import freenet.io.comm.DMT;
import freenet.io.comm.DisconnectedException;
import freenet.io.comm.Message;
import freenet.io.comm.MessageFilter;
import freenet.io.comm.NotConnectedException;
import freenet.io.comm.PeerContext;
import freenet.io.comm.RetrievalException;
import freenet.io.comm.SlowAsyncMessageFilterCallback;
import freenet.io.xfer.AbortedException;
import freenet.io.xfer.BlockReceiver;
import freenet.io.xfer.BlockReceiver.BlockReceiverCompletion;
import freenet.io.xfer.BlockReceiver.BlockReceiverTimeoutHandler;
import freenet.io.xfer.PartiallyReceivedBlock;
import freenet.keys.CHKBlock;
import freenet.keys.CHKVerifyException;
import freenet.keys.NodeCHK;
import freenet.store.KeyCollisionException;
import freenet.support.HexUtil;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.ShortBuffer;
import freenet.support.Logger.LogLevel;
import freenet.support.io.NativeThread;

/**
* @author amphibian
*
* Handle an incoming insert request.
* This corresponds to RequestHandler.
*/
public class CHKInsertHandler implements PrioRunnable, ByteCounter {
  private static volatile boolean logMINOR;

  static {
    Logger.registerLogThresholdCallback(new LogThresholdCallback(){
      @Override
      public void shouldUpdate(){
        logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
      }
    });
  }

    static final long DATA_INSERT_TIMEOUT = SECONDS.toMillis(10);

    final Node node;
    final long uid;
    final PeerNode source;
    final NodeCHK key;
    final long startTime;
    private short htl;
    private CHKInsertSender sender;
    private byte[] headers;
    private BlockReceiver br;
    private Thread runThread;
    PartiallyReceivedBlock prb;
    final InsertTag tag;
    private boolean canWriteDatastore;
  private final boolean forkOnCacheable;
  private final boolean preferInsert;
  private final boolean ignoreLowBackoff;
  private final boolean realTimeFlag;

    CHKInsertHandler(NodeCHK key, short htl, PeerNode source, long id, Node node, long startTime, InsertTag tag, boolean forkOnCacheable, boolean preferInsert, boolean ignoreLowBackoff, boolean realTimeFlag) {
        this.node = node;
        this.uid = id;
        this.source = source;
        this.startTime = startTime;
        this.tag = tag;
        this.key = key;
        this.htl = htl;
        canWriteDatastore = node.canWriteDatastoreInsert(htl);
        this.forkOnCacheable = forkOnCacheable;
        this.preferInsert = preferInsert;
        this.ignoreLowBackoff = ignoreLowBackoff;
        this.realTimeFlag = realTimeFlag;
    }
   
    @Override
  public String toString() {
        return super.toString()+" for "+uid;
    }
   
    @Override
    public void run() {
      freenet.support.Logger.OSThread.logPID(this);
        try {
            realRun();
        } catch (Throwable t) {
            Logger.error(this, "Caught in run() "+t, t);
            tag.handlerThrew(t);
        } finally {
          if(logMINOR) Logger.minor(this, "Exiting CHKInsertHandler.run() for "+uid);
          tag.unlockHandler();
        }
    }

    private void realRun() {
        runThread = Thread.currentThread();
       
        // FIXME implement rate limiting or something!
        // Send Accepted
        Message accepted = DMT.createFNPAccepted(uid);
        try {
      //Using sendSync here will help the next message filter not timeout... wait here or at the message filter.
      source.sendSync(accepted, this, realTimeFlag);
    } catch (NotConnectedException e1) {
      if(logMINOR) Logger.minor(this, "Lost connection to source");
      return;
    } catch (SyncSendWaitedTooLongException e) {
      Logger.error(this, "Unable to send "+accepted+" in a reasonable time to "+source);
      return;
    }
   
    if(tag.shouldSlowDown()) {
      try {
        source.sendAsync(DMT.createFNPRejectedOverload(uid, false, false, realTimeFlag), null, this);
      } catch (NotConnectedException e) {
        // Ignore.
      }
    }
   
        // Source will send us a DataInsert
       
        MessageFilter mf;
        mf = makeDataInsertFilter(DATA_INSERT_TIMEOUT);
       
        Message msg;
        try {
            msg = node.usm.waitFor(mf, this);
        } catch (DisconnectedException e) {
            Logger.normal(this, "Disconnected while waiting for DataInsert on "+uid);
            return;
        }
       
        if(logMINOR) Logger.minor(this, "Received "+msg);
       
        if(msg == null) {
          handleNoDataInsert();
          return;
        }
       
        if(msg.getSpec() == DMT.FNPDataInsertRejected) {
          try {
        source.sendAsync(DMT.createFNPDataInsertRejected(uid, msg.getShort(DMT.DATA_INSERT_REJECTED_REASON)), null, this);
      } catch (NotConnectedException e) {
        // Ignore.
      }
          return;
        }
       
        // We have a DataInsert
        headers = ((ShortBuffer)msg.getObject(DMT.BLOCK_HEADERS)).getData();
        // FIXME check the headers
       
        // Now create an CHKInsertSender, or use an existing one, or
        // discover that the data is in the store.

        // From this point onwards, if we return cleanly we must go through finish().
       
        prb = new PartiallyReceivedBlock(Node.PACKETS_IN_BLOCK, Node.PACKET_SIZE);
        if(htl > 0)
            sender = node.makeInsertSender(key, htl, uid, tag, source, headers, prb, false, false, forkOnCacheable, preferInsert, ignoreLowBackoff, realTimeFlag);
        br = new BlockReceiver(node.usm, source, uid, prb, this, node.getTicker(), false, realTimeFlag, myTimeoutHandler, false);
       
        // Receive the data, off thread
        Runnable dataReceiver = new DataReceiver();
    receiveStarted = true;
        node.executor.execute(dataReceiver, "CHKInsertHandler$DataReceiver for UID "+uid);

        // Wait...
        // What do we want to wait for?
        // If the data receive completes, that's very nice,
        // but doesn't really matter. What matters is what
        // happens to the CHKInsertSender. If the data receive
        // fails, that does matter...
       
        // We are waiting for a terminal status on the CHKInsertSender,
        // including SUCCESS.
        // If we get transfer failed, we can check whether the receive
        // failed first. If it did it's not our fault.
        // If the receive failed, and we haven't started transferring
        // yet, we probably want to kill the sender.
        // So we call the wait method on the CHKInsertSender, but we
        // also have a flag locally to indicate the receive failed.
        // And if it does, we interrupt.
       
        boolean receivedRejectedOverload = false;
       
        while(true) {
            synchronized(sender) {
                try {
                  if(sender.getStatus() == CHKInsertSender.NOT_FINISHED)
                    sender.wait(5000);
                } catch (InterruptedException e) {
                    // Cool, probably this is because the receive failed...
                }
            }
            if(receiveFailed()) {
                // Nothing else we can do
                finish(CHKInsertSender.RECEIVE_FAILED);
                return;
            }
           
            if((!receivedRejectedOverload) && sender.receivedRejectedOverload()) {
              receivedRejectedOverload = true;
              // Forward it
              // Does not need to be sent synchronously since is non-terminal.
              Message m = DMT.createFNPRejectedOverload(uid, false, true, realTimeFlag);
              try {
          source.sendAsync(m, null, this);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        }
            }
           
            int status = sender.getStatus();
           
            if(status == CHKInsertSender.NOT_FINISHED) {
                continue;
            }

            // Local RejectedOverload's (fatal).
            // Internal error counts as overload. It'd only create a timeout otherwise, which is the same thing anyway.
            if((status == CHKInsertSender.TIMED_OUT) ||
                (status == CHKInsertSender.GENERATED_REJECTED_OVERLOAD) ||
                (status == CHKInsertSender.INTERNAL_ERROR)) {
                msg = DMT.createFNPRejectedOverload(uid, true, true, realTimeFlag);
                try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to "+source);
          return;
        }
                // Might as well store it anyway.
                if((status == CHKInsertSender.TIMED_OUT) ||
                    (status == CHKInsertSender.GENERATED_REJECTED_OVERLOAD))
                  canCommit = true;
                finish(status);
                return;
            }
           
            if((status == CHKInsertSender.ROUTE_NOT_FOUND) || (status == CHKInsertSender.ROUTE_REALLY_NOT_FOUND)) {
                msg = DMT.createFNPRouteNotFound(uid, sender.getHTL());
                try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          if(logMINOR) Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to "+source);
          return;
        }
                canCommit = true;
                finish(status);
                return;
            }
           
            if(status == CHKInsertSender.RECEIVE_FAILED) {
              // Probably source's fault.
              finish(status);
              return;
            }
           
            if(status == CHKInsertSender.SUCCESS) {
              msg = DMT.createFNPInsertReply(uid);
              try {
          source.sendSync(msg, this, realTimeFlag);
        } catch (NotConnectedException e) {
          Logger.minor(this, "Lost connection to source");
          return;
        } catch (SyncSendWaitedTooLongException e) {
          Logger.error(this, "Took too long to send "+msg+" to "+source);
          return;
        }
                canCommit = true;
                finish(status);
                return;
            }
           
            // Otherwise...?
            Logger.error(this, "Unknown status code: "+sender.getStatusString());
            msg = DMT.createFNPRejectedOverload(uid, true, true, realTimeFlag);
            try {
        source.sendSync(msg, this, realTimeFlag);
      } catch (NotConnectedException e) {
        // Ignore
      } catch (SyncSendWaitedTooLongException e) {
        // Ignore
      }
            finish(CHKInsertSender.INTERNAL_ERROR);
            return;
        }
  }

  private MessageFilter makeDataInsertFilter(long timeout) {
      MessageFilter mfDataInsert = MessageFilter.create().setType(DMT.FNPDataInsert).setField(DMT.UID, uid).setSource(source).setTimeout(timeout);
      // DataInsertRejected means the transfer failed upstream so a DataInsert will not be sent.
      MessageFilter mfDataInsertRejected = MessageFilter.create().setType(DMT.FNPDataInsertRejected).setField(DMT.UID, uid).setSource(source).setTimeout(timeout);
      return mfDataInsert.or(mfDataInsertRejected);
  }

  private void handleNoDataInsert() {
      try {
        // Nodes wait until they have the DataInsert before forwarding, so there is absolutely no excuse: There is a local problem here!
        if(source.isConnected() && (startTime > (source.timeLastConnectionCompleted()+Node.HANDSHAKE_TIMEOUT*4)))
          Logger.warning(this, "Did not receive DataInsert on "+uid+" from "+source+" !");
        Message tooSlow = DMT.createFNPRejectedTimeout(uid);
        source.sendAsync(tooSlow, null, this);
        Message m = DMT.createFNPInsertTransfersCompleted(uid, true);
        source.sendAsync(m, null, this);
        prb = new PartiallyReceivedBlock(Node.PACKETS_IN_BLOCK, Node.PACKET_SIZE);
        br = new BlockReceiver(node.usm, source, uid, prb, this, node.getTicker(), false, realTimeFlag, null, false);
        prb.abort(RetrievalException.NO_DATAINSERT, "No DataInsert", true);
        source.localRejectedOverload("TimedOutAwaitingDataInsert", realTimeFlag);
       
        // Two stage timeout. Don't go fatal unless no response in 60 seconds.
        // Yes it's ugly everywhere but since we have a longish connection timeout it's necessary everywhere. :|
        // FIXME review two stage timeout everywhere with some low level networking guru.
        MessageFilter mf = makeDataInsertFilter(SECONDS.toMillis(60));
        node.usm.addAsyncFilter(mf, new SlowAsyncMessageFilterCallback() {

          @Override
          public void onMatched(Message m) {
            // Okay, great.
            // Either we got a DataInsert, in which case the transfer was aborted above, or we got a DataInsertRejected, which means it never started.
            // FIXME arguably we should wait until we have the message before sending the transfer cancel in case the message gets lost? Or maybe not?
            // FIXME unlock here rather than in finally block in realRun??? Unlocking in the finally block is safe (won't cause rejects), unlocking here might be more accurate ...
          }

          @Override
          public boolean shouldTimeout() {
            return false;
          }

          @Override
          public void onTimeout() {
            Logger.error(this, "No DataInsert for "+CHKInsertHandler.this+" from "+source+" ("+source.getVersionNumber()+")");
            // Fatal timeout. Something is seriously busted.
            // We've waited long enough that we know it's not just a connectivity problem - if it was we'd have disconnected by now.
              source.fatalTimeout();
          }

          @Override
          public void onDisconnect(PeerContext ctx) {
            // Okay. Somewhat expected, it was having problems.
          }

          @Override
          public void onRestarted(PeerContext ctx) {
            // Okay.
          }

          @Override
          public int getPriority() {
            return NativeThread.NORM_PRIORITY;
          }
         
        }, this);
        return;
      } catch (NotConnectedException e) {
        if(logMINOR) Logger.minor(this, "Lost connection to source");
      return;
      } catch (DisconnectedException e) {
        if(logMINOR) Logger.minor(this, "Lost connection to source");
      return;
    }
  }

  private boolean canCommit = false;
    private boolean sentCompletion = false;
    private Object sentCompletionLock = new Object();
   
    /**
     * If canCommit, and we have received all the data, and it
     * verifies, then commit it.
     */
    private void finish(int code) {
      if(logMINOR) Logger.minor(this, "Waiting for receive");
      long transferTimeout = realTimeFlag ?
          CHKInsertSender.TRANSFER_COMPLETION_ACK_TIMEOUT_REALTIME :
            CHKInsertSender.TRANSFER_COMPLETION_ACK_TIMEOUT_BULK;
    synchronized(this) {
      while(receiveStarted && !receiveCompleted) {
        try {
          wait(SECONDS.toMillis(100));
        } catch (InterruptedException e) {
          // Ignore
        }
      }
      }
   
    CHKBlock block = verify();
    // If we wanted to reduce latency at the cost of security (bug 3338), we'd commit here, or even on the receiver thread.
   
        // Wait for completion
        boolean sentCompletionWasSet;
        synchronized(sentCompletionLock) {
          sentCompletionWasSet = sentCompletion;
          sentCompletion = true;
        }
       
    Message m=null;
   
    boolean routingTookTooLong = false;
        if((sender != null) && (!sentCompletionWasSet)) {
            if(logMINOR) Logger.minor(this, "Waiting for completion");
            long startedTime = System.currentTimeMillis();
      //If there are downstream senders, our final success report depends on there being no timeouts in the chain.
          while(true) {
            synchronized(sender) {
              if(sender.completed()) {
                break;
              }
              try {
                int t = (int)Math.min(Integer.MAX_VALUE, startedTime + transferTimeout - System.currentTimeMillis());
                if(t > 0) sender.wait(t);
                else {
                  routingTookTooLong = true;
                  break;
                }
              } catch (InterruptedException e) {
                // Loop
              }
            }
          }
          if(routingTookTooLong) {
            tag.timedOutToHandlerButContinued();
            sentCompletionWasSet = true;
            try {
              source.sendAsync(DMT.createFNPInsertTransfersCompleted(uid, true), null, this);
            } catch (NotConnectedException e) {
              // Ignore.
            }
           
            Logger.error(this, "Insert took too long, telling downstream that it's finished and reassigning to self on "+this);
           
            // Still waiting.
            while(true) {
              synchronized(sender) {
                if(sender.completed()) {
                  break;
                }
                try {
                  sender.wait(SECONDS.toMillis(10));
                } catch (InterruptedException e) {
                  // Loop
                }
              }
            }
            if(logMINOR) Logger.minor(this, "Completed after telling downstream on "+this);
          }
          boolean failed = sender.anyTransfersFailed();
          if(!sentCompletionWasSet)
            m = DMT.createFNPInsertTransfersCompleted(uid, failed);
    }
   
    if((sender == null) && (!sentCompletionWasSet) && (canCommit)) {
      //There are no downstream senders, but we stored the data locally, report successful transfer.
      //Note that this is done even if the verify fails.
      m = DMT.createFNPInsertTransfersCompleted(uid, false /* no timeouts */);
    }   
   
    // Don't commit until after we have received all the downstream transfer completion notifications.
    // We don't want an attacker to see a ULPR notice from the inserter before he sees it from the end of the chain (bug 3338).
    if(block != null) {
      commit(block);
      block = null;
    }
   
    // Be generous with unlocking incoming requests, and cautious with
    // unlocking outgoing requests, hence avoid problems. If we wait until
    // the completion has been acknowledged, then there will be a period
    // during which the originator thinks we have unlocked but we haven't,
    // which will cause unnecessary rejects and thus mandatory backoff.
      tag.unlockHandler();
       
      if(m != null) {
          try {
            // We do need to sendSync here so we have accurate byte counter totals.
            source.sendSync(m, this, realTimeFlag);
            if(logMINOR) Logger.minor(this, "Sent completion: "+m+" for "+this);
          } catch (NotConnectedException e1) {
            if(logMINOR) Logger.minor(this, "Not connected: "+source+" for "+this);
            // May need to commit anyway...
          } catch (SyncSendWaitedTooLongException e) {
            Logger.error(this, "Took too long to send "+m+" to "+source);
            // May need to commit anyway...
      }
      }
           
        if(code != CHKInsertSender.TIMED_OUT && code != CHKInsertSender.GENERATED_REJECTED_OVERLOAD &&
            code != CHKInsertSender.INTERNAL_ERROR && code != CHKInsertSender.ROUTE_REALLY_NOT_FOUND &&
            code != CHKInsertSender.RECEIVE_FAILED && !receiveFailed()) {
          int totalSent = getTotalSentBytes();
          int totalReceived = getTotalReceivedBytes();
          if(sender != null) {
            totalSent += sender.getTotalSentBytes();
            totalReceived += sender.getTotalReceivedBytes();
          }
          if(logMINOR) Logger.minor(this, "Remote CHK insert cost "+totalSent+ '/' +totalReceived+" bytes ("+code+ ") receive failed = "+receiveFailed());
          node.nodeStats.remoteChkInsertBytesSentAverage.report(totalSent);
          node.nodeStats.remoteChkInsertBytesReceivedAverage.report(totalReceived);
          if(code == CHKInsertSender.SUCCESS) {
            // Report both sent and received because we have both a Handler and a Sender
            if(sender != null && sender.startedSendingData())
              node.nodeStats.successfulChkInsertBytesSentAverage.report(totalSent);
            node.nodeStats.successfulChkInsertBytesReceivedAverage.report(totalReceived);
          }
        }
    }
   
    /**
     * Verify data, or send DataInsertRejected.
     */
    private CHKBlock verify() {
        Message toSend = null;
       
        CHKBlock block = null;
       
        synchronized(this) {
          if((prb == null) || prb.isAborted()) return null;
            try {
                if(!canCommit) return null;
                if(!prb.allReceived()) return null;
                block = new CHKBlock(prb.getBlock(), headers, key);
            } catch (CHKVerifyException e) {
              Logger.error(this, "Verify failed in CHKInsertHandler: "+e+" - headers: "+HexUtil.bytesToHex(headers), e);
                toSend = DMT.createFNPDataInsertRejected(uid, DMT.DATA_INSERT_REJECTED_VERIFY_FAILED);
            } catch (AbortedException e) {
              Logger.error(this, "Receive failed: "+e);
              // Receiver thread (below) will handle sending the failure notice
            }
        }
        if(toSend != null) {
            try {
                source.sendAsync(toSend, null, this);
            } catch (NotConnectedException e) {
                // :(
              if(logMINOR) Logger.minor(this, "Lost connection in "+this+" when sending FNPDataInsertRejected");
            }
        }
        return block;
  }
   
    private void commit(CHKBlock block) {
        try {
      node.store(block, node.shouldStoreDeep(key, source, sender == null ? new PeerNode[0] : sender.getRoutedTo()), false, canWriteDatastore, false);
    } catch (KeyCollisionException e) {
      // Impossible with CHKs.
    }
        if(logMINOR) Logger.minor(this, "Committed");
    }

  /** Has the receive failed? If so, there's not much more that can be done... */
    private boolean receiveFailed;
   
    private boolean receiveStarted;
    private boolean receiveCompleted;

    public class DataReceiver implements PrioRunnable {

        @Override
        public void run() {
        freenet.support.Logger.OSThread.logPID(this);
          if(logMINOR) Logger.minor(this, "Receiving data for "+CHKInsertHandler.this);
          // Don't log whether the transfer succeeded or failed as the transfer was initiated by the source therefore could be unreliable evidence.
          br.receive(new BlockReceiverCompletion() {
           
            @Override
            public void blockReceived(byte[] buf) {
              if(logMINOR) Logger.minor(this, "Received data for "+CHKInsertHandler.this);
              synchronized(CHKInsertHandler.this) {
                receiveCompleted = true;
                CHKInsertHandler.this.notifyAll();
              }
             node.nodeStats.successfulBlockReceive(realTimeFlag, false);
            }

            @Override
            public void blockReceiveFailed(RetrievalException e) {
              synchronized(CHKInsertHandler.this) {
                receiveCompleted = true;
                receiveFailed = true;
                CHKInsertHandler.this.notifyAll();
              }
              // Cancel the sender
              if(sender != null)
                sender.onReceiveFailed(); // tell it to stop if it hasn't already failed... unless it's sending from store
              runThread.interrupt();
              tag.timedOutToHandlerButContinued(); // sender is finished, or will be very soon; we may however be waiting for the sendAborted downstream.
              Message msg = DMT.createFNPDataInsertRejected(uid, DMT.DATA_INSERT_REJECTED_RECEIVE_FAILED);
              try {
                source.sendSync(msg, CHKInsertHandler.this, realTimeFlag);
              } catch (NotConnectedException ex) {
                //If they are not connected, that's probably why the receive failed!
                if (logMINOR) Logger.minor(this, "Can't send "+msg+" to "+source+": "+ex);
              } catch (SyncSendWaitedTooLongException ex) {
                Logger.error(this, "Took too long to send "+msg+" to "+source);
          }
              if (e.getReason()==RetrievalException.SENDER_DISCONNECTED)
                Logger.normal(this, "Failed to retrieve (disconnect): "+e+" for "+CHKInsertHandler.this, e);
              else
                // Annoying, but we have stats for this; no need to call attention to it, it's unlikely to be a bug.
                Logger.normal(this, "Failed to retrieve ("+e.getReason()+"/"+RetrievalException.getErrString(e.getReason())+"): "+e+" for "+CHKInsertHandler.this, e);
             
              if(!prb.abortedLocally())
                node.nodeStats.failedBlockReceive(false, false, realTimeFlag, false);
              return;
            }
           
          });
        }

        @Override
    public String toString() {
          return super.toString()+" for "+uid;
        }

    @Override
    public int getPriority() {
      return NativeThread.HIGH_PRIORITY;
    }
       
    }

    private synchronized boolean receiveFailed() {
      return receiveFailed;
    }
   
    private final Object totalSync = new Object();
    private int totalSentBytes;
    private int totalReceivedBytes;
   
  @Override
  public void sentBytes(int x) {
    synchronized(totalSync) {
      totalSentBytes += x;
    }
    node.nodeStats.insertSentBytes(false, x);
  }

  @Override
  public void receivedBytes(int x) {
    synchronized(totalSync) {
      totalReceivedBytes += x;
    }
    node.nodeStats.insertReceivedBytes(false, x);
  }

  public int getTotalSentBytes() {
    return totalSentBytes;
  }
 
  public int getTotalReceivedBytes() {
    return totalReceivedBytes;
  }

  @Override
  public void sentPayload(int x) {
    node.sentPayload(x);
    node.nodeStats.insertSentBytes(false, -x);
  }

  @Override
  public int getPriority() {
    return NativeThread.HIGH_PRIORITY;
  }
 
  private BlockReceiverTimeoutHandler myTimeoutHandler = new BlockReceiverTimeoutHandler() {

    /** We timed out waiting for a block from the request sender. We do not know
     * whether it is the fault of the request sender or that of some previous node.
     * The PRB will be cancelled, resulting in all outgoing transfers for this insert
     * being cancelled quickly. If the problem occurred on a previous node, we will
     * receive a cancel. So we are consistent with the nodes we routed to, and it is
     * safe to wait for the node that routed to us to send an explicit cancel. We do
     * not need to do anything yet. */
    @Override
    public void onFirstTimeout() {
      // Do nothing.
    }

    /** We timed out, and the sender did not send us a timeout message, even after we
     * told it we were cancelling. Hence, we know that it was at fault. We need to
     * take action against it.
     */
    @Override
    public void onFatalTimeout(PeerContext receivingFrom) {
      Logger.error(this, "Fatal timeout receiving insert "+CHKInsertHandler.this+" from "+receivingFrom);
      ((PeerNode)receivingFrom).fatalTimeout();
    }
   
  };
   
}
TOP

Related Classes of freenet.node.CHKInsertHandler$DataReceiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.