diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java index 50332dc544..99cdedb267 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/PacketQueue.java @@ -147,6 +147,10 @@ class PacketQueue implements SendMessageStatusListener, Closeable { } options.setTagsToSend(sendTags); options.setTagThreshold(tagThresh); + // CLOSE, RESET, and PING packets unlikely to have a large payload + // and most of the rest of the packet is + // uncompressible: stream ids, signature + options.setGzip(packet.getPayloadSize() > 50); } else if (packet.isFlagSet(FLAGS_INITIAL_TAGS)) { if (con != null) { if (con.isInbound()) @@ -167,6 +171,11 @@ class PacketQueue implements SendMessageStatusListener, Closeable { } options.setTagsToSend(sendTags); options.setTagThreshold(tagThresh); + // SYN packets are likely to have compressible payload, even if + // conn gzip option is false (e.g. snark bitfield, HTTP headers). + // If they don't have a large payload, most of the rest of the packet + // is uncompressible: stream ids, destination and signature + options.setGzip(packet.getPayloadSize() > 50); } else { if (con != null) { if (con.isInbound() && con.getLifetime() < 2*60*1000) diff --git a/core/java/src/net/i2p/client/SendMessageOptions.java b/core/java/src/net/i2p/client/SendMessageOptions.java index 2b72892d75..768161422d 100644 --- a/core/java/src/net/i2p/client/SendMessageOptions.java +++ b/core/java/src/net/i2p/client/SendMessageOptions.java @@ -12,10 +12,15 @@ import net.i2p.data.DateAndFlags; * Static methods are for OutboundClientMessageOneShotJob to decode the * flags field on the router side. * + * GzipOption flags are as of 0.9.36, are client-side only, and are + * not included in the flags field or sent to the router. + * * @since 0.9.2 */ public class SendMessageOptions extends DateAndFlags { + private GzipOption _gzip = GzipOption.DEFAULT; + /** all subject to change */ /** @@ -205,4 +210,33 @@ public class SendMessageOptions extends DateAndFlags { return Reliability.DEFAULT; } } + + /** + * Overrides i2cp.gzip session option and size threshold + * for this message only. + * + * @since 0.9.36 + */ + public enum GzipOption { DEFAULT, GZIP_OFF, GZIP_ON } + + /** + * Overrides i2cp.gzip session option and size threshold + * for this message only. + * + * @return non-null, DEFAULT unless setGzip() was called + * @since 0.9.36 + */ + public GzipOption getGzip() { + return _gzip; + } + + /** + * Overrides i2cp.gzip session option and size threshold + * for this message only. + * + * @since 0.9.36 + */ + public void setGzip(boolean yes) { + _gzip = yes? GzipOption.GZIP_ON : GzipOption.GZIP_OFF; + } } diff --git a/core/java/src/net/i2p/client/impl/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/impl/I2PSessionMuxedImpl.java index 049bd3d91b..42d35c5b01 100644 --- a/core/java/src/net/i2p/client/impl/I2PSessionMuxedImpl.java +++ b/core/java/src/net/i2p/client/impl/I2PSessionMuxedImpl.java @@ -217,7 +217,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 { SessionKey keyUsed, Set tagsSent, long expires, int proto, int fromPort, int toPort, int flags) throws I2PSessionException { - payload = prepPayload(payload, offset, size, proto, fromPort, toPort); + payload = prepPayload(payload, offset, size, proto, fromPort, toPort, SendMessageOptions.GzipOption.DEFAULT); if (_noEffort) return sendNoEffort(dest, payload, expires, flags); else @@ -242,7 +242,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 { @Override public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, int proto, int fromPort, int toPort, SendMessageOptions options) throws I2PSessionException { - payload = prepPayload(payload, offset, size, proto, fromPort, toPort); + payload = prepPayload(payload, offset, size, proto, fromPort, toPort, options.getGzip()); //if (_noEffort) { sendNoEffort(dest, payload, options); return true; @@ -266,7 +266,7 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 { public long sendMessage(Destination dest, byte[] payload, int offset, int size, int proto, int fromPort, int toPort, SendMessageOptions options, SendMessageStatusListener listener) throws I2PSessionException { - payload = prepPayload(payload, offset, size, proto, fromPort, toPort); + payload = prepPayload(payload, offset, size, proto, fromPort, toPort, options.getGzip()); long nonce = _sendMessageNonce.incrementAndGet(); long expires = Math.max(_context.clock().now() + 60*1000L, options.getTime()); MessageState state = new MessageState(_context, nonce, this, expires, listener); @@ -279,11 +279,19 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 { * @return gzip compressed payload, ready to send * @since 0.9.14 */ - private byte[] prepPayload(byte[] payload, int offset, int size, int proto, int fromPort, int toPort) throws I2PSessionException { + private byte[] prepPayload(byte[] payload, int offset, int size, int proto, + int fromPort, int toPort, + SendMessageOptions.GzipOption gzo) throws I2PSessionException { verifyOpen(); updateActivity(); - if (shouldCompress(size)) + boolean docompress; + if (gzo == SendMessageOptions.GzipOption.DEFAULT) + docompress = shouldCompress(size); + else + docompress = gzo == SendMessageOptions.GzipOption.GZIP_ON; + + if (docompress) payload = DataHelper.compress(payload, offset, size); else payload = DataHelper.compress(payload, offset, size, DataHelper.NO_COMPRESSION);