Skip to content

Commit

Permalink
Merge pull request #833 from CRED-CLUB/abhilash/mixpanel_headers
Browse files Browse the repository at this point in the history
feat: add headers support for mixpanel proxy api calls
  • Loading branch information
zihejia committed Apr 9, 2024
2 parents d579541 + 6226d29 commit a3fdf69
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import com.mixpanel.android.util.Base64Coder;
import com.mixpanel.android.util.HttpService;
import com.mixpanel.android.util.ProxyServerInteractor;
import com.mixpanel.android.util.RemoteService;

import org.json.JSONArray;
Expand Down Expand Up @@ -62,7 +63,7 @@ public void setUp() {
private void setUpInstance(boolean trackAutomaticEvents) {
final RemoteService mockPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) {

final String jsonData = Base64Coder.decodeString(params.get("data").toString());
assertTrue(params.containsKey("data"));
Expand Down Expand Up @@ -211,7 +212,7 @@ public void testAutomaticMultipleInstances() throws InterruptedException {

final HttpService mpSecondPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) {
final String jsonData = Base64Coder.decodeString(params.get("data").toString());
assertTrue(params.containsKey("data"));
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import android.os.Bundle;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.mixpanel.android.util.Base64Coder;
import com.mixpanel.android.util.ProxyServerInteractor;
import com.mixpanel.android.util.RemoteService;
import com.mixpanel.android.util.HttpService;

Expand Down Expand Up @@ -61,7 +61,7 @@ public void setUp() {

final RemoteService mockPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory)
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory)
throws ServiceUnavailableException, IOException {
try {
if (mFlushResults.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.mixpanel.android.BuildConfig;
import com.mixpanel.android.util.Base64Coder;
import com.mixpanel.android.util.HttpService;
import com.mixpanel.android.util.ProxyServerInteractor;
import com.mixpanel.android.util.RemoteService;

import org.json.JSONArray;
Expand Down Expand Up @@ -702,7 +703,7 @@ public int addJSON(JSONObject message, String token, MPDbAdapter.Table table) {

final RemoteService mockPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) {
final boolean isIdentified = isIdentifiedRef.get();
assertTrue(params.containsKey("data"));
final String decoded = Base64Coder.decodeString(params.get("data").toString());
Expand Down Expand Up @@ -1397,7 +1398,7 @@ protected AnalyticsMessages getAnalyticsMessages() {
public void testAlias() {
final RemoteService mockPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) {
try {
assertTrue(params.containsKey("data"));
final String jsonData = Base64Coder.decodeString(params.get("data").toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.mixpanel.android.util.Base64Coder;
import com.mixpanel.android.util.HttpService;
import com.mixpanel.android.util.ProxyServerInteractor;
import com.mixpanel.android.util.RemoteService;

import org.json.JSONArray;
Expand Down Expand Up @@ -57,7 +58,7 @@ public void setUp() {

final RemoteService mockPoster = new HttpService() {
@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) {
if (params != null) {
final String jsonData = Base64Coder.decodeString(params.get("data").toString());
assertTrue(params.containsKey("data"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ private void sendData(MPDbAdapter dbAdapter, String token, MPDbAdapter.Table tab
byte[] response;
try {
final SSLSocketFactory socketFactory = mConfig.getSSLSocketFactory();
response = poster.performRequest(url, params, socketFactory);
response = poster.performRequest(url, mConfig.getProxyServerInteractor(), params, socketFactory);
if (null == response) {
deleteEvents = false;
logAboutMessageToMixpanel("Response was null, unexpected failure posting to " + url + ".");
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/mixpanel/android/mpmetrics/MPConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.mixpanel.android.BuildConfig;
import com.mixpanel.android.util.MPConstants;
import com.mixpanel.android.util.MPLog;
import com.mixpanel.android.util.ProxyServerInteractor;
import com.mixpanel.android.util.OfflineMode;

import java.security.GeneralSecurityException;
Expand Down Expand Up @@ -303,6 +304,11 @@ public String getEventsEndpoint() {

public boolean getTrackAutomaticEvents() { return mTrackAutomaticEvents; }

public void setServerURL(String serverURL, ProxyServerInteractor interactor) {
setServerURL(serverURL);
setProxyServerInteractor(interactor);
}

// In parity with iOS SDK
public void setServerURL(String serverURL) {
setEventsEndpointWithBaseURL(serverURL);
Expand Down Expand Up @@ -413,6 +419,14 @@ public synchronized OfflineMode getOfflineMode() {

///////////////////////////////////////////////

public ProxyServerInteractor getProxyServerInteractor() {
return this.serverCallbacks;
}

public void setProxyServerInteractor(ProxyServerInteractor interactor) {
this.serverCallbacks = interactor;
}

// Package access for testing only- do not call directly in library code
/* package */ static MPConfig readConfig(Context appContext, String instanceName) {
final String packageName = appContext.getPackageName();
Expand Down Expand Up @@ -472,5 +486,6 @@ public String toString() {
// Mutable, with synchronized accessor and mutator
private SSLSocketFactory mSSLSocketFactory;
private OfflineMode mOfflineMode;
private ProxyServerInteractor serverCallbacks = null;
private static final String LOGTAG = "MixpanelAPI.Conf";
}
12 changes: 12 additions & 0 deletions src/main/java/com/mixpanel/android/mpmetrics/MixpanelAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.os.Bundle;

import com.mixpanel.android.util.MPLog;
import com.mixpanel.android.util.ProxyServerInteractor;

import org.json.JSONArray;
import org.json.JSONException;
Expand Down Expand Up @@ -580,6 +581,17 @@ public void setServerURL(String serverURL) {
mConfig.setServerURL(serverURL);
}

/**
* Set the base URL used for Mixpanel API requests.
* Useful if you need to proxy Mixpanel requests. Defaults to https://api.mixpanel.com.
* To route data to Mixpanel's EU servers, set to https://api-eu.mixpanel.com
*
* @param serverURL the base URL used for Mixpanel API requests
* @param callback the callback for mixpanel proxy server api headers and status
*/
public void setServerURL(String serverURL, ProxyServerInteractor callback) {
mConfig.setServerURL(serverURL, callback);
}

public Boolean getTrackAutomaticEvents() { return mTrackAutomaticEvents; }
/**
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/com/mixpanel/android/util/HttpService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mixpanel.android.util;

import static com.mixpanel.android.util.MPConstants.URL.MIXPANEL_API;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
Expand Down Expand Up @@ -88,7 +90,7 @@ private boolean onOfflineMode(OfflineMode offlineMode) {
}

@Override
public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory) throws ServiceUnavailableException, IOException {
public byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory) throws ServiceUnavailableException, IOException {
MPLog.v(LOGTAG, "Attempting request to " + endpointUrl);

byte[] response = null;
Expand All @@ -112,6 +114,15 @@ public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSL
((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory);
}

if (interactor != null && isProxyRequest(endpointUrl)) {
Map<String,String> headers = interactor.getProxyRequestHeaders();
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
}

connection.setConnectTimeout(2000);
connection.setReadTimeout(30000);
if (null != params) {
Expand All @@ -133,6 +144,9 @@ public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSL
out.close();
out = null;
}
if (interactor != null && isProxyRequest(endpointUrl)) {
interactor.onProxyResponse(endpointUrl, connection.getResponseCode());
}
in = connection.getInputStream();
response = slurp(in);
in.close();
Expand Down Expand Up @@ -165,6 +179,10 @@ public byte[] performRequest(String endpointUrl, Map<String, Object> params, SSL
return response;
}

private static boolean isProxyRequest(String endpointUrl) {
return !endpointUrl.toLowerCase().contains(MIXPANEL_API.toLowerCase());
}

private static byte[] slurp(final InputStream inputStream)
throws IOException {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mixpanel.android.util;

import java.util.Map;

public interface ProxyServerInteractor {
Map<String, String> getProxyRequestHeaders();

void onProxyResponse(String apiPath, int responseCode);
}
2 changes: 1 addition & 1 deletion src/main/java/com/mixpanel/android/util/RemoteService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public interface RemoteService {

void checkIsMixpanelBlocked();

byte[] performRequest(String endpointUrl, Map<String, Object> params, SSLSocketFactory socketFactory)
byte[] performRequest(String endpointUrl, ProxyServerInteractor interactor, Map<String, Object> params, SSLSocketFactory socketFactory)
throws ServiceUnavailableException, IOException;

class ServiceUnavailableException extends Exception {
Expand Down

0 comments on commit a3fdf69

Please sign in to comment.