Skip to content
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
68 changes: 54 additions & 14 deletions src/main/java/com/mixpanel/mixpanelapi/MixpanelAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ public class MixpanelAPI implements AutoCloseable {
private static final Logger logger = Logger.getLogger(MixpanelAPI.class.getName());
private static final int BUFFER_SIZE = 256; // Small, we expect small responses.

private static final int CONNECT_TIMEOUT_MILLIS = 2000;
private static final int READ_TIMEOUT_MILLIS = 10000;
private static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 2000;
private static final int DEFAULT_READ_TIMEOUT_MILLIS = 10000;

protected final String mEventsEndpoint;
protected final String mPeopleEndpoint;
protected final String mGroupsEndpoint;
protected final String mImportEndpoint;
protected final boolean mUseGzipCompression;
protected final Integer mConnectTimeout;
protected final Integer mReadTimeout;
protected final LocalFlagsProvider mLocalFlags;
protected final RemoteFlagsProvider mRemoteFlags;
protected final JsonSerializer mJsonSerializer;
Expand All @@ -69,7 +71,7 @@ public MixpanelAPI() {
* @param useGzipCompression whether to use gzip compression for network requests
*/
public MixpanelAPI(boolean useGzipCompression) {
this(null, null, null, null, useGzipCompression, null, null, null);
this(null, null, null, null, useGzipCompression, null, null, null, null, null);
}

/**
Expand Down Expand Up @@ -98,7 +100,7 @@ public MixpanelAPI(RemoteFlagsConfig remoteFlagsConfig) {
* @param remoteFlagsConfig configuration for remote feature flags evaluation (can be null)
*/
private MixpanelAPI(LocalFlagsConfig localFlagsConfig, RemoteFlagsConfig remoteFlagsConfig) {
this(null, null, null, null, false, localFlagsConfig, remoteFlagsConfig, null);
this(null, null, null, null, false, localFlagsConfig, remoteFlagsConfig, null, null, null);
}

/**
Expand All @@ -111,7 +113,7 @@ private MixpanelAPI(LocalFlagsConfig localFlagsConfig, RemoteFlagsConfig remoteF
* @see #MixpanelAPI()
*/
public MixpanelAPI(String eventsEndpoint, String peopleEndpoint) {
this(eventsEndpoint, peopleEndpoint, null, null, false, null, null, null);
this(eventsEndpoint, peopleEndpoint, null, null, false, null, null, null, null, null);
}

/**
Expand All @@ -125,7 +127,7 @@ public MixpanelAPI(String eventsEndpoint, String peopleEndpoint) {
* @see #MixpanelAPI()
*/
public MixpanelAPI(String eventsEndpoint, String peopleEndpoint, String groupsEndpoint) {
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, null, false, null, null, null);
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, null, false, null, null, null, null, null);
}

/**
Expand All @@ -140,7 +142,7 @@ public MixpanelAPI(String eventsEndpoint, String peopleEndpoint, String groupsEn
* @see #MixpanelAPI()
*/
public MixpanelAPI(String eventsEndpoint, String peopleEndpoint, String groupsEndpoint, String importEndpoint) {
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, importEndpoint, false, null, null, null);
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, importEndpoint, false, null, null, null, null, null);
}

/**
Expand All @@ -156,7 +158,7 @@ public MixpanelAPI(String eventsEndpoint, String peopleEndpoint, String groupsEn
* @see #MixpanelAPI()
*/
public MixpanelAPI(String eventsEndpoint, String peopleEndpoint, String groupsEndpoint, String importEndpoint, boolean useGzipCompression) {
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, importEndpoint, useGzipCompression, null, null, null);
this(eventsEndpoint, peopleEndpoint, groupsEndpoint, importEndpoint, useGzipCompression, null, null, null, null, null);
}

/**
Expand All @@ -173,7 +175,9 @@ private MixpanelAPI(Builder builder) {
builder.useGzipCompression,
builder.flagsConfig instanceof LocalFlagsConfig ? (LocalFlagsConfig) builder.flagsConfig : null,
builder.flagsConfig instanceof RemoteFlagsConfig ? (RemoteFlagsConfig) builder.flagsConfig : null,
builder.jsonSerializer
builder.jsonSerializer,
builder.connectTimeout,
builder.readTimeout
);
}

Expand All @@ -197,13 +201,17 @@ private MixpanelAPI(
boolean useGzipCompression,
LocalFlagsConfig localFlagsConfig,
RemoteFlagsConfig remoteFlagsConfig,
JsonSerializer jsonSerializer
JsonSerializer jsonSerializer,
Integer connectTimeout,
Integer readTimeout
) {
mEventsEndpoint = eventsEndpoint != null ? eventsEndpoint : Config.BASE_ENDPOINT + "/track";
mPeopleEndpoint = peopleEndpoint != null ? peopleEndpoint : Config.BASE_ENDPOINT + "/engage";
mGroupsEndpoint = groupsEndpoint != null ? groupsEndpoint : Config.BASE_ENDPOINT + "/groups";
mImportEndpoint = importEndpoint != null ? importEndpoint : Config.BASE_ENDPOINT + "/import";
mUseGzipCompression = useGzipCompression;
mConnectTimeout = connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT_MILLIS;
mReadTimeout = readTimeout != null ? readTimeout : DEFAULT_READ_TIMEOUT_MILLIS;
mDefaultJsonSerializer = new OrgJsonSerializer();
if (jsonSerializer != null) {
logger.log(Level.INFO, "Custom JsonSerializer provided: " + jsonSerializer.getClass().getName());
Expand Down Expand Up @@ -313,8 +321,8 @@ protected String encodeDataString(String dataString) {
/* package */ boolean sendData(String dataString, String endpointUrl) throws IOException {
URL endpoint = new URL(endpointUrl);
URLConnection conn = endpoint.openConnection();
conn.setReadTimeout(READ_TIMEOUT_MILLIS);
conn.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
conn.setReadTimeout(mReadTimeout);
conn.setConnectTimeout(mConnectTimeout);
conn.setDoOutput(true);

byte[] dataToSend;
Expand Down Expand Up @@ -463,8 +471,8 @@ private String dataString(List<JSONObject> messages) {
/* package */ boolean sendImportData(String dataString, String endpointUrl, String token) throws IOException {
URL endpoint = new URL(endpointUrl);
HttpURLConnection conn = (HttpURLConnection) endpoint.openConnection();
conn.setReadTimeout(READ_TIMEOUT_MILLIS);
conn.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
conn.setReadTimeout(mReadTimeout);
conn.setConnectTimeout(mConnectTimeout);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
Expand Down Expand Up @@ -649,6 +657,8 @@ public static class Builder {
private boolean useGzipCompression;
private BaseFlagsConfig flagsConfig;
private JsonSerializer jsonSerializer;
private Integer connectTimeout;
private Integer readTimeout;

/**
* Sets the endpoint URL for Mixpanel events messages.
Expand Down Expand Up @@ -728,6 +738,36 @@ public Builder jsonSerializer(JsonSerializer jsonSerializer) {
return this;
}

/**
* Sets the connect timeout for Mixpanel network requests
*
* @param connectTimeoutInMillis connection timeout in milliseconds.
* Value must be 0 or larger.
* 0 indicates indefinite (no) timeout.
* @return this Builder instance for method chaining
*/
public Builder connectTimeout(int connectTimeoutInMillis) {
if (connectTimeoutInMillis >= 0) {
this.connectTimeout = connectTimeoutInMillis;
}
return this;
}

/**
* Sets the read timeout for Mixpanel network requests
*
* @param readTimeoutInMillis read timeout in milliseconds.
* Value must be 0 or larger.
* 0 indicates indefinite (no) timeout.
* @return this Builder instance for method chaining
*/
public Builder readTimeout(int readTimeoutInMillis) {
if (readTimeoutInMillis >= 0) {
this.readTimeout = readTimeoutInMillis;
}
return this;
}

/**
* Builds and returns a new MixpanelAPI instance with the configured settings.
*
Expand Down
39 changes: 39 additions & 0 deletions src/test/java/com/mixpanel/mixpanelapi/MixpanelAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,8 @@ public void testBuilderWithDefaults() {
assertFalse(api.mUseGzipCompression);
assertNull(api.mLocalFlags);
assertNull(api.mRemoteFlags);
assertEquals(Integer.valueOf(10000), api.mReadTimeout);
assertEquals(Integer.valueOf(2000), api.mConnectTimeout);
api.close();
}

Expand All @@ -1150,6 +1152,8 @@ public void testBuilderWithAllOptions() {
String expectedPeopleEndpoint = "https://custom.example.com/people";
String expectedGroupsEndpoint = "https://custom.example.com/groups";
String expectedImportEndpoint = "https://custom.example.com/import";
Integer expectedReadTimeout = 5000;
Integer expectedConnectTimeout = 7000;
boolean expectedGzipCompression = true;
LocalFlagsConfig expectedLocalFlagsConfig =
new LocalFlagsConfig.Builder().build();
Expand All @@ -1164,6 +1168,8 @@ public void testBuilderWithAllOptions() {
.useGzipCompression(expectedGzipCompression)
.flagsConfig(expectedLocalFlagsConfig)
.jsonSerializer(expectedJsonSerializer)
.connectTimeout(expectedConnectTimeout)
.readTimeout(expectedReadTimeout)
.build();

// THEN
Expand All @@ -1175,6 +1181,8 @@ public void testBuilderWithAllOptions() {
assertEquals(expectedJsonSerializer, api.mJsonSerializer);
assertNotNull(api.mLocalFlags);
assertNull(api.mRemoteFlags);
assertEquals(expectedReadTimeout, api.mReadTimeout);
assertEquals(expectedConnectTimeout, api.mConnectTimeout);
api.close();
}

Expand Down Expand Up @@ -1400,4 +1408,35 @@ public void testConstructorWithFourEndpointsAndGzip() {
assertNull(api.mRemoteFlags);
api.close();
}

public void testZeroValueTimeouts() {
// GIVEN
Integer expectedTimeout = 0;

// WHEN
MixpanelAPI api = new MixpanelAPI.Builder()
.connectTimeout(0)
.readTimeout(0)
.build();

// THEN
assertEquals(expectedTimeout, api.mConnectTimeout);
assertEquals(expectedTimeout, api.mReadTimeout);
api.close();
}

public void testNegativeValueTimeoutUsesDefaults() {
// GIVEN

// WHEN
MixpanelAPI api = new MixpanelAPI.Builder()
.connectTimeout(-1000)
.readTimeout(-2000)
.build();

// THEN
assertEquals(Integer.valueOf(2000), api.mConnectTimeout);
assertEquals(Integer.valueOf(10000), api.mReadTimeout);
api.close();
}
}