Refactored DeflateCompressionCodec to eliminate memory leak. Refactored GzipCompressionCodec and AbstractCompressionCodec to utilize consistent logic across compression algorithms. Resolves #392.

This commit is contained in:
Les Hazlewood 2018-10-13 16:42:37 -04:00
parent 1b5b89304a
commit af72fabbf3
4 changed files with 75 additions and 68 deletions

View File

@ -1,10 +1,14 @@
## Release Notes ## Release Notes
### 0.10.7
This patch release fixes a [memory leak](https://github.com/jwtk/jjwt/issues/392) found in the DEFLATE compression
codec implementation.
### 0.10.6 ### 0.10.6
This patch release updates the jackson-databind version to 2.9.8 to address a critical security vulnerability in that This patch release updates the jackson-databind version to 2.9.8 to address a critical security vulnerability in that
library. It also updates .travis.yml to remove deprecated builds and to "trick" travis into building with Oracle library.
JDK 1.7 as this library still supports that Java version even if Travis no longer does.
### 0.10.5 ### 0.10.5

View File

@ -18,8 +18,12 @@ package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionException; import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.lang.Assert; import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/** /**
* Abstract class that asserts arguments and wraps IOException with CompressionException. * Abstract class that asserts arguments and wraps IOException with CompressionException.
@ -28,6 +32,44 @@ import java.io.IOException;
*/ */
public abstract class AbstractCompressionCodec implements CompressionCodec { public abstract class AbstractCompressionCodec implements CompressionCodec {
//package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc).
//TODO: make protected on a minor release
interface StreamWrapper {
OutputStream wrap(OutputStream out) throws IOException;
}
//package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc).
//TODO: make protected on a minor release
byte[] readAndClose(InputStream input) throws IOException {
byte[] buffer = new byte[512];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int read;
try {
read = input.read(buffer); //assignment separate from loop invariant check for code coverage checks
while (read != -1) {
out.write(buffer, 0, read);
read = input.read(buffer);
}
} finally {
Objects.nullSafeClose(input);
}
return out.toByteArray();
}
//package-protected for a point release. This can be made protected on a minor release (0.11.0, 0.12.0, 1.0, etc).
//TODO: make protected on a minor release
byte[] writeAndClose(byte[] payload, StreamWrapper wrapper) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(512);
OutputStream compressionStream = wrapper.wrap(outputStream);
try {
compressionStream.write(payload);
compressionStream.flush();
} finally {
Objects.nullSafeClose(compressionStream);
}
return outputStream.toByteArray();
}
/** /**
* Implement this method to do the actual work of compressing the payload * Implement this method to do the actual work of compressing the payload
* *

View File

@ -15,13 +15,11 @@
*/ */
package io.jsonwebtoken.impl.compression; package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.lang.Objects; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.zip.Deflater; import java.io.OutputStream;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream; import java.util.zip.InflaterInputStream;
/** /**
* Codec implementing the <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate compression algorithm</a>. * Codec implementing the <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate compression algorithm</a>.
@ -32,43 +30,25 @@ public class DeflateCompressionCodec extends AbstractCompressionCodec {
private static final String DEFLATE = "DEF"; private static final String DEFLATE = "DEF";
private static final StreamWrapper WRAPPER = new StreamWrapper() {
@Override
public OutputStream wrap(OutputStream out) {
return new DeflaterOutputStream(out);
}
};
@Override @Override
public String getAlgorithmName() { public String getAlgorithmName() {
return DEFLATE; return DEFLATE;
} }
@Override @Override
public byte[] doCompress(byte[] payload) throws IOException { protected byte[] doCompress(byte[] payload) throws IOException {
return writeAndClose(payload, WRAPPER);
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
ByteArrayOutputStream outputStream = null;
DeflaterOutputStream deflaterOutputStream = null;
try {
outputStream = new ByteArrayOutputStream();
deflaterOutputStream = new DeflaterOutputStream(outputStream, deflater, true);
deflaterOutputStream.write(payload, 0, payload.length);
deflaterOutputStream.flush();
return outputStream.toByteArray();
} finally {
Objects.nullSafeClose(outputStream, deflaterOutputStream);
}
} }
@Override @Override
public byte[] doDecompress(byte[] compressed) throws IOException { protected byte[] doDecompress(byte[] compressed) throws IOException {
InflaterOutputStream inflaterOutputStream = null; return readAndClose(new InflaterInputStream(new ByteArrayInputStream(compressed)));
ByteArrayOutputStream decompressedOutputStream = null;
try {
decompressedOutputStream = new ByteArrayOutputStream();
inflaterOutputStream = new InflaterOutputStream(decompressedOutputStream);
inflaterOutputStream.write(compressed);
inflaterOutputStream.flush();
return decompressedOutputStream.toByteArray();
} finally {
Objects.nullSafeClose(decompressedOutputStream, inflaterOutputStream);
}
} }
} }

View File

@ -16,11 +16,10 @@
package io.jsonwebtoken.impl.compression; package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@ -33,43 +32,25 @@ public class GzipCompressionCodec extends AbstractCompressionCodec implements Co
private static final String GZIP = "GZIP"; private static final String GZIP = "GZIP";
private static final StreamWrapper WRAPPER = new StreamWrapper() {
@Override
public OutputStream wrap(OutputStream out) throws IOException {
return new GZIPOutputStream(out);
}
};
@Override @Override
public String getAlgorithmName() { public String getAlgorithmName() {
return GZIP; return GZIP;
} }
@Override @Override
protected byte[] doDecompress(byte[] compressed) throws IOException { protected byte[] doCompress(byte[] payload) throws IOException {
byte[] buffer = new byte[512]; return writeAndClose(payload, WRAPPER);
ByteArrayOutputStream outputStream = null;
GZIPInputStream gzipInputStream = null;
ByteArrayInputStream inputStream = null;
try {
inputStream = new ByteArrayInputStream(compressed);
gzipInputStream = new GZIPInputStream(inputStream);
outputStream = new ByteArrayOutputStream();
int read = gzipInputStream.read(buffer);
while (read != -1) {
outputStream.write(buffer, 0, read);
read = gzipInputStream.read(buffer);
}
return outputStream.toByteArray();
} finally {
Objects.nullSafeClose(inputStream, gzipInputStream, outputStream);
}
} }
protected byte[] doCompress(byte[] payload) throws IOException { @Override
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); protected byte[] doDecompress(byte[] compressed) throws IOException {
GZIPOutputStream compressorOutputStream = new GZIPOutputStream(outputStream, true); return readAndClose(new GZIPInputStream(new ByteArrayInputStream(compressed)));
try {
compressorOutputStream.write(payload, 0, payload.length);
compressorOutputStream.finish();
return outputStream.toByteArray();
} finally {
Objects.nullSafeClose(compressorOutputStream, outputStream);
}
} }
} }