forked from I2P_Developers/i2p.i2p
i2ptunnel:
- Pass Accept-Encoding header through HTTP client and server proxies, to allow end-to-end compression - Don't do transparent response compression if response Content-Encoding indicates it is already compressed - Minor encoding cleanups EepGet: - Send Accept-Encoding: gzip even when proxied - Minor cleanups
This commit is contained in:
@@ -39,7 +39,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
private final byte _buf1[];
|
||||
protected boolean _gzip;
|
||||
protected long _dataExpected;
|
||||
/** lower-case, trimmed */
|
||||
protected String _contentType;
|
||||
/** lower-case, trimmed */
|
||||
protected String _contentEncoding;
|
||||
|
||||
private static final int CACHE_SIZE = 8*1024;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
@@ -151,7 +154,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
responseLine = (responseLine.trim() + "\r\n");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Response: " + responseLine.trim());
|
||||
out.write(responseLine.getBytes());
|
||||
out.write(DataHelper.getUTF8(responseLine));
|
||||
} else {
|
||||
for (int j = lastEnd+1; j < i; j++) {
|
||||
if (_headerBuffer.getData()[j] == ':') {
|
||||
@@ -160,7 +163,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
if ( (keyLen <= 0) || (valLen < 0) )
|
||||
throw new IOException("Invalid header @ " + j);
|
||||
String key = DataHelper.getUTF8(_headerBuffer.getData(), lastEnd+1, keyLen);
|
||||
String val = null;
|
||||
String val;
|
||||
if (valLen == 0)
|
||||
val = "";
|
||||
else
|
||||
@@ -171,10 +174,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
|
||||
String lcKey = key.toLowerCase(Locale.US);
|
||||
if ("connection".equals(lcKey)) {
|
||||
out.write("Connection: close\r\n".getBytes());
|
||||
out.write(DataHelper.getASCII("Connection: close\r\n"));
|
||||
connectionSent = true;
|
||||
} else if ("proxy-connection".equals(lcKey)) {
|
||||
out.write("Proxy-Connection: close\r\n".getBytes());
|
||||
out.write(DataHelper.getASCII("Proxy-Connection: close\r\n"));
|
||||
proxyConnectionSent = true;
|
||||
} else if ("content-encoding".equals(lcKey) && "x-i2p-gzip".equals(val.toLowerCase(Locale.US))) {
|
||||
_gzip = true;
|
||||
@@ -189,7 +192,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if ("content-type".equals(lcKey)) {
|
||||
// save for compress decision on server side
|
||||
_contentType = val;
|
||||
_contentType = val.toLowerCase(Locale.US);
|
||||
} else if ("content-encoding".equals(lcKey)) {
|
||||
// save for compress decision on server side
|
||||
_contentEncoding = val.toLowerCase(Locale.US);
|
||||
} else if ("set-cookie".equals(lcKey)) {
|
||||
String lcVal = val.toLowerCase(Locale.US);
|
||||
if (lcVal.contains("domain=b32.i2p") ||
|
||||
@@ -203,7 +209,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.write((key.trim() + ": " + val.trim() + "\r\n").getBytes());
|
||||
out.write(DataHelper.getUTF8(key.trim() + ": " + val + "\r\n"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -214,9 +220,9 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
}
|
||||
|
||||
if (!connectionSent)
|
||||
out.write("Connection: close\r\n".getBytes());
|
||||
out.write(DataHelper.getASCII("Connection: close\r\n"));
|
||||
if (!proxyConnectionSent)
|
||||
out.write("Proxy-Connection: close\r\n".getBytes());
|
||||
out.write(DataHelper.getASCII("Proxy-Connection: close\r\n"));
|
||||
|
||||
finishHeaders();
|
||||
|
||||
@@ -237,7 +243,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
protected boolean shouldCompress() { return _gzip; }
|
||||
|
||||
protected void finishHeaders() throws IOException {
|
||||
out.write("\r\n".getBytes()); // end of the headers
|
||||
out.write(DataHelper.getASCII("\r\n")); // end of the headers
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -881,7 +881,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
} else if(lowercaseLine.startsWith("accept")) {
|
||||
// strip the accept-blah headers, as they vary dramatically from
|
||||
// browser to browser
|
||||
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
|
||||
// But allow Accept-Encoding: gzip, deflate
|
||||
if(!lowercaseLine.startsWith("accept-encoding: ") &&
|
||||
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
|
||||
line = null;
|
||||
continue;
|
||||
}
|
||||
@@ -933,8 +935,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
||||
// according to rfc2616 s14.3, this *should* force identity, even if
|
||||
// an explicit q=0 for gzip doesn't. tested against orion.i2p, and it
|
||||
// seems to work.
|
||||
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT)))
|
||||
newRequest.append("Accept-Encoding: \r\n");
|
||||
//if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT)))
|
||||
// newRequest.append("Accept-Encoding: \r\n");
|
||||
if (!usingInternalOutproxy)
|
||||
newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
|
||||
}
|
||||
|
@@ -419,12 +419,14 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
setEntry(headers, "Connection", "close");
|
||||
// we keep the enc sent by the browser before clobbering it, since it may have
|
||||
// been x-i2p-gzip
|
||||
String enc = getEntryOrNull(headers, "Accept-encoding");
|
||||
String altEnc = getEntryOrNull(headers, "X-Accept-encoding");
|
||||
String enc = getEntryOrNull(headers, "Accept-Encoding");
|
||||
String altEnc = getEntryOrNull(headers, "X-Accept-Encoding");
|
||||
|
||||
// according to rfc2616 s14.3, this *should* force identity, even if
|
||||
// "identity;q=1, *;q=0" didn't.
|
||||
setEntry(headers, "Accept-encoding", "");
|
||||
// as of 0.9.23, the client passes this header through, and we do the same,
|
||||
// so if the server and browser can do the compression/decompression, we don't have to
|
||||
//setEntry(headers, "Accept-Encoding", "");
|
||||
|
||||
socket.setReadTimeout(readTimeout);
|
||||
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
|
||||
@@ -432,7 +434,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
// instead of i2ptunnelrunner, use something that reads the HTTP
|
||||
// request from the socket, modifies the headers, sends the request to the
|
||||
// server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip
|
||||
// if it was one of the Accept-encoding: values, and gzip the payload
|
||||
// if it was one of the Accept-Encoding: values, and gzip the payload
|
||||
boolean allowGZIP = true;
|
||||
String val = opts.getProperty("i2ptunnel.gzip");
|
||||
if ( (val != null) && (!Boolean.parseBoolean(val)) )
|
||||
@@ -443,7 +445,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
boolean useGZIP = alt || ( (enc != null) && (enc.indexOf("x-i2p-gzip") >= 0) );
|
||||
// Don't pass this on, outproxies should strip so I2P traffic isn't so obvious but they probably don't
|
||||
if (alt)
|
||||
headers.remove("X-Accept-encoding");
|
||||
headers.remove("X-Accept-Encoding");
|
||||
|
||||
String modifiedHeader = formatHeaders(headers, command);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -671,6 +673,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
|
||||
/**
|
||||
* Don't compress small responses or images.
|
||||
* Don't compress things that are already compressed.
|
||||
* Compression is inline but decompression on the client side
|
||||
* creates a new thread.
|
||||
*/
|
||||
@@ -687,7 +690,11 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
(!_contentType.equals("application/x-bzip")) &&
|
||||
(!_contentType.equals("application/x-bzip2")) &&
|
||||
(!_contentType.equals("application/x-gzip")) &&
|
||||
(!_contentType.equals("application/zip"))));
|
||||
(!_contentType.equals("application/zip")))) &&
|
||||
(_contentEncoding == null ||
|
||||
((!_contentEncoding.equals("gzip")) &&
|
||||
(!_contentEncoding.equals("compress")) &&
|
||||
(!_contentEncoding.equals("deflate"))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -877,9 +884,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
|
||||
String lcName = name.toLowerCase(Locale.US);
|
||||
if ("accept-encoding".equals(lcName))
|
||||
name = "Accept-encoding";
|
||||
name = "Accept-Encoding";
|
||||
else if ("x-accept-encoding".equals(lcName))
|
||||
name = "X-Accept-encoding";
|
||||
name = "X-Accept-Encoding";
|
||||
else if ("x-forwarded-for".equals(lcName))
|
||||
name = "X-Forwarded-For";
|
||||
else if ("x-forwarded-server".equals(lcName))
|
||||
|
@@ -755,6 +755,8 @@ public class EepGet {
|
||||
Thread pusher = null;
|
||||
_decompressException = null;
|
||||
if (_isGzippedResponse) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Gzipped response, starting decompressor");
|
||||
PipedInputStream pi = BigPipedInputStream.getInstance();
|
||||
PipedOutputStream po = new PipedOutputStream(pi);
|
||||
pusher = new I2PAppThread(new Gunzipper(pi, _out), "EepGet Decompressor");
|
||||
@@ -1160,17 +1162,13 @@ public class EepGet {
|
||||
lookahead[1] = lookahead[2];
|
||||
lookahead[2] = (byte)cur;
|
||||
}
|
||||
|
||||
private static boolean isEndOfHeaders(byte lookahead[]) {
|
||||
byte first = lookahead[0];
|
||||
byte second = lookahead[1];
|
||||
byte third = lookahead[2];
|
||||
return (isNL(second) && isNL(third)) || // \n\n
|
||||
(isNL(first) && isNL(third)); // \n\r\n
|
||||
return lookahead[2] == NL &&
|
||||
(lookahead[0] == NL || lookahead[1] == NL); // \n\n or \n\r\n
|
||||
}
|
||||
|
||||
/** we ignore any potential \r, since we trim it on write anyway */
|
||||
private static final byte NL = '\n';
|
||||
private static boolean isNL(byte b) { return (b == NL); }
|
||||
|
||||
/**
|
||||
* @param timeout may be null
|
||||
@@ -1315,7 +1313,8 @@ public class EepGet {
|
||||
buf.append("Content-length: ").append(_postData.length()).append("\r\n");
|
||||
// This will be replaced if we are going through I2PTunnelHTTPClient
|
||||
buf.append("Accept-Encoding: ");
|
||||
if ((!_shouldProxy) &&
|
||||
// as of 0.9.23, the proxy passes the Accept-Encoding header through
|
||||
if ( /* (!_shouldProxy) && */
|
||||
// This is kindof a hack, but if we are downloading a gzip file
|
||||
// we don't want to transparently gunzip it and save it as a .gz file.
|
||||
(!path.endsWith(".gz")) && (!path.endsWith(".tgz")))
|
||||
|
Reference in New Issue
Block a user