synced up with the eepget from the syndie source tree (allowing better

control of timeouts and transparent redirection).  the users of eepget
in this source tree don't necessarily use the timeout controls, though
they can be updated to do so
This commit is contained in:
jrandom
2007-09-08 02:24:01 +00:00
committed by zzz
parent f332513755
commit 20c46cff04
7 changed files with 285 additions and 45 deletions

View File

@ -225,4 +225,5 @@ public class NewsFetcher implements Runnable, EepGet.StatusListener {
temp.delete(); temp.delete();
} }
public void headerReceived(String url, int attemptNum, String key, String val) {} public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
} }

View File

@ -107,6 +107,7 @@ public class ReseedHandler {
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {} public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {} public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
public void headerReceived(String url, int attemptNum, String key, String val) {} public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
// End of EepGet status listeners // End of EepGet status listeners
/** /**

View File

@ -169,6 +169,7 @@ public class UpdateHandler {
System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false"); System.setProperty("net.i2p.router.web.UpdateHandler.updateInProgress", "false");
} }
public void headerReceived(String url, int attemptNum, String key, String val) {} public void headerReceived(String url, int attemptNum, String key, String val) {}
public void attempting(String url) {}
} }
private void restart() { private void restart() {

View File

@ -37,7 +37,10 @@ public class EepGet {
private long _maxSize; // applied both against whole responses and chunks private long _maxSize; // applied both against whole responses and chunks
private String _outputFile; private String _outputFile;
private OutputStream _outputStream; private OutputStream _outputStream;
/** url we were asked to fetch */
private String _url; private String _url;
/** the URL we actually fetch from (may differ from the _url in case of redirect) */
private String _actualURL;
private String _postData; private String _postData;
private boolean _allowCaching; private boolean _allowCaching;
private List _listeners; private List _listeners;
@ -55,42 +58,42 @@ public class EepGet {
private boolean _encodingChunked; private boolean _encodingChunked;
private boolean _notModified; private boolean _notModified;
private String _contentType; private String _contentType;
private boolean _transferFailed;
private boolean _headersRead;
private boolean _aborted;
private long _fetchHeaderTimeout;
private long _fetchEndTime;
private long _fetchInactivityTimeout;
private int _redirects;
private String _redirectLocation;
// Constructor 7, calls 3 with: do proxy
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) { public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url); this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url);
} }
// Constructor 6, calls 1 with: do proxy, no etag
public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching) { public EepGet(I2PAppContext ctx, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching) {
this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url, allowCaching, null); this(ctx, true, proxyHost, proxyPort, numRetries, outputFile, url, allowCaching, null);
} }
// Constructor 5, calls 3 with: no proxy
public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url) { public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url) {
this(ctx, false, null, -1, numRetries, outputFile, url); this(ctx, false, null, -1, numRetries, outputFile, url);
} }
// Constructor 4, calls 1 with: no proxy, no etag
public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url, boolean allowCaching) { public EepGet(I2PAppContext ctx, int numRetries, String outputFile, String url, boolean allowCaching) {
this(ctx, false, null, -1, numRetries, outputFile, url, allowCaching, null); this(ctx, false, null, -1, numRetries, outputFile, url, allowCaching, null);
} }
// Constructor 3, calls 1 with: do caching, no etag
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) { public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url) {
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true, null); this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, outputFile, url, true, null);
} }
// Constructor 2, calls 0 with: no output buffer, do caching, no etag
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, String postData) { public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, String postData) {
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, true, null, postData); this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, true, null, postData);
} }
// Constructor 1, calls 0 with: no output buffer, no postdata
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag) { public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, String outputFile, String url, boolean allowCaching, String etag) {
this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, allowCaching, etag, null); this(ctx, shouldProxy, proxyHost, proxyPort, numRetries, -1, -1, outputFile, null, url, allowCaching, etag, null);
} }
// Constructor 0, real constructor
public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize, public EepGet(I2PAppContext ctx, boolean shouldProxy, String proxyHost, int proxyPort, int numRetries, long minSize, long maxSize,
String outputFile, OutputStream outputStream, String url, boolean allowCaching, String outputFile, OutputStream outputStream, String url, boolean allowCaching,
String etag, String postData) { String etag, String postData) {
_context = ctx; _context = ctx;
_log = ctx.logManager().getLog(EepGet.class); _log = ctx.logManager().getLog(EepGet.class);
_shouldProxy = shouldProxy; _shouldProxy = (proxyHost != null) && (proxyHost.length() > 0) && (proxyPort > 0) && shouldProxy;
_proxyHost = proxyHost; _proxyHost = proxyHost;
_proxyPort = proxyPort; _proxyPort = proxyPort;
_numRetries = numRetries; _numRetries = numRetries;
@ -99,11 +102,16 @@ public class EepGet {
_outputFile = outputFile; // if outputFile is set, outputStream must be null _outputFile = outputFile; // if outputFile is set, outputStream must be null
_outputStream = outputStream; // if both are set, outputStream overrides outputFile _outputStream = outputStream; // if both are set, outputStream overrides outputFile
_url = url; _url = url;
_actualURL = url;
_postData = postData; _postData = postData;
_alreadyTransferred = 0; _alreadyTransferred = 0;
_bytesTransferred = 0; _bytesTransferred = 0;
_bytesRemaining = -1; _bytesRemaining = -1;
_currentAttempt = 0; _currentAttempt = 0;
_transferFailed = false;
_headersRead = false;
_aborted = false;
_fetchHeaderTimeout = 30*1000;
_listeners = new ArrayList(1); _listeners = new ArrayList(1);
_etag = etag; _etag = etag;
} }
@ -203,6 +211,7 @@ public class EepGet {
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause); public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause);
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt); public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt);
public void headerReceived(String url, int currentAttempt, String key, String val); public void headerReceived(String url, int currentAttempt, String key, String val);
public void attempting(String url);
} }
private class CLIStatusListener implements StatusListener { private class CLIStatusListener implements StatusListener {
private int _markSize; private int _markSize;
@ -317,6 +326,7 @@ public class EepGet {
buf.append("KBps"); buf.append("KBps");
System.out.println(buf.toString()); System.out.println(buf.toString());
} }
public void attempting(String url) {}
public void headerReceived(String url, int currentAttempt, String key, String val) {} public void headerReceived(String url, int currentAttempt, String key, String val) {}
} }
@ -329,17 +339,48 @@ public class EepGet {
* Blocking fetch, returning true if the URL was retrieved, false if all retries failed * Blocking fetch, returning true if the URL was retrieved, false if all retries failed
* *
*/ */
public boolean fetch() { public boolean fetch() { return fetch(_fetchHeaderTimeout); }
/**
* Blocking fetch, timing out individual attempts if the HTTP response headers
* don't come back in the time given. If the timeout is zero or less, this will
* wait indefinitely.
*/
public boolean fetch(long fetchHeaderTimeout) {
return fetch(fetchHeaderTimeout, -1, -1);
}
public boolean fetch(long fetchHeaderTimeout, long totalTimeout, long inactivityTimeout) {
_fetchHeaderTimeout = fetchHeaderTimeout;
_fetchEndTime = (totalTimeout > 0 ? System.currentTimeMillis() + totalTimeout : -1);
_fetchInactivityTimeout = inactivityTimeout;
_keepFetching = true; _keepFetching = true;
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _url); _log.debug("Fetching (proxied? " + _shouldProxy + ") url=" + _actualURL);
while (_keepFetching) { while (_keepFetching) {
SocketTimeout timeout = null;
if (_fetchHeaderTimeout > 0)
timeout = new SocketTimeout(_fetchHeaderTimeout);
final SocketTimeout stimeout = timeout; // ugly
timeout.setTimeoutCommand(new Runnable() {
public void run() {
if (_log.shouldLog(Log.DEBUG))
_log.debug("timeout reached on " + _url + ": " + stimeout);
_aborted = true;
}
});
timeout.setTotalTimeoutPeriod(_fetchEndTime);
try { try {
sendRequest(); for (int i = 0; i < _listeners.size(); i++)
doFetch(); ((StatusListener)_listeners.get(i)).attempting(_url);
sendRequest(timeout);
timeout.resetTimer();
doFetch(timeout);
timeout.cancel();
if (!_transferFailed)
return true; return true;
break;
} catch (IOException ioe) { } catch (IOException ioe) {
timeout.cancel();
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
((StatusListener)_listeners.get(i)).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe); ((StatusListener)_listeners.get(i)).attemptFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt, _numRetries, ioe);
} finally { } finally {
@ -360,7 +401,10 @@ public class EepGet {
_currentAttempt++; _currentAttempt++;
if (_currentAttempt > _numRetries) if (_currentAttempt > _numRetries)
break; break;
try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} try {
long delay = _context.random().nextInt(60*1000);
Thread.sleep(5*1000+delay);
} catch (InterruptedException ie) {}
} }
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
@ -368,9 +412,57 @@ public class EepGet {
return false; return false;
} }
/** a single fetch attempt */ /** return true if the URL was completely retrieved */
private void doFetch() throws IOException { private void doFetch(SocketTimeout timeout) throws IOException {
_headersRead = false;
_aborted = false;
try {
readHeaders(); readHeaders();
} finally {
_headersRead = true;
}
if (_aborted)
throw new IOException("Timed out reading the HTTP headers");
timeout.resetTimer();
if (_fetchInactivityTimeout > 0)
timeout.setInactivityTimeout(_fetchInactivityTimeout);
else
timeout.setInactivityTimeout(60*1000);
if (_redirectLocation != null) {
try {
URL oldURL = new URL(_actualURL);
String query = oldURL.getQuery();
if (query == null) query = "";
if (_redirectLocation.startsWith("http://")) {
if ( (_redirectLocation.indexOf('?') < 0) && (query.length() > 0) )
_actualURL = _redirectLocation + "?" + query;
else
_actualURL = _redirectLocation;
} else {
URL url = new URL(_actualURL);
if (_redirectLocation.startsWith("/"))
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + _redirectLocation;
else
_actualURL = "http://" + url.getHost() + ":" + url.getPort() + "/" + _redirectLocation;
if ( (_actualURL.indexOf('?') < 0) && (query.length() > 0) )
_actualURL = _actualURL + "?" + query;
else
_actualURL = _actualURL;
}
} catch (MalformedURLException mue) {
throw new IOException("Redirected from an invalid URL");
}
_redirects++;
if (_redirects > 5)
throw new IOException("Too many redirects: to " + _redirectLocation);
if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation);
sendRequest(timeout);
doFetch(timeout);
return;
}
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Headers read completely, reading " + _bytesRemaining); _log.debug("Headers read completely, reading " + _bytesRemaining);
@ -384,13 +476,14 @@ public class EepGet {
int remaining = (int)_bytesRemaining; int remaining = (int)_bytesRemaining;
byte buf[] = new byte[1024]; byte buf[] = new byte[1024];
while (_keepFetching && ( (remaining > 0) || !strictSize )) { while (_keepFetching && ( (remaining > 0) || !strictSize ) && !_aborted) {
int toRead = buf.length; int toRead = buf.length;
if (strictSize && toRead > remaining) if (strictSize && toRead > remaining)
toRead = remaining; toRead = remaining;
int read = _proxyIn.read(buf, 0, toRead); int read = _proxyIn.read(buf, 0, toRead);
if (read == -1) if (read == -1)
break; break;
timeout.resetTimer();
_out.write(buf, 0, read); _out.write(buf, 0, read);
_bytesTransferred += read; _bytesTransferred += read;
// This seems necessary to properly resume a partial download into a stream, // This seems necessary to properly resume a partial download into a stream,
@ -400,10 +493,26 @@ public class EepGet {
_alreadyTransferred += read; _alreadyTransferred += read;
remaining -= read; remaining -= read;
if (remaining==0 && _encodingChunked) { if (remaining==0 && _encodingChunked) {
if(_proxyIn.read()=='\r' && _proxyIn.read()=='\n') { int char1 = _proxyIn.read();
if (char1 == '\r') {
int char2 = _proxyIn.read();
if (char2 == '\n') {
remaining = (int) readChunkLength(); remaining = (int) readChunkLength();
} else {
_out.write(char1);
_out.write(char2);
_bytesTransferred += 2;
remaining -= 2;
read += 2;
}
} else {
_out.write(char1);
_bytesTransferred++;
remaining--;
read++;
} }
} }
timeout.resetTimer();
if (read > 0) if (read > 0)
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
((StatusListener)_listeners.get(i)).bytesTransferred( ((StatusListener)_listeners.get(i)).bytesTransferred(
@ -418,10 +527,19 @@ public class EepGet {
_out.close(); _out.close();
_out = null; _out = null;
if (_log.shouldLog(Log.DEBUG)) if (_aborted)
_log.debug("Done transferring " + _bytesTransferred); throw new IOException("Timed out reading the HTTP data");
if ( (_bytesRemaining == -1) || (remaining == 0) ){ timeout.cancel();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Done transferring " + _bytesTransferred + " (ok? " + !_transferFailed + ")");
if (_transferFailed) {
// 404, etc
for (int i = 0; i < _listeners.size(); i++)
((StatusListener)_listeners.get(i)).transferFailed(_url, _bytesTransferred, _bytesRemaining, _currentAttempt);
} else if ( (_bytesRemaining == -1) || (remaining == 0) ) {
for (int i = 0; i < _listeners.size(); i++) for (int i = 0; i < _listeners.size(); i++)
((StatusListener)_listeners.get(i)).transferComplete( ((StatusListener)_listeners.get(i)).transferComplete(
_alreadyTransferred, _alreadyTransferred,
@ -442,31 +560,52 @@ public class EepGet {
boolean read = DataHelper.readLine(_proxyIn, buf); boolean read = DataHelper.readLine(_proxyIn, buf);
if (!read) throw new IOException("Unable to read the first line"); if (!read) throw new IOException("Unable to read the first line");
int responseCode = handleStatus(buf.toString()); int responseCode = handleStatus(buf.toString());
boolean redirect = false;
if (_log.shouldLog(Log.DEBUG))
_log.debug("rc: " + responseCode + " for " + _actualURL);
boolean rcOk = false; boolean rcOk = false;
switch (responseCode) { switch (responseCode) {
case 200: // full case 200: // full
if (_outputStream != null) _out = _outputStream; if (_outputStream != null)
else _out = new FileOutputStream(_outputFile, false); _out = _outputStream;
else
_out = new FileOutputStream(_outputFile, false);
_alreadyTransferred = 0; _alreadyTransferred = 0;
rcOk = true; rcOk = true;
break; break;
case 206: // partial case 206: // partial
if (_outputStream != null) _out = _outputStream; if (_outputStream != null)
else _out = new FileOutputStream(_outputFile, true); _out = _outputStream;
else
_out = new FileOutputStream(_outputFile, true);
rcOk = true; rcOk = true;
break; break;
case 301: // various redirections
case 302:
case 303:
case 307:
_alreadyTransferred = 0;
rcOk = true;
redirect = true;
break;
case 304: // not modified case 304: // not modified
_bytesRemaining = 0; _bytesRemaining = 0;
_keepFetching = false; _keepFetching = false;
_notModified = true; _notModified = true;
return; return;
case 404: // not found
_keepFetching = false;
_transferFailed = true;
return;
case 416: // completed (or range out of reach) case 416: // completed (or range out of reach)
_bytesRemaining = 0; _bytesRemaining = 0;
_keepFetching = false; _keepFetching = false;
return; return;
default: default:
rcOk = false; rcOk = false;
_transferFailed = true;
} }
buf.setLength(0); buf.setLength(0);
byte lookahead[] = new byte[3]; byte lookahead[] = new byte[3];
@ -500,6 +639,7 @@ public class EepGet {
if (_encodingChunked) { if (_encodingChunked) {
_bytesRemaining = readChunkLength(); _bytesRemaining = readChunkLength();
} }
if (!redirect) _redirectLocation = null;
return; return;
} }
break; break;
@ -594,6 +734,8 @@ public class EepGet {
_encodingChunked = true; _encodingChunked = true;
} else if (key.equalsIgnoreCase("Content-Type")) { } else if (key.equalsIgnoreCase("Content-Type")) {
_contentType=val; _contentType=val;
} else if (key.equalsIgnoreCase("Location")) {
_redirectLocation=val.trim();
} else { } else {
// ignore the rest // ignore the rest
} }
@ -616,7 +758,7 @@ public class EepGet {
private static final byte NL = '\n'; private static final byte NL = '\n';
private boolean isNL(byte b) { return (b == NL); } private boolean isNL(byte b) { return (b == NL); }
private void sendRequest() throws IOException { private void sendRequest(SocketTimeout timeout) throws IOException {
if (_outputStream != null) { if (_outputStream != null) {
// We are reading into a stream supplied by a caller, // We are reading into a stream supplied by a caller,
// for which we cannot easily determine how much we've written. // for which we cannot easily determine how much we've written.
@ -630,16 +772,24 @@ public class EepGet {
String req = getRequest(); String req = getRequest();
if (_proxyIn != null) try { _proxyIn.close(); } catch (IOException ioe) {}
if (_proxyOut != null) try { _proxyOut.close(); } catch (IOException ioe) {}
if (_proxy != null) try { _proxy.close(); } catch (IOException ioe) {}
if (_shouldProxy) { if (_shouldProxy) {
_proxy = new Socket(_proxyHost, _proxyPort); _proxy = new Socket(_proxyHost, _proxyPort);
} else { } else {
try { try {
URL url = new URL(_url); URL url = new URL(_actualURL);
if ("http".equals(url.getProtocol())) {
String host = url.getHost(); String host = url.getHost();
int port = url.getPort(); int port = url.getPort();
if (port == -1) if (port == -1)
port = 80; port = 80;
_proxy = new Socket(host, port); _proxy = new Socket(host, port);
} else {
throw new IOException("URL is not supported:" + _actualURL);
}
} catch (MalformedURLException mue) { } catch (MalformedURLException mue) {
throw new IOException("Request URL is invalid"); throw new IOException("Request URL is invalid");
} }
@ -647,7 +797,9 @@ public class EepGet {
_proxyIn = _proxy.getInputStream(); _proxyIn = _proxy.getInputStream();
_proxyOut = _proxy.getOutputStream(); _proxyOut = _proxy.getOutputStream();
_proxyOut.write(req.toString().getBytes()); timeout.setSocket(_proxy);
_proxyOut.write(DataHelper.getUTF8(req.toString()));
_proxyOut.flush(); _proxyOut.flush();
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
@ -659,12 +811,24 @@ public class EepGet {
boolean post = false; boolean post = false;
if ( (_postData != null) && (_postData.length() > 0) ) if ( (_postData != null) && (_postData.length() > 0) )
post = true; post = true;
URL url = new URL(_actualURL);
String proto = url.getProtocol();
String host = url.getHost();
int port = url.getPort();
String path = url.getPath();
String query = url.getQuery();
if (query != null)
path = path + "?" + query;
if (!path.startsWith("/"))
path = "/" + path;
if ( (port == 80) || (port == 443) || (port <= 0) ) path = proto + "://" + host + path;
else path = proto + "://" + host + ":" + port + path;
if (_log.shouldLog(Log.DEBUG)) _log.debug("Requesting " + path);
if (post) { if (post) {
buf.append("POST ").append(_url).append(" HTTP/1.1\r\n"); buf.append("POST ").append(_actualURL).append(" HTTP/1.1\r\n");
} else { } else {
buf.append("GET ").append(_url).append(" HTTP/1.1\r\n"); buf.append("GET ").append(_actualURL).append(" HTTP/1.1\r\n");
} }
URL url = new URL(_url);
buf.append("Host: ").append(url.getHost()).append("\r\n"); buf.append("Host: ").append(url.getHost()).append("\r\n");
if (_alreadyTransferred > 0) { if (_alreadyTransferred > 0) {
buf.append("Range: bytes="); buf.append("Range: bytes=");

View File

@ -36,6 +36,7 @@ public class EepGetScheduler implements EepGet.StatusListener {
public void fetch(boolean shouldBlock) { public void fetch(boolean shouldBlock) {
//Checking for a valid index is done in fetchNext, so we don't have to worry about it. //Checking for a valid index is done in fetchNext, so we don't have to worry about it.
if (shouldBlock) { if (shouldBlock) {
while (_curURL < _urls.size())
fetchNext(); fetchNext();
} else { } else {
fetch(); fetch();
@ -77,6 +78,7 @@ public class EepGetScheduler implements EepGet.StatusListener {
_listener.transferFailed(url, bytesTransferred, bytesRemaining, currentAttempt); _listener.transferFailed(url, bytesTransferred, bytesRemaining, currentAttempt);
fetchNext(); fetchNext();
} }
public void attempting(String url) { _listener.attempting(url); }
public void headerReceived(String url, int attemptNum, String key, String val) {} public void headerReceived(String url, int attemptNum, String key, String val) {}
} }

View File

@ -0,0 +1,71 @@
package net.i2p.util;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SocketTimeout implements SimpleTimer.TimedEvent {
private Socket _targetSocket;
private long _startTime;
private long _inactivityDelay;
private long _lastActivity;
private long _totalTimeoutTime;
private boolean _cancelled;
private Runnable _command;
public SocketTimeout(long delay) { this(null, delay); }
public SocketTimeout(Socket socket, long delay) {
_inactivityDelay = delay;
_targetSocket = socket;
_cancelled = false;
_lastActivity = _startTime = System.currentTimeMillis();
_totalTimeoutTime = -1;
SimpleTimer.getInstance().addEvent(SocketTimeout.this, delay);
}
public void timeReached() {
if (_cancelled) return;
if ( ( (_totalTimeoutTime > 0) && (_totalTimeoutTime <= System.currentTimeMillis()) ) ||
(_inactivityDelay + _lastActivity <= System.currentTimeMillis()) ) {
if (_targetSocket != null) {
try {
if (!_targetSocket.isClosed())
_targetSocket.close();
} catch (IOException ioe) {}
}
if (_command != null) _command.run();
} else {
SimpleTimer.getInstance().addEvent(SocketTimeout.this, _inactivityDelay);
}
}
public void cancel() {
_cancelled = true;
SimpleTimer.getInstance().removeEvent(SocketTimeout.this);
}
public void setSocket(Socket s) { _targetSocket = s; }
public void resetTimer() { _lastActivity = System.currentTimeMillis(); }
public void setInactivityTimeout(long timeout) { _inactivityDelay = timeout; }
public void setTotalTimeoutPeriod(long timeoutPeriod) {
if (timeoutPeriod > 0)
_totalTimeoutTime = _startTime + timeoutPeriod;
else
_totalTimeoutTime = -1;
}
public void setTimeoutCommand(Runnable job) { _command = job; }
private static final SimpleDateFormat _fmt = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss.SSS");
private static String ts(long when) { synchronized (_fmt) { return _fmt.format(new Date(when)); } }
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("started on ");
buf.append(ts(_startTime));
buf.append("idle for ");
buf.append(System.currentTimeMillis() - _lastActivity);
buf.append("ms ");
if (_totalTimeoutTime > 0)
buf.append("total timeout at ").append(ts(_totalTimeoutTime));
buf.append("cancelled? ").append(_cancelled);
return buf.toString();
}
}

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
* *
*/ */
public class RouterVersion { public class RouterVersion {
public final static String ID = "$Revision: 1.518 $ $Date: 2007-08-13 14:43:01 $"; public final static String ID = "$Revision: 1.519 $ $Date: 2007-08-23 19:33:33 $";
public final static String VERSION = "0.6.1.29"; public final static String VERSION = "0.6.1.29";
public final static long BUILD = 0; public final static long BUILD = 1;
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID); System.out.println("Router ID: " + RouterVersion.ID);