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
### 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
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
JDK 1.7 as this library still supports that Java version even if Travis no longer does.
library.
### 0.10.5

View File

@ -18,8 +18,12 @@ package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Abstract class that asserts arguments and wraps IOException with CompressionException.
@ -28,6 +32,44 @@ import java.io.IOException;
*/
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
*

View File

@ -15,13 +15,11 @@
*/
package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.io.OutputStream;
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>.
@ -32,43 +30,25 @@ public class DeflateCompressionCodec extends AbstractCompressionCodec {
private static final String DEFLATE = "DEF";
private static final StreamWrapper WRAPPER = new StreamWrapper() {
@Override
public OutputStream wrap(OutputStream out) {
return new DeflaterOutputStream(out);
}
};
@Override
public String getAlgorithmName() {
return DEFLATE;
}
@Override
public byte[] doCompress(byte[] payload) throws IOException {
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);
}
protected byte[] doCompress(byte[] payload) throws IOException {
return writeAndClose(payload, WRAPPER);
}
@Override
public byte[] doDecompress(byte[] compressed) throws IOException {
InflaterOutputStream inflaterOutputStream = null;
ByteArrayOutputStream decompressedOutputStream = null;
try {
decompressedOutputStream = new ByteArrayOutputStream();
inflaterOutputStream = new InflaterOutputStream(decompressedOutputStream);
inflaterOutputStream.write(compressed);
inflaterOutputStream.flush();
return decompressedOutputStream.toByteArray();
} finally {
Objects.nullSafeClose(decompressedOutputStream, inflaterOutputStream);
}
protected byte[] doDecompress(byte[] compressed) throws IOException {
return readAndClose(new InflaterInputStream(new ByteArrayInputStream(compressed)));
}
}

View File

@ -16,11 +16,10 @@
package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
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 StreamWrapper WRAPPER = new StreamWrapper() {
@Override
public OutputStream wrap(OutputStream out) throws IOException {
return new GZIPOutputStream(out);
}
};
@Override
public String getAlgorithmName() {
return GZIP;
}
@Override
protected byte[] doDecompress(byte[] compressed) throws IOException {
byte[] buffer = new byte[512];
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 {
return writeAndClose(payload, WRAPPER);
}
protected byte[] doCompress(byte[] payload) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream compressorOutputStream = new GZIPOutputStream(outputStream, true);
try {
compressorOutputStream.write(payload, 0, payload.length);
compressorOutputStream.finish();
return outputStream.toByteArray();
} finally {
Objects.nullSafeClose(compressorOutputStream, outputStream);
}
@Override
protected byte[] doDecompress(byte[] compressed) throws IOException {
return readAndClose(new GZIPInputStream(new ByteArrayInputStream(compressed)));
}
}