Author: toad
Date: 2006-01-18 16:38:29 +0000 (Wed, 18 Jan 2006)
New Revision: 7873
Added:
trunk/freenet/src/freenet/node/fcp/
trunk/freenet/src/freenet/node/fcp/AllDataMessage.java
trunk/freenet/src/freenet/node/fcp/ClientGet.java
trunk/freenet/src/freenet/node/fcp/ClientGetMessage.java
trunk/freenet/src/freenet/node/fcp/ClientHelloMessage.java
trunk/freenet/src/freenet/node/fcp/ClientRequest.java
trunk/freenet/src/freenet/node/fcp/DataCarryingMessage.java
trunk/freenet/src/freenet/node/fcp/DataFoundMessage.java
trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
trunk/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
trunk/freenet/src/freenet/node/fcp/FCPConnectionOutputHandler.java
trunk/freenet/src/freenet/node/fcp/FCPMessage.java
trunk/freenet/src/freenet/node/fcp/FCPServer.java
trunk/freenet/src/freenet/node/fcp/FetchErrorMessage.java
trunk/freenet/src/freenet/node/fcp/IdentifierCollisionMessage.java
trunk/freenet/src/freenet/node/fcp/MessageInvalidException.java
trunk/freenet/src/freenet/node/fcp/NodeHelloMessage.java
trunk/freenet/src/freenet/node/fcp/ProtocolErrorMessage.java
trunk/freenet/src/freenet/support/io/LineReader.java
Modified:
trunk/freenet/src/freenet/client/BlockFetcher.java
trunk/freenet/src/freenet/client/FailureCodeTracker.java
trunk/freenet/src/freenet/client/FetchException.java
trunk/freenet/src/freenet/client/Fetcher.java
trunk/freenet/src/freenet/client/FetcherContext.java
trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
trunk/freenet/src/freenet/client/Segment.java
trunk/freenet/src/freenet/client/SplitFetcher.java
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/node/QueuedDataRequest.java
trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
trunk/freenet/src/freenet/node/RequestHandler.java
trunk/freenet/src/freenet/node/RequestStarterClient.java
trunk/freenet/src/freenet/node/SimpleLowLevelClient.java
trunk/freenet/src/freenet/node/TextModeClientInterface.java
trunk/freenet/src/freenet/node/Version.java
trunk/freenet/src/freenet/support/BucketTools.java
trunk/freenet/src/freenet/support/SimpleFieldSet.java
trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
Log:
361: Basic FCP support (request only). Also lots of bug fixes.
Modified: trunk/freenet/src/freenet/client/BlockFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/BlockFetcher.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/client/BlockFetcher.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -60,37 +60,11 @@
fatalError(e, FetchException.INVALID_METADATA);
} catch (FetchException e) {
int code = e.getMode();
- switch(code) {
- case FetchException.ARCHIVE_FAILURE:
- case FetchException.BLOCK_DECODE_ERROR:
- case FetchException.HAS_MORE_METASTRINGS:
- case FetchException.INVALID_METADATA:
- case FetchException.NOT_IN_ARCHIVE:
- case FetchException.TOO_DEEP_ARCHIVE_RECURSION:
- case FetchException.TOO_MANY_ARCHIVE_RESTARTS:
- case FetchException.TOO_MANY_METADATA_LEVELS:
- case FetchException.TOO_MANY_REDIRECTS:
- case FetchException.TOO_MUCH_RECURSION:
- case FetchException.UNKNOWN_METADATA:
- case FetchException.UNKNOWN_SPLITFILE_METADATA:
- // Fatal, probably an error on insert
+ boolean isFatal = e.isFatal();
+ if(isFatal)
fatalError(e, code);
- return;
-
- case FetchException.DATA_NOT_FOUND:
- case FetchException.ROUTE_NOT_FOUND:
- case FetchException.REJECTED_OVERLOAD:
- case FetchException.TRANSFER_FAILED:
- // Non-fatal
+ else
nonfatalError(e, code);
- return;
-
- case FetchException.BUCKET_ERROR:
- case FetchException.INTERNAL_ERROR:
- // Maybe fatal
- nonfatalError(e, code);
- return;
- }
} catch (ArchiveFailureException e) {
fatalError(e, FetchException.ARCHIVE_FAILURE);
} catch (ArchiveRestartException e) {
Modified: trunk/freenet/src/freenet/client/FailureCodeTracker.java
===================================================================
--- trunk/freenet/src/freenet/client/FailureCodeTracker.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/client/FailureCodeTracker.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -4,6 +4,8 @@
import java.util.HashMap;
import java.util.Iterator;
+import freenet.support.SimpleFieldSet;
+
/**
* Essentially a map of integer to incrementible integer.
* FIXME maybe move this to support, give it a better name?
@@ -11,6 +13,7 @@
public class FailureCodeTracker {
public final boolean insert;
+ private int total;
public FailureCodeTracker(boolean insert) {
this.insert = insert;
@@ -28,6 +31,7 @@
if(i == null)
map.put(key, i = new Item());
i.x++;
+ total++;
}
public synchronized void inc(Integer key, int val) {
@@ -35,6 +39,7 @@
if(i == null)
map.put(key, i = new Item());
i.x+=val;
+ total += val;
}
public synchronized String toVerboseString() {
@@ -53,14 +58,53 @@
return sb.toString();
}
- public synchronized FailureCodeTracker merge(FailureCodeTracker
accumulatedFatalErrorCodes) {
- Iterator keys = map.keySet().iterator();
+ /**
+ * Merge codes from another tracker into this one.
+ */
+ public synchronized FailureCodeTracker merge(FailureCodeTracker source)
{
+ Iterator keys = source.map.keySet().iterator();
while(keys.hasNext()) {
Integer k = (Integer) keys.next();
- Item item = (Item) map.get(k);
+ Item item = (Item) source.map.get(k);
inc(k, item.x);
}
return this;
}
+
+ public void merge(FetchException e) {
+ if(insert) throw new IllegalStateException("Merging a
FetchException in an insert!");
+ if(e.errorCodes != null) {
+ merge(e.errorCodes);
+ }
+ // Increment mode anyway, so we get the splitfile error as well.
+ inc(e.mode);
+ }
+
+ public synchronized int totalCount() {
+ return total;
+ }
+
+ /** Copy verbosely to a SimpleFieldSet */
+ public synchronized void copyToFieldSet(SimpleFieldSet sfs, String
prefix) {
+ Iterator keys = map.keySet().iterator();
+ while(keys.hasNext()) {
+ Integer k = (Integer) keys.next();
+ Item item = (Item) map.get(k);
+ int code = k.intValue();
+ // prefix.num.Description=<code description>
+ // prefix.num.Count=<count>
+
sfs.put(prefix+Integer.toHexString(code)+".Description",
+ insert ?
InserterException.getMessage(code) : FetchException.getMessage(code));
+ sfs.put(prefix+Integer.toHexString(code)+".Count",
Integer.toHexString(item.x));
+ }
+ }
+
+ public synchronized boolean isOneCodeOnly() {
+ return map.size() == 1;
+ }
+
+ public synchronized int getFirstCode() {
+ return ((Integer) map.keySet().toArray()[0]).intValue();
+ }
}
Modified: trunk/freenet/src/freenet/client/FetchException.java
===================================================================
--- trunk/freenet/src/freenet/client/FetchException.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/client/FetchException.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -1,7 +1,5 @@
package freenet.client;
-import java.io.IOException;
-
import freenet.support.Logger;
/**
@@ -17,6 +15,8 @@
/** For collection errors */
public final FailureCodeTracker errorCodes;
+ public final String extraMessage;
+
/** Get the failure mode. */
public int getMode() {
return mode;
@@ -24,6 +24,7 @@
public FetchException(int m) {
super(getMessage(m));
+ extraMessage = null;
mode = m;
errorCodes = null;
Logger.minor(this, "FetchException("+getMessage(mode)+")",
this);
@@ -31,6 +32,7 @@
public FetchException(MetadataParseException e) {
super(getMessage(INVALID_METADATA)+": "+e.getMessage());
+ extraMessage = e.getMessage();
mode = INVALID_METADATA;
errorCodes = null;
initCause(e);
@@ -39,6 +41,7 @@
public FetchException(ArchiveFailureException e) {
super(getMessage(INVALID_METADATA)+": "+e.getMessage());
+ extraMessage = e.getMessage();
mode = ARCHIVE_FAILURE;
errorCodes = null;
initCause(e);
@@ -47,6 +50,7 @@
public FetchException(int mode, Throwable t) {
super(getMessage(mode)+": "+t.getMessage());
+ extraMessage = t.getMessage();
this.mode = mode;
errorCodes = null;
initCause(t);
@@ -55,6 +59,7 @@
public FetchException(int mode, FailureCodeTracker errorCodes) {
super(getMessage(mode));
+ extraMessage = null;
this.mode = mode;
this.errorCodes = errorCodes;
Logger.minor(this, "FetchException("+getMessage(mode)+")");
@@ -63,6 +68,7 @@
public FetchException(int mode, String msg) {
super(getMessage(mode)+": "+msg);
+ extraMessage = msg;
errorCodes = null;
this.mode = mode;
Logger.minor(this, "FetchException("+getMessage(mode)+"):
"+msg,this);
@@ -118,6 +124,8 @@
return "Too many blocks per segment";
case NOT_ENOUGH_METASTRINGS:
return "No default document; give more metastrings in
URI";
+ case CANCELLED:
+ return "Cancelled by caller";
default:
return "Unknown fetch error code: "+mode;
}
@@ -173,4 +181,50 @@
public static final int TOO_MANY_BLOCKS_PER_SEGMENT = 23;
/** Not enough meta strings in URI given and no default document */
public static final int NOT_ENOUGH_METASTRINGS = 24;
+ /** Explicitly cancelled */
+ public static final int CANCELLED = 25;
+
+ /** Is an error fatal i.e. is there no point retrying? */
+ public boolean isFatal() {
+ switch(mode) {
+ // Problems with the data as inserted. No point retrying.
+ case FetchException.ARCHIVE_FAILURE:
+ case FetchException.BLOCK_DECODE_ERROR:
+ case FetchException.HAS_MORE_METASTRINGS:
+ case FetchException.INVALID_METADATA:
+ case FetchException.NOT_IN_ARCHIVE:
+ case FetchException.TOO_DEEP_ARCHIVE_RECURSION:
+ case FetchException.TOO_MANY_ARCHIVE_RESTARTS:
+ case FetchException.TOO_MANY_METADATA_LEVELS:
+ case FetchException.TOO_MANY_REDIRECTS:
+ case FetchException.TOO_MUCH_RECURSION:
+ case FetchException.UNKNOWN_METADATA:
+ case FetchException.UNKNOWN_SPLITFILE_METADATA:
+ return true;
+
+ // Low level errors, can be retried
+ case FetchException.DATA_NOT_FOUND:
+ case FetchException.ROUTE_NOT_FOUND:
+ case FetchException.REJECTED_OVERLOAD:
+ case FetchException.TRANSFER_FAILED:
+ return false;
+
+ case FetchException.BUCKET_ERROR:
+ case FetchException.INTERNAL_ERROR:
+ // Maybe fatal
+ return false;
+
+ case FetchException.SPLITFILE_ERROR:
+ // Fatal, because there are internal retries
+ return true;
+
+ case FetchException.CANCELLED:
+ // Fatal
+ return true;
+
+ default:
+ Logger.error(this, "Do not know if error code is fatal:
"+getMessage(mode));
+ return false; // assume it isn't
+ }
+ }
}
Modified: trunk/freenet/src/freenet/client/Fetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/Fetcher.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/client/Fetcher.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -2,7 +2,6 @@
import java.io.IOException;
import java.net.MalformedURLException;
-import java.util.Iterator;
import java.util.LinkedList;
import freenet.client.events.DecodedBlockEvent;
@@ -22,7 +21,7 @@
/** Class that does the actual fetching. Does not have to have a user friendly
* interface!
*/
-class Fetcher {
+public class Fetcher {
/** The original URI to be fetched. */
final FreenetURI origURI;
@@ -37,7 +36,7 @@
/**
* Local-only constructor, with ArchiveContext, for recursion via e.g.
archives.
*/
- Fetcher(FreenetURI uri, FetcherContext fctx, ArchiveContext actx) {
+ public Fetcher(FreenetURI uri, FetcherContext fctx, ArchiveContext
actx) {
if(uri == null) throw new NullPointerException();
origURI = uri;
ctx = fctx;
@@ -61,8 +60,32 @@
* by this driver routine.
*/
public FetchResult run() throws FetchException {
+ FailureCodeTracker tracker = new FailureCodeTracker(false);
+ FetchException lastThrown = null;
+ for(int j=0;j<ctx.maxNonSplitfileRetries+1;j++) {
+ try {
+ return runOnce();
+ } catch (FetchException e) {
+ lastThrown = e;
+ tracker.merge(e);
+ if(e.isFatal()) throw e;
+ Logger.normal(this, "Possibly retrying "+this+"
despite "+e, e);
+ continue;
+ }
+ }
+ if(tracker.totalCount() == 1)
+ throw lastThrown;
+ else {
+ if(tracker.isOneCodeOnly())
+ throw new
FetchException(tracker.getFirstCode());
+ throw new
FetchException(FetchException.SPLITFILE_ERROR, tracker);
+ }
+ }
+
+ public FetchResult runOnce() throws FetchException {
for(int i=0;i<ctx.maxArchiveRestarts;i++) {
try {
+ if(ctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
ClientMetadata dm = new ClientMetadata();
ClientKey key;
try {
@@ -121,6 +144,7 @@
FetchResult realRun(ClientMetadata dm, int recursionLevel, ClientKey
key, LinkedList metaStrings, boolean dontEnterImplicitArchives, boolean
localOnly)
throws FetchException, MetadataParseException, ArchiveFailureException,
ArchiveRestartException {
Logger.minor(this, "Running fetch for: "+key);
+ if(ctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
recursionLevel++;
if(recursionLevel > ctx.maxRecursionLevel)
throw new
FetchException(FetchException.TOO_MUCH_RECURSION, ""+recursionLevel+" should be
< "+ctx.maxRecursionLevel);
@@ -128,7 +152,8 @@
// Do the fetch
ClientKeyBlock block;
try {
- block = ctx.client.getKey(key, localOnly,
ctx.starterClient, ctx.cacheLocalRequests);
+ block = ctx.client.getKey(key, localOnly,
ctx.starterClient, ctx.cacheLocalRequests, ctx.ignoreStore);
+ if(ctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
} catch (LowLevelGetException e) {
switch(e.code) {
case LowLevelGetException.DATA_NOT_FOUND:
@@ -206,6 +231,7 @@
Metadata metadata, ArchiveHandler container, FreenetURI
thisKey, boolean dontEnterImplicitArchives, boolean localOnly)
throws MetadataParseException, FetchException, ArchiveFailureException,
ArchiveRestartException {
+ if(ctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
if(metadata.isSimpleManifest()) {
String name;
if(metaStrings.isEmpty())
Modified: trunk/freenet/src/freenet/client/FetcherContext.java
===================================================================
--- trunk/freenet/src/freenet/client/FetcherContext.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/client/FetcherContext.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -9,35 +9,41 @@
/** Context for a Fetcher. Contains all the settings a Fetcher needs to know
about. */
public class FetcherContext implements Cloneable {
+ public static final int IDENTICAL_MASK = 0;
static final int SPLITFILE_DEFAULT_BLOCK_MASK = 1;
static final int SPLITFILE_DEFAULT_MASK = 2;
static final int SPLITFILE_USE_LENGTHS_MASK = 3;
/** Low-level client to send low-level requests to. */
final SimpleLowLevelClient client;
- final long maxOutputLength;
- final long maxTempLength;
+ public long maxOutputLength;
+ public long maxTempLength;
final ArchiveManager archiveManager;
- final BucketFactory bucketFactory;
- final int maxRecursionLevel;
- final int maxArchiveRestarts;
- final boolean dontEnterImplicitArchives;
- final int maxSplitfileThreads;
- final int maxSplitfileBlockRetries;
- final int maxNonSplitfileRetries;
- final RandomSource random;
- final boolean allowSplitfiles;
- final boolean followRedirects;
- final boolean localRequestOnly;
- final ClientEventProducer eventProducer;
+ public final BucketFactory bucketFactory;
+ public int maxRecursionLevel;
+ public int maxArchiveRestarts;
+ public boolean dontEnterImplicitArchives;
+ public int maxSplitfileThreads;
+ public int maxSplitfileBlockRetries;
+ public int maxNonSplitfileRetries;
+ public final RandomSource random;
+ public boolean allowSplitfiles;
+ public boolean followRedirects;
+ public boolean localRequestOnly;
+ public boolean ignoreStore;
+ public final ClientEventProducer eventProducer;
/** Whether to allow non-full blocks, or blocks which are not direct
CHKs, in splitfiles.
* Set by the splitfile metadata and the mask constructor, so we don't
need to pass it in. */
- final boolean splitfileUseLengths;
- final int maxMetadataSize;
- final int maxDataBlocksPerSegment;
- final int maxCheckBlocksPerSegment;
- final RequestStarterClient starterClient;
- final boolean cacheLocalRequests;
+ public boolean splitfileUseLengths;
+ public int maxMetadataSize;
+ public int maxDataBlocksPerSegment;
+ public int maxCheckBlocksPerSegment;
+ public final RequestStarterClient starterClient;
+ public boolean cacheLocalRequests;
+ private boolean cancelled;
+ public final boolean isCancelled() {
+ return cancelled;
+ }
public FetcherContext(SimpleLowLevelClient client, long curMaxLength,
long curMaxTempLength, int maxMetadataSize, int
maxRecursionLevel, int maxArchiveRestarts,
@@ -72,13 +78,36 @@
}
public FetcherContext(FetcherContext ctx, int maskID) {
- if(maskID == SPLITFILE_DEFAULT_BLOCK_MASK) {
+ if(maskID == IDENTICAL_MASK) {
this.client = ctx.client;
this.maxOutputLength = ctx.maxOutputLength;
this.maxMetadataSize = ctx.maxMetadataSize;
this.maxTempLength = ctx.maxTempLength;
this.archiveManager = ctx.archiveManager;
this.bucketFactory = ctx.bucketFactory;
+ this.maxRecursionLevel = ctx.maxRecursionLevel;
+ this.maxArchiveRestarts = ctx.maxArchiveRestarts;
+ this.dontEnterImplicitArchives =
ctx.dontEnterImplicitArchives;
+ this.random = ctx.random;
+ this.maxSplitfileThreads = ctx.maxSplitfileThreads;
+ this.maxSplitfileBlockRetries =
ctx.maxSplitfileBlockRetries;
+ this.maxNonSplitfileRetries =
ctx.maxNonSplitfileRetries;
+ this.allowSplitfiles = ctx.allowSplitfiles;
+ this.followRedirects = ctx.followRedirects;
+ this.localRequestOnly = ctx.localRequestOnly;
+ this.splitfileUseLengths = ctx.splitfileUseLengths;
+ this.eventProducer = ctx.eventProducer;
+ this.maxDataBlocksPerSegment =
ctx.maxDataBlocksPerSegment;
+ this.maxCheckBlocksPerSegment =
ctx.maxCheckBlocksPerSegment;
+ this.starterClient = ctx.starterClient;
+ this.cacheLocalRequests = ctx.cacheLocalRequests;
+ } else if(maskID == SPLITFILE_DEFAULT_BLOCK_MASK) {
+ this.client = ctx.client;
+ this.maxOutputLength = ctx.maxOutputLength;
+ this.maxMetadataSize = ctx.maxMetadataSize;
+ this.maxTempLength = ctx.maxTempLength;
+ this.archiveManager = ctx.archiveManager;
+ this.bucketFactory = ctx.bucketFactory;
this.maxRecursionLevel = 1;
this.maxArchiveRestarts = 0;
this.dontEnterImplicitArchives = true;
@@ -144,6 +173,10 @@
} else throw new IllegalArgumentException();
}
+ public void cancel() {
+ this.cancelled = true;
+ }
+
/** Make public, but just call parent for a field for field copy */
public Object clone() {
try {
Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -40,6 +40,8 @@
*/
public FreenetURI insertManifest(FreenetURI insertURI, HashMap
bucketsByName, String defaultName) throws InserterException;
+ public FetcherContext getFetcherContext();
+
/**
* Add a ClientEventListener.
*/
Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -95,12 +95,7 @@
*/
public FetchResult fetch(FreenetURI uri) throws FetchException {
if(uri == null) throw new NullPointerException();
- FetcherContext context = new FetcherContext(client,
curMaxLength, curMaxTempLength, curMaxMetadataLength,
- MAX_RECURSION, MAX_ARCHIVE_RESTARTS,
DONT_ENTER_IMPLICIT_ARCHIVES,
- SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES,
NON_SPLITFILE_RETRIES,
- FETCH_SPLITFILES, FOLLOW_REDIRECTS,
LOCAL_REQUESTS_ONLY,
- MAX_SPLITFILE_BLOCKS_PER_SEGMENT,
MAX_SPLITFILE_CHECK_BLOCKS_PER_SEGMENT,
- random, archiveManager, bucketFactory,
globalEventProducer, requestStarter, cacheLocalRequests);
+ FetcherContext context = getFetcherContext();
Fetcher f = new Fetcher(uri, context);
return f.run();
}
@@ -146,4 +141,14 @@
public void addGlobalHook(ClientEventListener listener) {
globalEventProducer.addEventListener(listener);
}
+
+ public FetcherContext getFetcherContext() {
+ return
+ new FetcherContext(client, curMaxLength,
curMaxTempLength, curMaxMetadataLength,
+ MAX_RECURSION, MAX_ARCHIVE_RESTARTS,
DONT_ENTER_IMPLICIT_ARCHIVES,
+ SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES,
NON_SPLITFILE_RETRIES,
+ FETCH_SPLITFILES, FOLLOW_REDIRECTS,
LOCAL_REQUESTS_ONLY,
+ MAX_SPLITFILE_BLOCKS_PER_SEGMENT,
MAX_SPLITFILE_CHECK_BLOCKS_PER_SEGMENT,
+ random, archiveManager, bucketFactory,
globalEventProducer, requestStarter, cacheLocalRequests);
+ }
}
Modified: trunk/freenet/src/freenet/client/Segment.java
===================================================================
--- trunk/freenet/src/freenet/client/Segment.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/client/Segment.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -261,6 +261,12 @@
}
public void onProgress() {
+ if(fetcherContext.isCancelled()) {
+ finished = true;
+ tracker.kill();
+ failureException = new
FetchException(FetchException.CANCELLED);
+ parentFetcher.gotBlocks(this);
+ }
parentFetcher.onProgress();
}
Modified: trunk/freenet/src/freenet/client/SplitFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/SplitFetcher.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/client/SplitFetcher.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -61,6 +61,7 @@
public SplitFetcher(Metadata metadata, ArchiveContext archiveContext,
FetcherContext ctx, int recursionLevel) throws MetadataParseException,
FetchException {
actx = archiveContext;
fctx = ctx;
+ if(fctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
overrideLength = metadata.dataLength;
this.maxTempLength = ctx.maxTempLength;
splitfileType = metadata.getSplitfileType();
@@ -137,6 +138,7 @@
*/
while(true) {
synchronized(this) {
+ if(fctx.isCancelled()) throw new
FetchException(FetchException.CANCELLED);
if(fetchingSegment == null) {
// Pick a random segment
fetchingSegment =
chooseUnstartedSegment();
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2006-01-18 14:35:15 UTC (rev
7872)
+++ trunk/freenet/src/freenet/node/Node.java 2006-01-18 16:38:29 UTC (rev
7873)
@@ -57,6 +57,7 @@
import freenet.keys.NodeSSK;
import freenet.keys.SSKBlock;
import freenet.keys.SSKVerifyException;
+import freenet.node.fcp.FCPServer;
import freenet.store.BerkeleyDBFreenetStore;
import freenet.store.FreenetStore;
import freenet.support.BucketFactory;
@@ -181,7 +182,7 @@
// Client stuff
final ArchiveManager archiveManager;
- final BucketFactory tempBucketFactory;
+ public final BucketFactory tempBucketFactory;
final RequestThrottle requestThrottle;
final RequestStarter requestStarter;
final RequestThrottle insertThrottle;
@@ -331,10 +332,12 @@
Thread t = new Thread(new MemoryChecker(), "Memory checker");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
- SimpleToadletServer server = new SimpleToadletServer(port+2001);
+ SimpleToadletServer server = new SimpleToadletServer(port+2000);
FproxyToadlet fproxy = new
FproxyToadlet(n.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS,
(short)0));
server.register(fproxy, "/", false);
- System.out.println("Starting fproxy on port "+(port+2001));
+ System.out.println("Starting fproxy on port "+(port+2000));
+ new FCPServer(port+3000, n);
+ System.out.println("Starting FCP server on port "+(port+3000));
//server.register(fproxy, "/SSK@", false);
//server.register(fproxy, "/KSK@", false);
}
@@ -466,23 +469,23 @@
usm.start();
}
- public ClientKeyBlock getKey(ClientKey key, boolean localOnly,
RequestStarterClient client, boolean cache) throws LowLevelGetException {
+ public ClientKeyBlock getKey(ClientKey key, boolean localOnly,
RequestStarterClient client, boolean cache, boolean ignoreStore) throws
LowLevelGetException {
if(key instanceof ClientSSK) {
ClientSSK k = (ClientSSK) key;
if(k.getPubKey() != null)
cacheKey(k.pubKeyHash, k.getPubKey());
}
if(localOnly)
- return realGetKey(key, localOnly, cache);
+ return realGetKey(key, localOnly, cache, ignoreStore);
else
- return client.getKey(key, localOnly, cache);
+ return client.getKey(key, localOnly, cache, ignoreStore);
}
- public ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean
cache) throws LowLevelGetException {
+ public ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean
cache, boolean ignoreStore) throws LowLevelGetException {
if(key instanceof ClientCHK)
- return realGetCHK((ClientCHK)key, localOnly, cache);
+ return realGetCHK((ClientCHK)key, localOnly, cache,
ignoreStore);
else if(key instanceof ClientSSK)
- return realGetSSK((ClientSSK)key, localOnly, cache);
+ return realGetSSK((ClientSSK)key, localOnly, cache,
ignoreStore);
else
throw new IllegalArgumentException("Not a CHK or SSK: "+key);
}
@@ -491,14 +494,14 @@
* Really trivially simple client interface.
* Either it succeeds or it doesn't.
*/
- ClientCHKBlock realGetCHK(ClientCHK key, boolean localOnly, boolean cache)
throws LowLevelGetException {
+ ClientCHKBlock realGetCHK(ClientCHK key, boolean localOnly, boolean cache,
boolean ignoreStore) throws LowLevelGetException {
long startTime = System.currentTimeMillis();
long uid = random.nextLong();
if(!lockUID(uid)) {
Logger.error(this, "Could not lock UID just randomly generated:
"+uid+" - probably indicates broken PRNG");
throw new
LowLevelGetException(LowLevelGetException.INTERNAL_ERROR);
}
- Object o = makeRequestSender(key.getNodeCHK(), MAX_HTL, uid, null,
lm.loc.getValue(), localOnly, cache);
+ Object o = makeRequestSender(key.getNodeCHK(), MAX_HTL, uid, null,
lm.loc.getValue(), localOnly, cache, ignoreStore);
if(o instanceof CHKBlock) {
try {
return new ClientCHKBlock((CHKBlock)o, key);
@@ -579,14 +582,14 @@
* Really trivially simple client interface.
* Either it succeeds or it doesn't.
*/
- ClientSSKBlock realGetSSK(ClientSSK key, boolean localOnly, boolean cache)
throws LowLevelGetException {
+ ClientSSKBlock realGetSSK(ClientSSK key, boolean localOnly, boolean cache,
boolean ignoreStore) throws LowLevelGetException {
long startTime = System.currentTimeMillis();
long uid = random.nextLong();
if(!lockUID(uid)) {
Logger.error(this, "Could not lock UID just randomly generated:
"+uid+" - probably indicates broken PRNG");
throw new
LowLevelGetException(LowLevelGetException.INTERNAL_ERROR);
}
- Object o = makeRequestSender(key.getNodeKey(), MAX_HTL, uid, null,
lm.loc.getValue(), localOnly, cache);
+ Object o = makeRequestSender(key.getNodeKey(), MAX_HTL, uid, null,
lm.loc.getValue(), localOnly, cache, ignoreStore);
if(o instanceof SSKBlock) {
try {
SSKBlock block = (SSKBlock)o;
@@ -981,10 +984,11 @@
* a RequestSender, unless the HTL is 0, in which case NULL.
* RequestSender.
*/
- public synchronized Object makeRequestSender(Key key, short htl, long uid,
PeerNode source, double closestLocation, boolean localOnly, boolean cache) {
+ public synchronized Object makeRequestSender(Key key, short htl, long uid,
PeerNode source, double closestLocation, boolean localOnly, boolean cache,
boolean ignoreStore) {
Logger.minor(this,
"makeRequestSender("+key+","+htl+","+uid+","+source+") on "+portNumber);
// In store?
KeyBlock chk = null;
+ if(!ignoreStore) {
try {
if(key instanceof NodeCHK)
chk = chkDatastore.fetch((NodeCHK)key, !cache);
@@ -1012,6 +1016,7 @@
Logger.error(this, "Error accessing store: "+e, e);
}
if(chk != null) return chk;
+ }
if(localOnly) return null;
Logger.minor(this, "Not in store locally");
Modified: trunk/freenet/src/freenet/node/QueuedDataRequest.java
===================================================================
--- trunk/freenet/src/freenet/node/QueuedDataRequest.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/QueuedDataRequest.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -9,18 +9,20 @@
private final ClientKey key;
private final boolean localOnly;
private final boolean cache;
+ private final boolean ignoreStore;
private QueueingSimpleLowLevelClient client;
- public QueuedDataRequest(ClientKey key, boolean localOnly, boolean
cache, QueueingSimpleLowLevelClient client) {
+ public QueuedDataRequest(ClientKey key, boolean localOnly, boolean
cache, QueueingSimpleLowLevelClient client, boolean ignoreStore) {
this.key = key;
this.localOnly = localOnly;
this.client = client;
this.cache = cache;
+ this.ignoreStore = ignoreStore;
}
public ClientKeyBlock waitAndFetch() throws LowLevelGetException {
waitForSendClearance();
- return client.realGetKey(key, localOnly, cache);
+ return client.realGetKey(key, localOnly, cache, ignoreStore);
}
}
Modified: trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
===================================================================
--- trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/QueueingSimpleLowLevelClient.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -1,15 +1,12 @@
package freenet.node;
-import freenet.client.InsertBlock;
-import freenet.keys.ClientCHKBlock;
import freenet.keys.ClientKey;
import freenet.keys.ClientKeyBlock;
-import freenet.keys.KeyBlock;
interface QueueingSimpleLowLevelClient extends SimpleLowLevelClient {
/** Unqueued version. Only call from QueuedDataRequest ! */
- ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean
cache) throws LowLevelGetException;
+ ClientKeyBlock realGetKey(ClientKey key, boolean localOnly, boolean
cache, boolean ignoreStore) throws LowLevelGetException;
/** Ditto */
void realPut(ClientKeyBlock block, boolean cache) throws
LowLevelPutException;
Modified: trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
===================================================================
--- trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -191,7 +191,7 @@
node2 = random.nextInt(NUMBER_OF_NODES);
} while(node2 == node1);
Node fetchNode = nodes[node2];
- block = (ClientCHKBlock) fetchNode.getKey((ClientKey) chk,
false, starters[node2], true);
+ block = (ClientCHKBlock) fetchNode.getKey((ClientKey) chk,
false, starters[node2], true, false);
if(block == null) {
Logger.error(RealNodeRequestInsertTest.class, "Fetch
FAILED from "+node2);
requestsAvg.report(0.0);
Modified: trunk/freenet/src/freenet/node/RequestHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestHandler.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/node/RequestHandler.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -58,7 +58,7 @@
Message accepted = DMT.createFNPAccepted(uid);
source.send(accepted);
- Object o = node.makeRequestSender(key, htl, uid, source, closestLoc,
false, true);
+ Object o = node.makeRequestSender(key, htl, uid, source, closestLoc,
false, true, false);
if(o instanceof KeyBlock) {
KeyBlock block = (KeyBlock) o;
Message df = createDataFound(block);
Modified: trunk/freenet/src/freenet/node/RequestStarterClient.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestStarterClient.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/RequestStarterClient.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -44,8 +44,8 @@
* Blocking fetch of a key.
* @throws LowLevelGetException If the fetch failed for some reason.
*/
- public ClientKeyBlock getKey(ClientKey key, boolean localOnly, boolean
cache) throws LowLevelGetException {
- QueuedDataRequest qdr = new QueuedDataRequest(key, localOnly,
cache, client);
+ public ClientKeyBlock getKey(ClientKey key, boolean localOnly, boolean
cache, boolean ignoreStore) throws LowLevelGetException {
+ QueuedDataRequest qdr = new QueuedDataRequest(key, localOnly,
cache, client, ignoreStore);
addRequest(qdr);
return qdr.waitAndFetch();
}
Modified: trunk/freenet/src/freenet/node/SimpleLowLevelClient.java
===================================================================
--- trunk/freenet/src/freenet/node/SimpleLowLevelClient.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/SimpleLowLevelClient.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -20,7 +20,7 @@
* @param cache If false, don't cache the data. See the comments at the top
* of Node.java.
*/
- public ClientKeyBlock getKey(ClientKey key, boolean localOnly,
RequestStarterClient client, boolean cache) throws LowLevelGetException;
+ public ClientKeyBlock getKey(ClientKey key, boolean localOnly,
RequestStarterClient client, boolean cache, boolean ignoreStore) throws
LowLevelGetException;
/**
* Insert a key.
Modified: trunk/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- trunk/freenet/src/freenet/node/TextModeClientInterface.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/TextModeClientInterface.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -159,6 +159,9 @@
System.out.println();
} catch (FetchException e) {
System.out.println("Error: "+e.getMessage());
+ if(e.getMode() == e.SPLITFILE_ERROR && e.errorCodes != null) {
+ System.out.println(e.errorCodes.toVerboseString());
+ }
}
} else if(uline.startsWith("GETFILE:")) {
// Should have a key next
@@ -699,10 +702,12 @@
try {
pn = new PeerNode(fs, n);
} catch (FSParseException e1) {
- System.err.println("Did not parse: "+e1.getMessage());
+ System.err.println("Did not parse: "+e1);
+ Logger.error(this, "Did not parse: "+e1, e1);
return;
} catch (PeerParseException e1) {
- System.err.println("Did not parse: "+e1.getMessage());
+ System.err.println("Did not parse: "+e1);
+ Logger.error(this, "Did not parse: "+e1, e1);
return;
}
if(n.peers.addPeer(pn))
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-01-18 14:35:15 UTC (rev
7872)
+++ trunk/freenet/src/freenet/node/Version.java 2006-01-18 16:38:29 UTC (rev
7873)
@@ -20,7 +20,7 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- public static final int buildNumber = 359;
+ public static final int buildNumber = 361;
/** Oldest build of Fred we will talk to */
public static final int lastGoodBuild = 359;
Added: trunk/freenet/src/freenet/node/fcp/AllDataMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/AllDataMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/AllDataMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,42 @@
+package freenet.node.fcp;
+
+import freenet.node.Node;
+import freenet.support.Bucket;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * All the data, all in one big chunk. Obviously we must already have
+ * all the data to send it. We do not want to have to block on a request,
+ * especially as there may be errors.
+ */
+public class AllDataMessage extends DataCarryingMessage {
+
+ final long dataLength;
+ final String identifier;
+
+ public AllDataMessage(FCPConnectionHandler handler, Bucket bucket,
String identifier) {
+ this.bucket = bucket;
+ this.dataLength = bucket.size();
+ this.identifier = identifier;
+ }
+
+ long dataLength() {
+ return dataLength;
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet fs = new SimpleFieldSet();
+ fs.put("DataLength", Long.toHexString(dataLength));
+ fs.put("Identifier", identifier);
+ return fs;
+ }
+
+ public String getName() {
+ return "AllData";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) throws
MessageInvalidException {
+ throw new
MessageInvalidException(ProtocolErrorMessage.INVALID_MESSAGE, "AllData goes
from server to client not the other way around");
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGet.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/ClientGet.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -0,0 +1,70 @@
+package freenet.node.fcp;
+
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.Fetcher;
+import freenet.client.FetcherContext;
+import freenet.keys.FreenetURI;
+import freenet.support.Logger;
+
+/**
+ * A simple client fetch. This can of course fetch arbitrarily large
+ * files, including splitfiles, redirects, etc.
+ */
+public class ClientGet extends ClientRequest implements Runnable {
+
+ private final FreenetURI uri;
+ private final FetcherContext fctx;
+ private final Fetcher f;
+ private final String identifier;
+ private final int verbosity;
+ private final FCPConnectionHandler handler;
+
+ public ClientGet(FCPConnectionHandler handler, ClientGetMessage
message) {
+ uri = message.uri;
+ // Create a Fetcher directly in order to get more fine-grained
control,
+ // since the client may override a few context elements.
+ this.handler = handler;
+ fctx = new FetcherContext(handler.defaultFetchContext,
FetcherContext.IDENTICAL_MASK);
+ // ignoreDS
+ fctx.localRequestOnly = message.dsOnly;
+ fctx.ignoreStore = message.ignoreDS;
+ fctx.maxNonSplitfileRetries = message.maxRetries;
+ fctx.maxSplitfileBlockRetries =
Math.max(fctx.maxSplitfileBlockRetries, message.maxRetries);
+ this.identifier = message.identifier;
+ this.verbosity = message.verbosity;
+ // FIXME do something with verbosity !!
+ // Has already been checked
+ if(message.returnType != ClientGetMessage.RETURN_TYPE_DIRECT)
+ throw new IllegalStateException("Unknown return type:
"+message.returnType);
+ fctx.maxOutputLength = message.maxSize;
+ fctx.maxTempLength = message.maxTempSize;
+ f = new Fetcher(uri, fctx);
+ Thread t = new Thread(this, "FCP fetcher for "+uri+"
("+identifier+") on "+handler);
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void cancel() {
+ fctx.cancel();
+ }
+
+ public void run() {
+ try {
+ FetchResult fr = f.run();
+ // Success!!!
+ FCPMessage msg = new DataFoundMessage(handler, fr,
identifier);
+ handler.outputHandler.queue(msg);
+ // Send all the data at once
+ // FIXME there should be other options
+ msg = new AllDataMessage(handler, fr.asBucket(),
identifier);
+ handler.outputHandler.queue(msg);
+ } catch (FetchException e) {
+ // Error
+ Logger.minor(this, "Caught "+e, e);
+ FCPMessage msg = new FetchErrorMessage(handler, e,
identifier);
+ handler.outputHandler.queue(msg);
+ }
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/ClientGetMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGetMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/ClientGetMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,130 @@
+package freenet.node.fcp;
+
+import java.net.MalformedURLException;
+
+import freenet.keys.FreenetURI;
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * ClientGet message.
+ *
+ * Example:
+ *
+ * ClientGet
+ * IgnoreDS=false // true = ignore the datastore
+ * DSOnly=false // true = only check the datastore, don't route (~= htl 0)
+ * URI=KSK at gpl.txt
+ * Identifier=Request Number One
+ * Verbosity=0 // no status, just tell us when it's done
+ * ReturnType=direct // return all at once over the FCP connection
+ * MaxSize=100 // maximum size of returned data (all numbers in hex)
+ * MaxTempSize=1000 // maximum size of intermediary data
+ * MaxRetries=100 // automatic retry supported as an option
+ * EndMessage
+ */
+public class ClientGetMessage extends FCPMessage {
+
+ final boolean ignoreDS;
+ final boolean dsOnly;
+ final FreenetURI uri;
+ final String identifier;
+ final int verbosity;
+ final int returnType;
+ final long maxSize;
+ final long maxTempSize;
+ final int maxRetries;
+
+ // FIXME move these to the actual getter process
+ static final int RETURN_TYPE_DIRECT = 0;
+
+ public ClientGetMessage(SimpleFieldSet fs) throws
MessageInvalidException {
+ ignoreDS = Boolean.getBoolean(fs.get("IgnoreDS"));
+ dsOnly = Boolean.getBoolean(fs.get("DSOnly"));
+ try {
+ uri = new FreenetURI(fs.get("URI"));
+ } catch (MalformedURLException e) {
+ throw new
MessageInvalidException(ProtocolErrorMessage.URI_PARSE_ERROR, e.getMessage());
+ }
+ identifier = fs.get("Identifier");
+ if(identifier == null)
+ throw new
MessageInvalidException(ProtocolErrorMessage.MISSING_FIELD, "No Identifier");
+ String verbosityString = fs.get("Verbosity");
+ if(verbosityString == null)
+ verbosity = 0;
+ else {
+ try {
+ verbosity = Integer.parseInt(verbosityString,
16);
+ } catch (NumberFormatException e) {
+ throw new
MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error
parsing Verbosity field: "+e.getMessage());
+ }
+ }
+ String returnTypeString = fs.get("ReturnType");
+ if(returnTypeString == null ||
returnTypeString.equalsIgnoreCase("direct"))
+ returnType = RETURN_TYPE_DIRECT;
+ else
+ throw new
MessageInvalidException(ProtocolErrorMessage.MESSAGE_PARSE_ERROR, "Unknown
return-type");
+ String maxSizeString = fs.get("MaxSize");
+ if(maxSizeString == null)
+ // default to unlimited
+ maxSize = Long.MAX_VALUE;
+ else {
+ try {
+ maxSize = Long.parseLong(maxSizeString, 16);
+ } catch (NumberFormatException e) {
+ throw new
MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error
parsing MaxSize field: "+e.getMessage());
+ }
+ }
+ String maxTempSizeString = fs.get("MaxTempSize");
+ if(maxTempSizeString == null)
+ // default to unlimited
+ maxTempSize = Long.MAX_VALUE;
+ else {
+ try {
+ maxTempSize = Long.parseLong(maxTempSizeString,
16);
+ } catch (NumberFormatException e) {
+ throw new
MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error
parsing MaxSize field: "+e.getMessage());
+ }
+ }
+ String maxRetriesString = fs.get("MaxRetries");
+ if(maxRetriesString == null)
+ // default to 0
+ maxRetries = 0;
+ else {
+ try {
+ maxRetries = Integer.parseInt(maxRetriesString,
16);
+ } catch (NumberFormatException e) {
+ throw new
MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error
parsing MaxSize field: "+e.getMessage());
+ }
+ }
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet fs = new SimpleFieldSet();
+ fs.put("IgnoreDS", Boolean.toString(ignoreDS));
+ fs.put("URI", uri.toString(false));
+ fs.put("Identifier", identifier);
+ fs.put("Verbosity", Integer.toHexString(verbosity));
+ fs.put("ReturnType", getReturnTypeString());
+ fs.put("MaxSize", Long.toHexString(maxSize));
+ fs.put("MaxTempSize", Long.toHexString(maxTempSize));
+ fs.put("MaxRetries", Integer.toHexString(maxRetries));
+ return fs;
+ }
+
+ private String getReturnTypeString() {
+ if(returnType == RETURN_TYPE_DIRECT)
+ return "direct";
+ else
+ throw new IllegalStateException("Unknown return type:
"+returnType);
+ }
+
+ public String getName() {
+ return "ClientGet";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) {
+ handler.startClientGet(this);
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/ClientHelloMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientHelloMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/ClientHelloMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,45 @@
+package freenet.node.fcp;
+
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * ClientHello
+ * Name=Toad's Test Client
+ * ExpectedVersion=0.7.0
+ * End
+ */
+public class ClientHelloMessage extends FCPMessage {
+
+ String clientName;
+ String clientExpectedVersion;
+
+ public ClientHelloMessage(SimpleFieldSet fs) throws
MessageInvalidException {
+ clientName = fs.get("Name");
+ clientExpectedVersion = fs.get("ExpectedVersion");
+ if(clientName == null)
+ throw new
MessageInvalidException(ProtocolErrorMessage.MISSING_FIELD, "ClientHello must
contain a Name field");
+ if(clientExpectedVersion == null)
+ throw new
MessageInvalidException(ProtocolErrorMessage.MISSING_FIELD, "ClientHello must
contain a ExpectedVersion field");
+ // FIXME check the expected version
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet sfs = new SimpleFieldSet();
+ sfs.put("Name", clientName);
+ sfs.put("ExpectedVersion", clientExpectedVersion);
+ return sfs;
+ }
+
+ public String getName() {
+ return "ClientHello";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) {
+ // We know the Hello is valid.
+ handler.setClientName(clientName);
+ FCPMessage msg = new NodeHelloMessage();
+ handler.outputHandler.queue(msg);
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/ClientRequest.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientRequest.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/ClientRequest.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,12 @@
+package freenet.node.fcp;
+
+/**
+ * A request process carried out by the node for an FCP client.
+ * Examples: ClientGet, ClientPut, MultiGet.
+ */
+public abstract class ClientRequest {
+
+ /** Cancel */
+ public abstract void cancel();
+
+}
Added: trunk/freenet/src/freenet/node/fcp/DataCarryingMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/DataCarryingMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/DataCarryingMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,36 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
+
+
+public abstract class DataCarryingMessage extends FCPMessage {
+
+ protected Bucket bucket;
+
+ abstract long dataLength();
+
+ public void readFrom(InputStream is, BucketFactory bf) throws
IOException {
+ long len = dataLength();
+ if(len < 0)
+ throw new IllegalArgumentException("Invalid length:
"+len);
+ Bucket bucket = bf.makeBucket(len);
+ BucketTools.copyFrom(bucket, is, len);
+ this.bucket = bucket;
+ }
+
+ public void send(OutputStream os) throws IOException {
+ super.send(os);
+ BucketTools.copyTo(bucket, os, dataLength());
+ }
+
+ String getEndString() {
+ return "Data";
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/DataFoundMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/DataFoundMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/DataFoundMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,35 @@
+package freenet.node.fcp;
+
+import freenet.client.FetchResult;
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+public class DataFoundMessage extends FCPMessage {
+
+ final String identifier;
+ final String mimeType;
+ final long dataLength;
+
+ public DataFoundMessage(FCPConnectionHandler handler, FetchResult fr,
String identifier) {
+ this.identifier = identifier;
+ this.mimeType = fr.getMimeType();
+ this.dataLength = fr.size();
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet fs = new SimpleFieldSet();
+ fs.put("Identifier", identifier);
+ fs.put("Metadata.ContentType", mimeType);
+ fs.put("DataLength", Long.toHexString(dataLength));
+ return fs;
+ }
+
+ public String getName() {
+ return "DataFound";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) throws
MessageInvalidException {
+ throw new
MessageInvalidException(ProtocolErrorMessage.INVALID_MESSAGE, "DataFound goes
from server to client not the other way around");
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FCPConnectionHandler.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,108 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.HashMap;
+
+import freenet.client.FetcherContext;
+import freenet.node.Node;
+import freenet.support.BucketFactory;
+import freenet.support.Logger;
+
+public class FCPConnectionHandler {
+
+ final Socket sock;
+ final FCPConnectionInputHandler inputHandler;
+ final FCPConnectionOutputHandler outputHandler;
+ final Node node;
+ private boolean isClosed;
+ private boolean inputClosed;
+ private boolean outputClosed;
+ private String clientName;
+ final BucketFactory bf;
+ final HashMap requestsByIdentifier;
+ final FetcherContext defaultFetchContext;
+
+ public FCPConnectionHandler(Socket s, Node node) {
+ this.sock = s;
+ this.node = node;
+ this.inputHandler = new FCPConnectionInputHandler(this);
+ this.outputHandler = new FCPConnectionOutputHandler(this);
+ isClosed = false;
+ this.bf = node.tempBucketFactory;
+ requestsByIdentifier = new HashMap();
+ defaultFetchContext =
+ node.makeClient((short)0,(short)0).getFetcherContext();
+ }
+
+ public void close() {
+ ClientRequest[] requests;
+ synchronized(this) {
+ isClosed = true;
+ requests = new
ClientRequest[requestsByIdentifier.size()];
+ requests = (ClientRequest[])
requestsByIdentifier.values().toArray(requests);
+ }
+ for(int i=0;i<requests.length;i++)
+ requests[i].cancel();
+ }
+
+ public boolean isClosed() {
+ return isClosed;
+ }
+
+ public void closedInput() {
+ try {
+ sock.shutdownInput();
+ } catch (IOException e) {
+ // Ignore
+ }
+ synchronized(this) {
+ inputClosed = true;
+ if(!outputClosed) return;
+ }
+ try {
+ sock.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ public void closedOutput() {
+ try {
+ sock.shutdownOutput();
+ } catch (IOException e) {
+ // Ignore
+ }
+ synchronized(this) {
+ outputClosed = true;
+ if(!inputClosed) return;
+ }
+ try {
+ sock.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ public void setClientName(String name) {
+ this.clientName = name;
+ }
+
+ public String getClientName() {
+ return clientName;
+ }
+
+ public void startClientGet(ClientGetMessage message) {
+ String id = message.identifier;
+ if(requestsByIdentifier.containsKey(id)) {
+ Logger.normal(this, "Identifier collision on "+this);
+ FCPMessage msg = new IdentifierCollisionMessage(id);
+ outputHandler.queue(msg);
+ return;
+ }
+ synchronized(this) {
+ if(isClosed) return;
+ ClientGet cg = new ClientGet(this, message);
+ }
+ }
+}
Added: trunk/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FCPConnectionInputHandler.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,78 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+import freenet.support.io.LineReadingInputStream;
+
+public class FCPConnectionInputHandler implements Runnable {
+
+ final FCPConnectionHandler handler;
+
+ public FCPConnectionInputHandler(FCPConnectionHandler handler) {
+ this.handler = handler;
+ Thread t = new Thread(this, "FCP input handler for
"+handler.sock.getRemoteSocketAddress()+":"+handler.sock.getPort());
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void run() {
+ try {
+ realRun();
+ } catch (IOException e) {
+ Logger.minor(this, "Caught "+e, e);
+ } catch (Throwable t) {
+ Logger.error(this, "Caught "+t, t);
+ }
+ handler.close();
+ handler.closedInput();
+ }
+
+ public void realRun() throws IOException {
+ InputStream is = handler.sock.getInputStream();
+ LineReadingInputStream lis = new LineReadingInputStream(is);
+
+ boolean firstMessage = true;
+
+ while(true) {
+ SimpleFieldSet fs;
+ // Read a message
+ String messageType = lis.readLine(64, 64);
+ fs = new SimpleFieldSet(lis, 4096, 128);
+ FCPMessage msg;
+ try {
+ msg = FCPMessage.create(messageType, fs);
+ if(msg == null) continue;
+ } catch (MessageInvalidException e) {
+ FCPMessage err = new
ProtocolErrorMessage(e.protocolCode, false, e.getMessage());
+ handler.outputHandler.queue(err);
+ continue;
+ }
+ if(firstMessage && !(msg instanceof
ClientHelloMessage)) {
+ FCPMessage err = new
ProtocolErrorMessage(ProtocolErrorMessage.CLIENT_HELLO_MUST_BE_FIRST_MESSAGE,
true, null);
+ handler.outputHandler.queue(err);
+ handler.close();
+ continue;
+ }
+ if(msg instanceof DataCarryingMessage) {
+ ((DataCarryingMessage)msg).readFrom(lis,
handler.bf);
+ }
+ if((!firstMessage) && msg instanceof
ClientHelloMessage) {
+ FCPMessage err = new
ProtocolErrorMessage(ProtocolErrorMessage.NO_LATE_CLIENT_HELLOS, false, null);
+ handler.outputHandler.queue(err);
+ continue;
+ }
+ try {
+ msg.run(handler, handler.node);
+ } catch (MessageInvalidException e) {
+ FCPMessage err = new
ProtocolErrorMessage(e.protocolCode, false, e.getMessage());
+ handler.outputHandler.queue(err);
+ continue;
+ }
+ firstMessage = false;
+ if(handler.isClosed()) return;
+ }
+ }
+}
Added: trunk/freenet/src/freenet/node/fcp/FCPConnectionOutputHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPConnectionOutputHandler.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FCPConnectionOutputHandler.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,65 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedList;
+
+import freenet.support.Logger;
+
+public class FCPConnectionOutputHandler implements Runnable {
+
+ final FCPConnectionHandler handler;
+ final LinkedList outQueue;
+
+ public FCPConnectionOutputHandler(FCPConnectionHandler handler) {
+ this.handler = handler;
+ this.outQueue = new LinkedList();
+ Thread t = new Thread(this, "FCP output handler for
"+handler.sock.getRemoteSocketAddress()+":"+handler.sock.getPort());
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void run() {
+ try {
+ realRun();
+ } catch (IOException e) {
+ Logger.minor(this, "Caught "+e, e);
+ } catch (Throwable t) {
+ Logger.minor(this, "Caught "+t, t);
+ }
+ handler.close();
+ handler.closedOutput();
+ }
+
+ private void realRun() throws IOException {
+ OutputStream os = handler.sock.getOutputStream();
+ while(true) {
+ FCPMessage msg;
+ synchronized(outQueue) {
+ while(true) {
+ if(outQueue.isEmpty()) {
+ if(handler.isClosed()) return;
+ try {
+ outQueue.wait(10000);
+ } catch (InterruptedException
e) {
+ // Ignore
+ }
+ continue;
+ }
+ msg = (FCPMessage)
outQueue.removeFirst();
+ break;
+ }
+ }
+ msg.send(os);
+ if(handler.isClosed()) return;
+ }
+ }
+
+ public void queue(FCPMessage msg) {
+ synchronized(outQueue) {
+ outQueue.add(msg);
+ outQueue.notifyAll();
+ }
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/FCPMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPMessage.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FCPMessage.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -0,0 +1,44 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+public abstract class FCPMessage {
+
+ public void send(OutputStream os) throws IOException {
+ SimpleFieldSet sfs = getFieldSet();
+ sfs.setEndMarker(getEndString());
+ String msg = sfs.toString();
+ os.write((getName()+"\n").getBytes("UTF-8"));
+ os.write(msg.getBytes("UTF-8"));
+ }
+
+ String getEndString() {
+ return "EndMessage";
+ }
+
+ public abstract SimpleFieldSet getFieldSet();
+
+ public abstract String getName();
+
+ public static FCPMessage create(String name, SimpleFieldSet fs) throws
MessageInvalidException {
+ if(name.equals("ClientHello"))
+ return new ClientHelloMessage(fs);
+ if(name.equals("ClientGet"))
+ return new ClientGetMessage(fs);
+ if(name.equals("Void"))
+ return null;
+// if(name.equals("ClientPut"))
+// return new ClientPutFCPMessage(fs);
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /** Do whatever it is that we do with this type of message.
+ * @throws MessageInvalidException */
+ public abstract void run(FCPConnectionHandler handler, Node node)
throws MessageInvalidException;
+
+}
Added: trunk/freenet/src/freenet/node/fcp/FCPServer.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPServer.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FCPServer.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -0,0 +1,45 @@
+package freenet.node.fcp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import freenet.node.Node;
+import freenet.support.Logger;
+
+/**
+ * FCP server process.
+ */
+public class FCPServer implements Runnable {
+
+ final ServerSocket sock;
+ final Node node;
+
+ public FCPServer(int port, Node node) throws IOException {
+ this.sock = new ServerSocket(port, 0,
InetAddress.getByName("127.0.0.1"));
+ this.node = node;
+ Thread t = new Thread(this, "FCP server");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void run() {
+ while(true) {
+ try {
+ realRun();
+ } catch (IOException e) {
+ Logger.minor(this, "Caught "+e, e);
+ } catch (Throwable t) {
+ Logger.error(this, "Caught "+t, t);
+ }
+ }
+ }
+
+ private void realRun() throws IOException {
+ // Accept a connection
+ Socket s = sock.accept();
+ FCPConnectionHandler handler = new FCPConnectionHandler(s,
node);
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/FetchErrorMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FetchErrorMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/FetchErrorMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,45 @@
+package freenet.node.fcp;
+
+import freenet.client.FailureCodeTracker;
+import freenet.client.FetchException;
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+public class FetchErrorMessage extends FCPMessage {
+
+ final int code;
+ final String codeDescription;
+ final String extraDescription;
+ final FailureCodeTracker tracker;
+ final boolean isFatal;
+
+ public FetchErrorMessage(FCPConnectionHandler handler, FetchException
e, String identifier) {
+ this.tracker = e.errorCodes;
+ this.code = e.mode;
+ this.codeDescription = FetchException.getMessage(code);
+ this.extraDescription = e.extraMessage;
+ this.isFatal = e.isFatal();
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet sfs = new SimpleFieldSet();
+ sfs.put("Code", Integer.toHexString(code));
+ sfs.put("CodeDescription", codeDescription);
+ if(extraDescription != null)
+ sfs.put("ExtraDescription", extraDescription);
+ sfs.put("Fatal", Boolean.toString(isFatal));
+ if(tracker != null) {
+ tracker.copyToFieldSet(sfs, "Errors.");
+ }
+ return sfs;
+ }
+
+ public String getName() {
+ return "FetchError";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) throws
MessageInvalidException {
+ throw new
MessageInvalidException(ProtocolErrorMessage.INVALID_MESSAGE, "FetchError goes
from server to client not the other way around");
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/IdentifierCollisionMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/IdentifierCollisionMessage.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/IdentifierCollisionMessage.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,29 @@
+package freenet.node.fcp;
+
+import freenet.node.Node;
+import freenet.support.SimpleFieldSet;
+
+public class IdentifierCollisionMessage extends FCPMessage {
+
+ final String identifier;
+
+ public IdentifierCollisionMessage(String id) {
+ this.identifier = id;
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet sfs = new SimpleFieldSet();
+ sfs.put("Identifier", identifier);
+ return sfs;
+ }
+
+ public String getName() {
+ return "IdentifierCollision";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node)
+ throws MessageInvalidException {
+ throw new
MessageInvalidException(ProtocolErrorMessage.INVALID_MESSAGE,
"IdentifierCollision goes from server to client not the other way around");
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/MessageInvalidException.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/MessageInvalidException.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/MessageInvalidException.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,17 @@
+package freenet.node.fcp;
+
+/**
+ * Thrown when an FCP message is invalid. This is after we have a
+ * SimpleFieldSet; one example is if the fields necessary do not exist.
+ * This is a catch-all error; it corresponds to MESSAGE_PARSE_ERROR on
+ * ProtocolError.
+ */
+public class MessageInvalidException extends Exception {
+
+ int protocolCode;
+
+ public MessageInvalidException(int protocolCode, String extra) {
+ super(extra);
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/NodeHelloMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/NodeHelloMessage.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/NodeHelloMessage.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,36 @@
+package freenet.node.fcp;
+
+import freenet.node.Node;
+import freenet.node.Version;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * NodeHello
+ *
+ * NodeHello
+ * FCPVersion=<protocol version>
+ * Node=Fred
+ * Version=0.7.0,401
+ * EndMessage
+ */
+public class NodeHelloMessage extends FCPMessage {
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet sfs = new SimpleFieldSet();
+ // FIXME
+ sfs.put("FCPVersion", "0.7.0");
+ sfs.put("Node", "Fred");
+ sfs.put("Version", Version.getVersionString());
+ return sfs;
+ }
+
+ public String getName() {
+ return "NodeHello";
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) {
+ throw new UnsupportedOperationException();
+ // Client should not be sending this!
+ }
+
+}
Added: trunk/freenet/src/freenet/node/fcp/ProtocolErrorMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ProtocolErrorMessage.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/node/fcp/ProtocolErrorMessage.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -0,0 +1,78 @@
+package freenet.node.fcp;
+
+import freenet.node.Node;
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * ProtocolError (some problem parsing the other side's FCP messages)
+ *
+ * ProtocolError
+ * Code=1
+ * CodeDescription=ClientHello must be first message
+ * ExtraDescription=Duh
+ * Fatal=false // means the connection stays open
+ * EndMessage
+ */
+public class ProtocolErrorMessage extends FCPMessage {
+
+ static final int CLIENT_HELLO_MUST_BE_FIRST_MESSAGE = 1;
+ static final int NO_LATE_CLIENT_HELLOS = 2;
+ static final int MESSAGE_PARSE_ERROR = 3;
+ static final int URI_PARSE_ERROR = 4;
+ static final int MISSING_FIELD = 5;
+ static final int ERROR_PARSING_NUMBER = 6;
+ static final int INVALID_MESSAGE = 7;
+
+ final int code;
+ final String extra;
+ final boolean fatal;
+
+ private String codeDescription() {
+ switch(code) {
+ case CLIENT_HELLO_MUST_BE_FIRST_MESSAGE:
+ return "ClientHello must be first message";
+ case NO_LATE_CLIENT_HELLOS:
+ return "No late ClientHello's accepted";
+ case MESSAGE_PARSE_ERROR:
+ return "Unknown message parsing error";
+ case URI_PARSE_ERROR:
+ return "Error parsing URI";
+ case MISSING_FIELD:
+ return "Missing field";
+ case ERROR_PARSING_NUMBER:
+ return "Error parsing a numeric field";
+ case INVALID_MESSAGE:
+ return "Don't know what to do with message";
+ default:
+ Logger.error(this, "Unknown error code: "+code, new
Exception("debug"));
+ return "(Unknown)";
+ }
+ }
+
+ public ProtocolErrorMessage(int code, boolean fatal, String extra) {
+ this.code = code;
+ this.extra = extra;
+ this.fatal = fatal;
+ }
+
+ public SimpleFieldSet getFieldSet() {
+ SimpleFieldSet sfs = new SimpleFieldSet();
+ sfs.put("Code", Integer.toHexString(code));
+ sfs.put("CodeDescription", codeDescription());
+ if(extra != null)
+ sfs.put("ExtraDescription", extra);
+ sfs.put("Fatal", Boolean.toString(fatal));
+ return sfs;
+ }
+
+ public void run(FCPConnectionHandler handler, Node node) {
+ Logger.error(this, "Client reported protocol error");
+ if(fatal) handler.close();
+ }
+
+ public String getName() {
+ return "ProtocolError";
+ }
+
+}
Modified: trunk/freenet/src/freenet/support/BucketTools.java
===================================================================
--- trunk/freenet/src/freenet/support/BucketTools.java 2006-01-18 14:35:15 UTC
(rev 7872)
+++ trunk/freenet/src/freenet/support/BucketTools.java 2006-01-18 16:38:29 UTC
(rev 7873)
@@ -399,6 +399,32 @@
}
}
+ /** Copy data from an InputStream into a Bucket. */
+ public static void copyFrom(Bucket bucket, InputStream is, long
truncateLength) throws IOException {
+ OutputStream os = bucket.getOutputStream();
+ byte[] buf = new byte[4096];
+ if(truncateLength < 0) truncateLength = Long.MAX_VALUE;
+ try {
+ long moved = 0;
+ while(moved < truncateLength) {
+ // DO NOT move the (int) inside the Math.min()!
big numbers truncate to negative numbers.
+ int bytes = (int) Math.min(buf.length,
truncateLength - moved);
+ if(bytes <= 0)
+ throw new
IllegalStateException("bytes="+bytes+", truncateLength="+truncateLength+",
moved="+moved);
+ bytes = is.read(buf, 0, bytes);
+ if(bytes <= 0) {
+ if(truncateLength == Long.MAX_VALUE)
+ break;
+ throw new IOException("Could not move
required quantity of data: "+bytes+" (moved "+moved+" of "+truncateLength+")");
+ }
+ os.write(buf, 0, bytes);
+ moved += bytes;
+ }
+ } finally {
+ os.close();
+ }
+ }
+
/**
* Split the data into a series of read-only Bucket's.
* @param origData The original data Bucket.
Modified: trunk/freenet/src/freenet/support/SimpleFieldSet.java
===================================================================
--- trunk/freenet/src/freenet/support/SimpleFieldSet.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/support/SimpleFieldSet.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -11,6 +11,8 @@
import java.util.Map;
import java.util.Set;
+import freenet.support.io.LineReader;
+
/**
* @author amphibian
*
@@ -20,12 +22,18 @@
public class SimpleFieldSet {
final Map map;
+ String endMarker;
public SimpleFieldSet(BufferedReader br) throws IOException {
map = new HashMap();
read(br);
}
+ public SimpleFieldSet(LineReader lis, int maxLineLength, int
lineBufferSize) throws IOException {
+ map = new HashMap();
+ read(lis, maxLineLength, lineBufferSize);
+ }
+
/**
* Empty constructor
*/
@@ -67,13 +75,44 @@
String after = line.substring(index+1);
map.put(before, after);
} else {
- if(line.equals("End")) return;
- throw new IOException("Unknown end-marker: \""+line+"\"");
+ endMarker = line;
+ return;
}
}
}
+ /**
+ * Read from disk
+ * Format:
+ * blah=blah
+ * blah=blah
+ * End
+ */
+ private void read(LineReader br, int maxLength, int bufferSize) throws
IOException {
+ boolean firstLine = true;
+ while(true) {
+ String line = br.readLine(maxLength, bufferSize);
+ if(line == null) {
+ if(firstLine) throw new EOFException();
+ throw new IOException();
+ }
+ firstLine = false;
+ int index = line.indexOf('=');
+ if(index >= 0) {
+ // Mapping
+ String before = line.substring(0, index);
+ String after = line.substring(index+1);
+ map.put(before, after);
+ } else {
+ endMarker = line;
+ return;
+ }
+
+ }
+ }
+
+
public String get(String key) {
return (String) map.get(key);
}
@@ -95,7 +134,10 @@
String value = (String) entry.getValue();
w.write(key+"="+value+"\n");
}
- w.write("End\n");
+ if(endMarker != null)
+ w.write(endMarker+"\n");
+ else
+ w.write("End\n");
}
public String toString() {
@@ -107,4 +149,12 @@
}
return sw.toString();
}
+
+ public String getEndMarker() {
+ return endMarker;
+ }
+
+ public void setEndMarker(String s) {
+ endMarker = s;
+ }
}
Added: trunk/freenet/src/freenet/support/io/LineReader.java
===================================================================
--- trunk/freenet/src/freenet/support/io/LineReader.java 2006-01-18
14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/support/io/LineReader.java 2006-01-18
16:38:29 UTC (rev 7873)
@@ -0,0 +1,9 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+public interface LineReader {
+
+ public String readLine(int maxLength, int bufferSize) throws
IOException;
+
+}
Modified: trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
2006-01-18 14:35:15 UTC (rev 7872)
+++ trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
2006-01-18 16:38:29 UTC (rev 7873)
@@ -8,7 +8,7 @@
/**
* A FilterInputStream which provides readLine().
*/
-public class LineReadingInputStream extends FilterInputStream {
+public class LineReadingInputStream extends FilterInputStream implements
LineReader {
public LineReadingInputStream(InputStream in) {
super(in);