Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enable GZIP compression for request payloads #852

Merged
merged 1 commit into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,32 @@ public void testSetMaximumDatabaseLimit2() {
assertEquals(100000000, mixpanelAPI.getMaximumDatabaseLimit());
}

@Test
public void testShouldGzipRequestPayload() {
final Bundle metaData = new Bundle();
metaData.putBoolean("com.mixpanel.android.MPConfig.GzipRequestPayload", true);
MPConfig mpConfig = mpConfig(metaData);
assertTrue(mpConfig.shouldGzipRequestPayload());

mpConfig.setShouldGzipRequestPayload(false);
assertFalse(mpConfig.shouldGzipRequestPayload());

mpConfig.setShouldGzipRequestPayload(true);
assertTrue(mpConfig.shouldGzipRequestPayload());

// assert false by default
MPConfig mpConfig2 = mpConfig(new Bundle());
assertFalse(mpConfig2.shouldGzipRequestPayload());

MixpanelAPI mixpanelAPI = mixpanelApi(mpConfig);

assertTrue(mixpanelAPI.shouldGzipRequestPayload());

mixpanelAPI.setShouldGzipRequestPayload(false);
assertFalse(mixpanelAPI.shouldGzipRequestPayload());

}

private MPConfig mpConfig(final Bundle metaData) {
return new MPConfig(metaData, InstrumentationRegistry.getInstrumentation().getContext(), null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected MPDbAdapter makeDbAdapter(Context context) {
}

protected RemoteService getPoster() {
return new HttpService();
return new HttpService(mConfig.shouldGzipRequestPayload());
}

////////////////////////////////////////////////////
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/mixpanel/android/mpmetrics/MPConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ public synchronized void setOfflineMode(OfflineMode offlineMode) {
mBulkUploadLimit = metaData.getInt("com.mixpanel.android.MPConfig.BulkUploadLimit", 40); // 40 records default
mFlushInterval = metaData.getInt("com.mixpanel.android.MPConfig.FlushInterval", 60 * 1000); // one minute default
mFlushBatchSize = metaData.getInt("com.mixpanel.android.MPConfig.FlushBatchSize", 50); // flush 50 events at a time by default
shouldGzipRequestPayload = metaData.getBoolean("com.mixpanel.android.MPConfig.GzipRequestPayload", false);
mFlushOnBackground = metaData.getBoolean("com.mixpanel.android.MPConfig.FlushOnBackground", true);
mMinimumDatabaseLimit = metaData.getInt("com.mixpanel.android.MPConfig.MinimumDatabaseLimit", 20 * 1024 * 1024); // 20 Mb
mMaximumDatabaseLimit = metaData.getInt("com.mixpanel.android.MPConfig.MaximumDatabaseLimit", Integer.MAX_VALUE); // 2 Gb
Expand Down Expand Up @@ -277,6 +278,10 @@ public int getFlushBatchSize() {
public void setFlushBatchSize(int flushBatchSize) {
mFlushBatchSize = flushBatchSize;
}
public boolean shouldGzipRequestPayload() { return shouldGzipRequestPayload; }

public void setShouldGzipRequestPayload(Boolean shouldGzip) { shouldGzipRequestPayload = shouldGzip; }


// Throw away records that are older than this in milliseconds. Should be below the server side age limit for events.
public long getDataExpiration() {
Expand Down Expand Up @@ -476,6 +481,7 @@ public String toString() {
private String mPeopleEndpoint;
private String mGroupsEndpoint;
private int mFlushBatchSize;
private boolean shouldGzipRequestPayload;

private final String mResourcePackageName;
private final int mMinSessionDuration;
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/mixpanel/android/mpmetrics/MixpanelAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,24 @@ public int getFlushBatchSize() {
return mConfig.getFlushBatchSize();
}

/**
* Set whether the request payload should be GZIP-compressed before being sent.
*
* @param shouldGzipRequestPayload boolean, true to enable GZIP compression, false otherwise.
*/
public void setShouldGzipRequestPayload(boolean shouldGzipRequestPayload) {
mConfig.setShouldGzipRequestPayload(shouldGzipRequestPayload);
}

/**
* Get whether the request payload is currently set to be GZIP-compressed.
*
* @return boolean, whether GZIP compression is enabled
*/
public boolean shouldGzipRequestPayload() {
return mConfig.shouldGzipRequestPayload();
}

/**
* Set an integer number of bytes, the maximum size limit to the Mixpanel database.
*
Expand Down
33 changes: 29 additions & 4 deletions src/main/java/com/mixpanel/android/util/HttpService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.net.InetAddress;
import java.net.URL;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
Expand All @@ -27,10 +28,20 @@
*/
public class HttpService implements RemoteService {


private final boolean shouldGzipRequestPayload;

private static boolean sIsMixpanelBlocked;
private static final int MIN_UNAVAILABLE_HTTP_RESPONSE_CODE = HttpURLConnection.HTTP_INTERNAL_ERROR;
private static final int MAX_UNAVAILABLE_HTTP_RESPONSE_CODE = 599;

public HttpService(boolean shouldGzipRequestPayload) {
this.shouldGzipRequestPayload = shouldGzipRequestPayload;
}

public HttpService() {
this(false);
}
@Override
public void checkIsMixpanelBlocked() {
Thread t = new Thread(new Runnable() {
Expand Down Expand Up @@ -104,7 +115,7 @@ public byte[] performRequest(String endpointUrl, ProxyServerInteractor interacto
while (retries < 3 && !succeeded) {
InputStream in = null;
OutputStream out = null;
BufferedOutputStream bout = null;
OutputStream bout = null;
HttpURLConnection connection = null;

try {
Expand All @@ -131,12 +142,15 @@ public byte[] performRequest(String endpointUrl, ProxyServerInteractor interacto
builder.appendQueryParameter(param.getKey(), param.getValue().toString());
}
String query = builder.build().getEncodedQuery();

connection.setFixedLengthStreamingMode(query.getBytes().length);
if (shouldGzipRequestPayload) {
connection.setRequestProperty(CONTENT_ENCODING_HEADER, GZIP_CONTENT_TYPE_HEADER);
} else {
connection.setFixedLengthStreamingMode(query.getBytes().length);
}
connection.setDoOutput(true);
connection.setRequestMethod("POST");
out = connection.getOutputStream();
bout = new BufferedOutputStream(out);
bout = getBufferedOutputStream(out);
bout.write(query.getBytes("UTF-8"));
bout.flush();
bout.close();
Expand Down Expand Up @@ -179,6 +193,14 @@ public byte[] performRequest(String endpointUrl, ProxyServerInteractor interacto
return response;
}

private OutputStream getBufferedOutputStream(OutputStream out) throws IOException {
if(shouldGzipRequestPayload) {
return new GZIPOutputStream(new BufferedOutputStream(out), HTTP_OUTPUT_STREAM_BUFFER_SIZE);
} else {
return new BufferedOutputStream(out);
}
}

private static boolean isProxyRequest(String endpointUrl) {
return !endpointUrl.toLowerCase().contains(MIXPANEL_API.toLowerCase());
}
Expand All @@ -199,4 +221,7 @@ private static byte[] slurp(final InputStream inputStream)
}

private static final String LOGTAG = "MixpanelAPI.Message";
private static final int HTTP_OUTPUT_STREAM_BUFFER_SIZE = 8192;
private static final String CONTENT_ENCODING_HEADER = "Content-Encoding";
private static final String GZIP_CONTENT_TYPE_HEADER = "gzip";
}
Loading