* I2PTunnelHTTPServer: Don't compress small responses or images

This commit is contained in:
zzz
2011-09-19 23:50:25 +00:00
parent 6630c29071
commit e6d44a6199
2 changed files with 52 additions and 6 deletions

View File

@ -46,6 +46,9 @@ class HTTPResponseOutputStream extends FilterOutputStream {
private final byte _buf1[]; private final byte _buf1[];
protected boolean _gzip; protected boolean _gzip;
private long _dataWritten; private long _dataWritten;
protected long _dataExpected;
protected String _contentType;
private static final int CACHE_SIZE = 8*1024; private static final int CACHE_SIZE = 8*1024;
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE); private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
// OOM DOS prevention // OOM DOS prevention
@ -130,10 +133,11 @@ class HTTPResponseOutputStream extends FilterOutputStream {
} }
/** /**
* Tweak that first HTTP response line (HTTP 200 OK, etc) * Possibly tweak that first HTTP response line (HTTP/1.0 200 OK, etc).
* Overridden on server side.
* *
*/ */
protected static String filterResponseLine(String line) { protected String filterResponseLine(String line) {
return line; return line;
} }
@ -184,6 +188,15 @@ class HTTPResponseOutputStream extends FilterOutputStream {
} else if ("Proxy-Authenticate".equalsIgnoreCase(key)) { } else if ("Proxy-Authenticate".equalsIgnoreCase(key)) {
// filter this hop-by-hop header; outproxy authentication must be configured in I2PTunnelHTTPClient // filter this hop-by-hop header; outproxy authentication must be configured in I2PTunnelHTTPClient
} else { } else {
if ("Content-Length".equalsIgnoreCase(key)) {
// save for compress decision on server side
try {
_dataExpected = Long.parseLong(val);
} catch (NumberFormatException nfe) {}
} else if ("Content-Type".equalsIgnoreCase(key)) {
// save for compress decision on server side
_contentType = val;
}
out.write((key.trim() + ": " + val.trim() + "\r\n").getBytes()); out.write((key.trim() + ": " + val.trim() + "\r\n").getBytes());
} }
break; break;

View File

@ -303,19 +303,48 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
} }
} }
/**
* This plus a typ. HTTP response header will fit into a 1730-byte streaming message.
*/
private static final int MIN_TO_COMPRESS = 1300;
private static class CompressedResponseOutputStream extends HTTPResponseOutputStream { private static class CompressedResponseOutputStream extends HTTPResponseOutputStream {
private InternalGZIPOutputStream _gzipOut; private InternalGZIPOutputStream _gzipOut;
public CompressedResponseOutputStream(OutputStream o) { public CompressedResponseOutputStream(OutputStream o) {
super(o); super(o);
_dataExpected = -1;
} }
/**
* Overridden to peek at response code. Always returns line.
*/
@Override @Override
protected boolean shouldCompress() { return true; } protected String filterResponseLine(String line) {
String[] s = line.split(" ", 3);
if (s.length > 1 &&
(s[1].startsWith("3") || s[1].startsWith("5")))
_dataExpected = 0;
return line;
}
/**
* Don't compress small responses or images.
* Compression is inline but decompression on the client side
* creates a new thread.
*/
@Override
protected boolean shouldCompress() {
return (_dataExpected < 0 || _dataExpected >= MIN_TO_COMPRESS) &&
(_contentType == null || !_contentType.startsWith("image/"));
}
@Override @Override
protected void finishHeaders() throws IOException { protected void finishHeaders() throws IOException {
//if (_log.shouldLog(Log.INFO)) //if (_log.shouldLog(Log.INFO))
// _log.info("Including x-i2p-gzip as the content encoding in the response"); // _log.info("Including x-i2p-gzip as the content encoding in the response");
out.write("Content-encoding: x-i2p-gzip\r\n".getBytes()); if (shouldCompress())
out.write("Content-encoding: x-i2p-gzip\r\n".getBytes());
super.finishHeaders(); super.finishHeaders();
} }
@ -324,9 +353,12 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
//if (_log.shouldLog(Log.INFO)) //if (_log.shouldLog(Log.INFO))
// _log.info("Beginning compression processing"); // _log.info("Beginning compression processing");
//out.flush(); //out.flush();
_gzipOut = new InternalGZIPOutputStream(out); if (shouldCompress()) {
out = _gzipOut; _gzipOut = new InternalGZIPOutputStream(out);
out = _gzipOut;
}
} }
public long getTotalRead() { public long getTotalRead() {
InternalGZIPOutputStream gzipOut = _gzipOut; InternalGZIPOutputStream gzipOut = _gzipOut;
if (gzipOut != null) if (gzipOut != null)
@ -334,6 +366,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
else else
return 0; return 0;
} }
public long getTotalCompressed() { public long getTotalCompressed() {
InternalGZIPOutputStream gzipOut = _gzipOut; InternalGZIPOutputStream gzipOut = _gzipOut;
if (gzipOut != null) if (gzipOut != null)