Author: dbkr
Date: 2008-04-22 22:38:33 +0000 (Tue, 22 Apr 2008)
New Revision: 19505
Added:
trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java
Modified:
trunk/apps/Freemail/src/freemail/InboundContact.java
trunk/apps/Freemail/src/freemail/NIMFetcher.java
trunk/apps/Freemail/src/freemail/OutboundContact.java
trunk/apps/Freemail/src/freemail/RTSFetcher.java
trunk/apps/Freemail/src/freemail/SlotManager.java
trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
Log:
Change FCP API so fetches throw exceptions on failure. More checking of fetch
failure codes, especially when fetching RTS messages. Ignore RTS slots whose
fetches have produced fatal errors.
Modified: trunk/apps/Freemail/src/freemail/InboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/InboundContact.java 2008-04-22
21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/InboundContact.java 2008-04-22
22:38:33 UTC (rev 19505)
@@ -34,6 +34,7 @@
import freemail.utils.PropsFile;
import freemail.utils.EmailAddress;
import freemail.utils.Logger;
+import freemail.fcp.FCPFetchException;
import freemail.fcp.HighLevelFCPClient;
import freemail.fcp.ConnectionTerminatedException;
@@ -106,9 +107,9 @@
msg = fcpcli.fetch(key);
} catch (ConnectionTerminatedException cte) {
return;
- }
- if (msg == null) {
- Logger.minor(this,"No mail there.");
+ } catch (FCPFetchException fe) {
+ // XXX: Slot should be marked dead if this is a
fatal error
+ Logger.minor(this,"No mail in slot (fetch
returned "+fe.getMessage()+")");
continue;
}
Logger.normal(this,"Found a message!");
@@ -212,14 +213,15 @@
if (sd.indexOf("\r") > 0 || sd.indexOf("\n") > 0)
return false;
Logger.normal(this,"Attempting to fetch sender's
mailsite to validate From address...");
- File result =
cli.fetch("KSK@"+sd+MailSite.ALIAS_SUFFIX);
-
- if (result == null) {
+ File result;
+ try {
+ result =
cli.fetch("KSK@"+sd+MailSite.ALIAS_SUFFIX);
+ } catch (FCPFetchException fe) {
// we just received the message so we can
assume our
// network connection is healthy, and the
mailsite
// ought to be easily retrievable, so fail.
// If this proves to be an issue, change it.
- Logger.error(this,"Failed to fetch sender's
mailsite. Sender's From address therefore not valid.");
+ Logger.error(this,"Failed to fetch sender's
mailsite ("+fe.getMessage()+"). Sender's From address therefore not valid.");
return false;
}
Logger.normal(this,"Fetched sender's mailsite");
Modified: trunk/apps/Freemail/src/freemail/NIMFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/NIMFetcher.java 2008-04-22 21:37:38 UTC
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/NIMFetcher.java 2008-04-22 22:38:33 UTC
(rev 19505)
@@ -21,6 +21,7 @@
package freemail;
+import freemail.fcp.FCPFetchException;
import freemail.fcp.HighLevelFCPClient;
import freemail.fcp.ConnectionTerminatedException;
import freemail.utils.DateStringFactory;
@@ -87,19 +88,21 @@
for (int i = startnum; i < startnum + POLL_AHEAD; i++) {
Logger.normal(this,"trying to fetch "+keybase+i);
- File result = fcpcli.fetch(keybase+i);
+ File result;
+ try {
+ result = fcpcli.fetch(keybase+i);
+ } catch (FCPFetchException fe) {
+ Logger.normal(this,keybase+i+": no message
("+fe.getMessage()+").");
+ continue;
+ }
- if (result != null) {
- Logger.normal(this,keybase+i+": got message!");
- try {
- this.storeMessage(new
BufferedReader(new FileReader(result)), this.mb);
- result.delete();
- log.addMessage(i, "received");
- } catch (IOException ioe) {
- continue;
- }
- } else {
- Logger.normal(this,keybase+i+": no message.");
+ Logger.normal(this,keybase+i+": got message!");
+ try {
+ this.storeMessage(new BufferedReader(new
FileReader(result)), this.mb);
+ result.delete();
+ log.addMessage(i, "received");
+ } catch (IOException ioe) {
+ continue;
}
}
}
Modified: trunk/apps/Freemail/src/freemail/OutboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/OutboundContact.java 2008-04-22
21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/OutboundContact.java 2008-04-22
22:38:33 UTC (rev 19505)
@@ -37,6 +37,7 @@
import freemail.utils.EmailAddress;
import freemail.utils.PropsFile;
import freemail.utils.DateStringFactory;
+import freemail.fcp.FCPFetchException;
import freemail.fcp.HighLevelFCPClient;
import freemail.fcp.FCPInsertErrorMessage;
import freemail.fcp.FCPBadFileException;
@@ -170,9 +171,15 @@
HighLevelFCPClient fcpcli = new HighLevelFCPClient();
Logger.minor(this,"polling for CTS message: "+ctskey);
- File cts = fcpcli.fetch(ctskey);
-
- if (cts == null) {
+ try {
+ File cts = fcpcli.fetch(ctskey);
+
+ Logger.normal(this,"Sucessfully received CTS
for "+this.address.getSubDomain());
+ cts.delete();
+ this.contactfile.put("status", "cts-received");
+ // delete initial slot for forward secrecy
+ this.contactfile.remove("initialslot");
+ } catch (FCPFetchException fe) {
Logger.minor(this,"CTS not received");
// haven't got the CTS message. should we give
up yet?
String senttime =
this.contactfile.get("rts-sent-at");
@@ -181,13 +188,6 @@
// yes, send another RTS
this.init();
}
-
- } else {
- Logger.normal(this,"Sucessfully received CTS
for "+this.address.getSubDomain());
- cts.delete();
- this.contactfile.put("status", "cts-received");
- // delete initial slot for forward secrecy
- this.contactfile.remove("initialslot");
}
} else {
this.init();
@@ -436,10 +436,11 @@
HighLevelFCPClient cli = new HighLevelFCPClient();
Logger.normal(this,"Attempting to fetch mailsite redirect
"+key);
- File result = cli.fetch(key);
-
- if (result == null) {
- Logger.normal(this,"Failed to retrieve mailsite
redirect "+key);
+ File result;
+ try {
+ result = cli.fetch(key);
+ } catch (FCPFetchException fe) {
+ Logger.normal(this,"Failed to retrieve mailsite
redirect "+key+" ("+fe.getMessage()+")");
return null;
}
@@ -472,9 +473,10 @@
HighLevelFCPClient cli = new HighLevelFCPClient();
Logger.normal(this,"Attempting to fetch
"+this.address.getMailpageKey());
- File mailsite_file = cli.fetch(this.address.getMailpageKey());
-
- if (mailsite_file == null) {
+ File mailsite_file;
+ try {
+ mailsite_file =
cli.fetch(this.address.getMailpageKey());
+ } catch (FCPFetchException fe) {
Logger.normal(this,"Failed to retrieve mailsite
"+this.address.getMailpageKey());
return false;
}
@@ -698,8 +700,8 @@
Logger.minor(this,"Looking for message ack on "+key);
- File ack = fcpcli.fetch(key);
- if (ack != null) {
+ try {
+ File ack = fcpcli.fetch(key);
Logger.normal(this,"Ack received for message
"+msgs[i].uid+" on contact "+this.address.domain+". Now that's a job well
done.");
ack.delete();
msgs[i].delete();
@@ -707,24 +709,26 @@
this.contactfile.put("status", "cts-received");
// delete initial slot for forward secrecy
this.contactfile.remove("initialslot");
- } else {
- Logger.minor(this,"Failed to receive ack on
"+key);
- if (System.currentTimeMillis() >
msgs[i].first_send_time + FAIL_DELAY) {
- // give up and bounce the message
- File m = msgs[i].getMessageFile();
-
- Postman.bounceMessage(m,
account.getMessageBank(),
- "Freemail has been
trying for too long to deliver this message, and has received no
acknowledgement. "
- +"It is possible that
the recipient has not run Freemail since you sent the message. "
- +"If you believe this
is likely, try resending the message.", true);
- Logger.normal(this,"Giving up on
message - been trying for too long.");
- msgs[i].delete();
- } else if (System.currentTimeMillis() >
msgs[i].last_send_time + RETRANSMIT_DELAY) {
- // no ack yet - retransmit on another
slot
- msgs[i].slot = this.popNextSlot();
- // mark for re-insertion
- msgs[i].last_send_time = -1;
- msgs[i].saveProps();
+ } catch (FCPFetchException fe) {
+ Logger.minor(this,"Failed to receive ack on
"+key+" ("+fe.getMessage()+")");
+ if (!fe.isNetworkError()) {
+ if (System.currentTimeMillis() >
msgs[i].first_send_time + FAIL_DELAY) {
+ // give up and bounce the
message
+ File m =
msgs[i].getMessageFile();
+
+ Postman.bounceMessage(m,
account.getMessageBank(),
+ "Freemail has
been trying for too long to deliver this message, and has received no
acknowledgement. "
+ +"It is
possible that the recipient has not run Freemail since you sent the message. "
+ +"If you
believe this is likely, try resending the message.", true);
+ Logger.normal(this,"Giving up
on message - been trying for too long.");
+ msgs[i].delete();
+ } else if (System.currentTimeMillis() >
msgs[i].last_send_time + RETRANSMIT_DELAY) {
+ // no ack yet - retransmit on
another slot
+ msgs[i].slot =
this.popNextSlot();
+ // mark for re-insertion
+ msgs[i].last_send_time = -1;
+ msgs[i].saveProps();
+ }
}
}
}
Modified: trunk/apps/Freemail/src/freemail/RTSFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/RTSFetcher.java 2008-04-22 21:37:38 UTC
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/RTSFetcher.java 2008-04-22 22:38:33 UTC
(rev 19505)
@@ -21,6 +21,7 @@
package freemail;
+import freemail.fcp.FCPFetchException;
import freemail.fcp.HighLevelFCPClient;
import freemail.fcp.ConnectionTerminatedException;
import freemail.utils.DateStringFactory;
@@ -157,9 +158,9 @@
while ( (slot = sm.getNextSlotNat()) > 0) {
Logger.minor(this,"trying to fetch "+keybase+slot);
- File result = fcpcli.fetch(keybase+slot);
-
- if (result != null) {
+ try {
+ File result = fcpcli.fetch(keybase+slot);
+
Logger.normal(this,keybase+slot+": got RTS!");
File rts_dest = new File(this.contact_dir,
RTS_UNPROC_PREFIX + "-" + log.getAndIncUnprocNextId()+",0");
@@ -169,8 +170,23 @@
// provided that worked, we can move on
to the next RTS message
sm.slotUsed();
}
- } else {
- Logger.minor(this,keybase+slot+": no RTS.");
+ } catch (FCPFetchException fe) {
+ if (fe.isFatal()) {
+ Logger.error(this,keybase+slot+": fatal
fetch error - marking slot as used.");
+ sm.slotUsed();
+ } else if (fe.getCode() ==
FCPFetchException.ALL_DATA_NOT_FOUND) {
+ // This could be the node not managing
to find the CHK containing the actual data (since RTS messages are
+ // over 1KB, the node will opaquely
insert them as a KSK redirect to a CHK, since a KSK/SSK can only hold
+ // 1KB of data). It could also be
someone inserting dummy redirects to our RTS queue. We'll have to keep
+ // checking it, but we have to check
slots until we find some that are really empty, we'd never manage
+ // to fetch anything if they are dead
keys.
+ Logger.error(this,keybase+slot+": All
Data not found - leaving slot in queue and will poll an extra key");
+ sm.incPollAhead();
+ } else if (fe.getCode() ==
FCPFetchException.DATA_NOT_FOUND) {
+ Logger.minor(this,keybase+slot+": no
RTS.");
+ } else {
+ Logger.minor(this,keybase+slot+": other
non-fatal fetch error:"+fe.getMessage());
+ }
}
}
}
@@ -300,9 +316,10 @@
Logger.normal(this,"Trying to fetch sender's mailsite:
"+their_mailsite);
-
- File msfile = fcpcli.fetch(their_mailsite);
- if (msfile == null) {
+ File msfile;
+ try {
+ msfile = fcpcli.fetch(their_mailsite);
+ } catch (FCPFetchException fe) {
// oh well, try again in a bit
rtsfile.delete();
return false;
Modified: trunk/apps/Freemail/src/freemail/SlotManager.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SlotManager.java 2008-04-22 21:37:38 UTC
(rev 19504)
+++ trunk/apps/Freemail/src/freemail/SlotManager.java 2008-04-22 22:38:33 UTC
(rev 19505)
@@ -70,6 +70,10 @@
this.pollAhead = pa;
}
+ public void incPollAhead() {
+ ++pollAhead;
+ }
+
/** Mark the last given slot as used
*/
public synchronized void slotUsed() {
Added: trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java
===================================================================
--- trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java
(rev 0)
+++ trunk/apps/Freemail/src/freemail/fcp/FCPFetchException.java 2008-04-22
22:38:33 UTC (rev 19505)
@@ -0,0 +1,133 @@
+package freemail.fcp;
+
+import freemail.utils.Logger;
+
+public class FCPFetchException extends Exception {
+ static final long serialVersionUID = -1;
+
+ // The following code shamelessly stolen from Freenet's
FetchException.java (but reordered)
+ /** Too many levels of recursion into archives */
+ public static final int TOO_DEEP_ARCHIVE_RECURSION = 1;
+ /** Don't know what to do with splitfile */
+ public static final int UNKNOWN_SPLITFILE_METADATA = 2;
+ /** Don't know what to do with metadata */
+ public static final int UNKNOWN_METADATA = 3;
+ /** Got a MetadataParseException */
+ public static final int INVALID_METADATA = 4;
+ /** Got an ArchiveFailureException */
+ public static final int ARCHIVE_FAILURE = 5;
+ /** Failed to decode a block */
+ public static final int BLOCK_DECODE_ERROR = 6;
+ /** Too many split metadata levels */
+ public static final int TOO_MANY_METADATA_LEVELS = 7;
+ /** Too many archive restarts */
+ public static final int TOO_MANY_ARCHIVE_RESTARTS = 8;
+ /** Too deep recursion */
+ public static final int TOO_MUCH_RECURSION = 9;
+ /** Tried to access an archive file but not in an archive */
+ public static final int NOT_IN_ARCHIVE = 10;
+ /** Too many meta strings. E.g. requesting CHK at blah,blah,blah as CHK
at blah,blah,blah/filename.ext */
+ public static final int TOO_MANY_PATH_COMPONENTS = 11;
+ /** Failed to read from or write to a bucket; a kind of internal error
*/
+ public static final int BUCKET_ERROR = 12;
+ /** Data not found */
+ public static final int DATA_NOT_FOUND = 13;
+ /** Route not found */
+ public static final int ROUTE_NOT_FOUND = 14;
+ /** Downstream overload */
+ public static final int REJECTED_OVERLOAD = 15;
+ /** Too many redirects */
+ public static final int TOO_MANY_REDIRECTS = 16;
+ /** An internal error occurred */
+ public static final int INTERNAL_ERROR = 17;
+ /** The node found the data but the transfer failed */
+ public static final int TRANSFER_FAILED = 18;
+ /** Splitfile error. This should be a SplitFetchException. */
+ public static final int SPLITFILE_ERROR = 19;
+ /** Invalid URI. */
+ public static final int INVALID_URI = 20;
+ /** Too big */
+ public static final int TOO_BIG = 21;
+ /** Metadata too big */
+ public static final int TOO_BIG_METADATA = 22;
+ /** Splitfile has too big segments */
+ 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_PATH_COMPONENTS = 24;
+ /** Explicitly cancelled */
+ public static final int CANCELLED = 25;
+ /** Archive restart */
+ public static final int ARCHIVE_RESTART = 26;
+ /** There is a more recent version of the USK, ~= HTTP 301; FProxy will
turn this into a 301 */
+ public static final int PERMANENT_REDIRECT = 27;
+ /** Not all data was found; some DNFs but some successes */
+ public static final int ALL_DATA_NOT_FOUND = 28;
+ /** Requestor specified a list of allowed MIME types, and the key's
type wasn't in the list */
+ public static final int WRONG_MIME_TYPE = 29;
+ /** A node killed the request because it had recently been tried and
had DNFed */
+ public static final int RECENTLY_FAILED = 30;
+
+ private final FCPMessage fcpMessage;
+
+ public FCPFetchException(FCPMessage fcpmsg) {
+ fcpMessage = fcpmsg;
+ }
+
+ public FCPMessage getFailureMessage() {
+ return fcpMessage;
+ }
+
+ public int getCode() {
+ String strCode = (String)fcpMessage.headers.get("Code");
+ if (strCode == null) {
+ Logger.error(this, "FCP error message with no error
code!");
+ return 0;
+ }
+ return Integer.parseInt(strCode);
+ }
+
+ public String getMessage() {
+ String msg =
(String)fcpMessage.headers.get("ShortCodeDescription");
+ if (msg != null) return msg;
+
+ // No short description? try the long one.
+ msg = (String)fcpMessage.headers.get("CodeDescription");
+ if (msg != null) return msg;
+
+ // No? Does it have a code?
+ msg = (String)fcpMessage.headers.get("Code");
+ if (msg != null) return "Error number "+msg+" (no description
given)";
+
+ // Give up then
+ return "Unknown error (no hints given by the node)";
+ }
+
+ /**
+ * @return true if the error is the fault of the network or our
connection to it, and has no bearing on whether or not
+ * the key itself is present or not.
+ */
+ public boolean isNetworkError() {
+ switch (getCode()) {
+ case BUCKET_ERROR:
+ case ROUTE_NOT_FOUND:
+ case REJECTED_OVERLOAD:
+ case INTERNAL_ERROR:
+ case TRANSFER_FAILED:
+ case CANCELLED:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if all future requests for this this key will fail too
+ */
+ public boolean isFatal() {
+ String fatal = (String)fcpMessage.headers.get("Fatal");
+ if (fatal == null) {
+ Logger.error(this, "No 'fatal' field found - FCP
protocol change? Assuming not fatal.");
+ return false;
+ }
+ return fatal.equalsIgnoreCase("true");
+ }
+}
Modified: trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
===================================================================
--- trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
2008-04-22 21:37:38 UTC (rev 19504)
+++ trunk/apps/Freemail/src/freemail/fcp/HighLevelFCPClient.java
2008-04-22 22:38:33 UTC (rev 19505)
@@ -43,7 +43,7 @@
// It's up to the client to delete this File once they're
// done with it
- public synchronized File fetch(String key) throws
ConnectionTerminatedException {
+ public synchronized File fetch(String key) throws
ConnectionTerminatedException, FCPFetchException {
FCPMessage msg = this.conn.getMessage("ClientGet");
msg.headers.put("URI", key);
msg.headers.put("ReturnType", "direct");
@@ -55,8 +55,8 @@
break;
} catch (NoNodeConnectionException nnce) {
try {
- Logger.error(this,"got no conn
exception: "+nnce.getMessage());
- Thread.sleep(5000);
+ Logger.error(this,"No Freenet node
available - waiting: "+nnce.getMessage());
+ Thread.sleep(10000);
} catch (InterruptedException ie) {
}
} catch (FCPBadFileException bfe) {
@@ -84,9 +84,9 @@
if (newuri == null) return null;
return this.fetch(newuri);
}
- return null;
+ throw new FCPFetchException(donemsg);
} else {
- return null;
+ throw new FCPFetchException(donemsg);
}
}