diff --git a/browsermob-core/pom.xml b/browsermob-core/pom.xml
index 2071434ee..8e5cdab71 100644
--- a/browsermob-core/pom.xml
+++ b/browsermob-core/pom.xml
@@ -139,6 +139,13 @@
${project.version}
+
+
+ org.brotli
+ dec
+ 0.1.2
+
+
org.javassist
diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java
index d69ad8f76..1bce61ad7 100644
--- a/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java
+++ b/browsermob-core/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java
@@ -25,6 +25,7 @@
*/
public class ServerResponseCaptureFilter extends HttpFiltersAdapter {
private static final Logger log = LoggerFactory.getLogger(ServerResponseCaptureFilter.class);
+ private static final String BROTLI_COMPRESSION = "br";
/**
* Populated by serverToProxyResponse() when processing the HttpResponse object
@@ -133,6 +134,13 @@ protected void decompressContents() {
} catch (RuntimeException e) {
log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.getUri(), e);
}
+ } else if(contentEncoding.equals(BROTLI_COMPRESSION)) {
+ try {
+ fullResponseContents = BrowserMobHttpUtil.decompressBrotliContents(getRawResponseContents());
+ decompressionSuccessful = true;
+ } catch (RuntimeException e) {
+ log.warn("Failed to decompress response with encoding type " + contentEncoding + " when decoding request from " + originalRequest.getUri(), e);
+ }
} else {
log.warn("Cannot decode unsupported content encoding type {}", contentEncoding);
}
diff --git a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java
index 98172810f..264b3a2ee 100644
--- a/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java
+++ b/browsermob-core/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java
@@ -3,6 +3,7 @@
import com.google.common.io.BaseEncoding;
import com.google.common.net.HostAndPort;
import com.google.common.net.MediaType;
+import org.brotli.dec.BrotliInputStream;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
@@ -14,6 +15,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -78,7 +80,7 @@ public static long getHeaderSize(HttpHeaders headers) {
/**
* Decompresses the gzipped byte stream.
*
- * @param fullMessage gzipped byte stream to decomress
+ * @param fullMessage gzipped byte stream to decompress
* @return decompressed bytes
* @throws DecompressionException thrown if the fullMessage cannot be read or decompressed for any reason
*/
@@ -111,6 +113,42 @@ public static byte[] decompressContents(byte[] fullMessage) throws Decompression
return fullMessage;
}
+ /**
+ * Decompresses the brotli byze stream
+ *
+ * @param fullMessage brotli byte stream to decompress
+ * @return decompressed bytes
+ * @throws DecompressionException thrown if the fullMessage cannot be read or decompressed for any reason
+ */
+ public static byte[] decompressBrotliContents(byte[] fullMessage) throws DecompressionException {
+ InputStream brotliReader = null;
+ ByteArrayOutputStream uncompressed;
+ try {
+ brotliReader = new BrotliInputStream(new ByteArrayInputStream(fullMessage));
+
+ uncompressed = new ByteArrayOutputStream(fullMessage.length);
+
+ byte[] decompressBuffer = new byte[DECOMPRESS_BUFFER_SIZE];
+ int bytesRead;
+ while ((bytesRead = brotliReader.read(decompressBuffer)) > -1) {
+ uncompressed.write(decompressBuffer, 0, bytesRead);
+ }
+
+ fullMessage = uncompressed.toByteArray();
+ } catch (IOException e) {
+ throw new DecompressionException("Unable to decompress response", e);
+ } finally {
+ try {
+ if (brotliReader != null) {
+ brotliReader.close();
+ }
+ } catch (IOException e) {
+ log.warn("Unable to close brotli stream", e);
+ }
+ }
+ return fullMessage;
+ }
+
/**
* Returns true if the content type string indicates textual content. Currently these are any Content-Types that start with one of the
* following: