diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index 57f4abe026da1f8d04fb6dd8b674613ccde97917..25116721a6a1d99a1964071cec0a9e594bbf280d 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -55,6 +55,8 @@ public class EepGet { protected long _bytesTransferred; protected long _bytesRemaining; protected int _currentAttempt; + protected int _responseCode = -1; + protected boolean _shouldWriteErrorToOutput; protected String _etag; protected String _lastModified; protected boolean _encodingChunked; @@ -245,7 +247,14 @@ public class EepGet { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified); 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); + + /** + * Note: Headers are not processed, and this is not called, for most error response codes, + * unless setWriteErrorToOutput() is called before fetch(). + * To be changed? + */ public void headerReceived(String url, int currentAttempt, String key, String val); + public void attempting(String url); } protected class CLIStatusListener implements StatusListener { @@ -560,7 +569,7 @@ public class EepGet { throw new IOException("HTTP response size " + _bytesRemaining + " violates maximum of " + _maxSize + " bytes"); int remaining = (int)_bytesRemaining; - byte buf[] = new byte[1024]; + byte buf[] = new byte[8*1024]; while (_keepFetching && ( (remaining > 0) || !strictSize ) && !_aborted) { int toRead = buf.length; if (strictSize && toRead > remaining) @@ -654,13 +663,13 @@ public class EepGet { boolean read = DataHelper.readLine(_proxyIn, buf); if (!read) throw new IOException("Unable to read the first line"); - int responseCode = handleStatus(buf.toString()); + _responseCode = handleStatus(buf.toString()); boolean redirect = false; if (_log.shouldLog(Log.DEBUG)) - _log.debug("rc: " + responseCode + " for " + _actualURL); + _log.debug("rc: " + _responseCode + " for " + _actualURL); boolean rcOk = false; - switch (responseCode) { + switch (_responseCode) { case 200: // full if (_outputStream != null) _out = _outputStream; @@ -693,20 +702,51 @@ public class EepGet { case 404: // not found case 409: // bad addr helper case 503: // no outproxy - _keepFetching = false; _transferFailed = true; - // maybe we should throw instead of return to get the return code back to the user - return; + if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) { + _keepFetching = false; + return; + } + // output the error data to the stream + rcOk = true; + if (_out == null) { + if (_outputStream != null) + _out = _outputStream; + else + _out = new FileOutputStream(_outputFile, true); + } + break; case 416: // completed (or range out of reach) _bytesRemaining = 0; - _keepFetching = false; - return; + if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) { + _keepFetching = false; + return; + } + // output the error data to the stream + rcOk = true; + if (_out == null) { + if (_outputStream != null) + _out = _outputStream; + else + _out = new FileOutputStream(_outputFile, true); + } + break; case 504: // gateway timeout // throw out of doFetch() to fetch() and try again throw new IOException("HTTP Proxy timeout"); default: - rcOk = false; - _keepFetching = false; + if (_alreadyTransferred == 0 && !_shouldWriteErrorToOutput) { + _keepFetching = false; + } else { + // output the error data to the stream + rcOk = true; + if (_out == null) { + if (_outputStream != null) + _out = _outputStream; + else + _out = new FileOutputStream(_outputFile, true); + } + } _transferFailed = true; } @@ -742,7 +782,7 @@ public class EepGet { increment(lookahead, cur); if (isEndOfHeaders(lookahead)) { if (!rcOk) - throw new IOException("Invalid HTTP response code: " + responseCode); + throw new IOException("Invalid HTTP response code: " + _responseCode); if (_encodingChunked) { _bytesRemaining = readChunkLength(); } @@ -991,4 +1031,34 @@ public class EepGet { public String getContentType() { return _contentType; } + + /** + * The server response (200, etc). + * @return -1 if invalid, or if the proxy never responded, + * or if no proxy was used and the server never responded. + * If a non-proxied request partially succeeded (for example a redirect followed + * by a fail, or a partial fetch followed by a fail), this will + * be the last status code received. + * Note that fetch() may return false even if this returns 200. + * + * @since 0.8.8 + */ + public int getStatusCode() { + return _responseCode; + } + + /** + * If called (before calling fetch()), + * data from the server or proxy will be written to the + * output file or stream even on an error response code (4xx, 5xx, etc). + * The error data will only be written if no previous data was written + * on an earlier try. + * Caller must of course check getStatusCode() or the + * fetch() return value. + * + * @since 0.8.8 + */ + public void setWriteErrorToOutput() { + _shouldWriteErrorToOutput = true; + } }