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

[CHAT-5063] feat: sdk proxy wrapper for agent tracking #1060

Closed
wants to merge 1 commit into from
Closed
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
16 changes: 16 additions & 0 deletions android/src/main/java/io/ably/lib/rest/AblyRest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ public AblyRest(ClientOptions options) throws AblyException {
super(options, new AndroidPlatformAgentProvider());
}

/**
* Constructor implementation to be able to have proxy based on this class
*/
protected AblyRest(AblyRest underlyingClient, DerivedClientOptions derivedOptions) {
super(underlyingClient, derivedOptions);
}

/**
* Retrieves a {@link LocalDevice} object that represents the current state of the device as a target for push notifications.
* <p>
Expand All @@ -56,6 +63,15 @@ public void setAndroidContext(Context context) throws AblyException {
this.push.tryRequestRegistrationToken();
}

/**
* [Internal Method]
* <p/>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public AblyRest createDerivedClient(DerivedClientOptions derivedOptions) {
return new AblyRest(this, derivedOptions);
}

/**
* clientId set by late initialisation
*/
Expand Down
16 changes: 16 additions & 0 deletions java/src/main/java/io/ably/lib/rest/AblyRest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,20 @@ public AblyRest(String key) throws AblyException {
public AblyRest(ClientOptions options) throws AblyException {
super(options, new JavaPlatformAgentProvider());
}

/**
* Constructor implementation to be able to have proxy based on this class
*/
protected AblyRest(AblyRest underlyingClient, DerivedClientOptions derivedOptions) {
super(underlyingClient, derivedOptions);
}

/**
* [Internal Method]
* <p/>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public AblyRest createDerivedClient(DerivedClientOptions derivedOptions) {
return new AblyRest(this, derivedOptions);
}
}
13 changes: 13 additions & 0 deletions lib/src/main/java/io/ably/lib/http/AsyncHttpScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,23 @@ public AsyncHttpScheduler(HttpCore httpCore, ClientOptions options) {
super(httpCore, new CloseableThreadPoolExecutor(options));
}

private AsyncHttpScheduler(HttpCore httpCore, CloseableExecutor executor) {
super(httpCore, executor);
}

private static final long KEEP_ALIVE_TIME = 2000L;

protected static final String TAG = AsyncHttpScheduler.class.getName();

/**
* [Internal Method]
* <p>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public AsyncHttpScheduler exchangeHttpCore(HttpCore httpCore) {
return new AsyncHttpScheduler(httpCore, this.executor);
}

private static class CloseableThreadPoolExecutor implements CloseableExecutor {
private final ThreadPoolExecutor executor;

Expand Down
9 changes: 9 additions & 0 deletions lib/src/main/java/io/ably/lib/http/Http.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ public void close() throws Exception {
asyncHttp.close();
}

/**
* [Internal Method]
* <p>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public Http exchangeHttpCore(HttpCore httpCore) {
return new Http(asyncHttp.exchangeHttpCore(httpCore), new SyncHttpScheduler(httpCore));
}

public class Request<Result> {
private final Execute<Result> execute;

Expand Down
26 changes: 25 additions & 1 deletion lib/src/main/java/io/ably/lib/http/HttpCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.ably.lib.network.HttpRequest;
import io.ably.lib.network.HttpResponse;
import io.ably.lib.rest.Auth;
import io.ably.lib.rest.DerivedClientOptions;
import io.ably.lib.transport.Defaults;
import io.ably.lib.transport.Hosts;
import io.ably.lib.types.AblyException;
Expand Down Expand Up @@ -68,6 +69,8 @@ public class HttpCore {
private final HttpEngine engine;
private HttpAuth proxyAuth;

private DerivedClientOptions derivedOptions;

/*************************
* Public API
*************************/
Expand Down Expand Up @@ -103,6 +106,18 @@ public HttpCore(ClientOptions options, Auth auth, PlatformAgentProvider platform
this.engine = engineFactory.create(new HttpEngineConfig(ClientOptionsUtils.convertToProxyConfig(options)));
}

private HttpCore(HttpCore underlyingHttpCore, DerivedClientOptions derivedOptions) {
this.options = underlyingHttpCore.options;
this.auth = underlyingHttpCore.auth;
this.platformAgentProvider = underlyingHttpCore.platformAgentProvider;
this.scheme = underlyingHttpCore.scheme;
this.port = underlyingHttpCore.port;
this.hosts = underlyingHttpCore.hosts;
this.proxyAuth = underlyingHttpCore.proxyAuth;
this.engine = underlyingHttpCore.engine;
this.derivedOptions = derivedOptions;
}

/**
* Make a synchronous HTTP request specified by URL and proxy, retrying if necessary on WWW-Authenticate
*
Expand Down Expand Up @@ -307,7 +322,7 @@ private Map<String, String> collectRequestHeaders(URL url, String method, Param[

/* pass required headers */
requestHeaders.put(Defaults.ABLY_PROTOCOL_VERSION_HEADER, Defaults.ABLY_PROTOCOL_VERSION); // RSC7a
requestHeaders.put(Defaults.ABLY_AGENT_HEADER, AgentHeaderCreator.create(options.agents, platformAgentProvider));
requestHeaders.put(Defaults.ABLY_AGENT_HEADER, AgentHeaderCreator.create(options.agents, platformAgentProvider, derivedOptions));
if (options.clientId != null)
requestHeaders.put(Defaults.ABLY_CLIENT_ID_HEADER, Base64Coder.encodeString(options.clientId));

Expand Down Expand Up @@ -455,6 +470,15 @@ private Response executeRequest(HttpRequest request) {
return response;
}

/**
* [Internal Method]
* <p>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public HttpCore applyDerivedOptions(DerivedClientOptions derivedOptions) {
return new HttpCore(this, derivedOptions);
}

/**
* Interface for an entity that supplies an httpCore request body
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/src/main/java/io/ably/lib/http/HttpScheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ public <T> Future<T> ablyHttpExecuteWithRetry(
return request;
}

private final CloseableExecutor executor;
protected final CloseableExecutor executor;
private final HttpCore httpCore;

protected static final String TAG = HttpScheduler.class.getName();
Expand Down
19 changes: 19 additions & 0 deletions lib/src/main/java/io/ably/lib/realtime/AblyRealtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.ably.lib.rest.AblyRest;
import io.ably.lib.rest.Auth;
import io.ably.lib.rest.DerivedClientOptions;
import io.ably.lib.transport.ConnectionManager;
import io.ably.lib.types.AblyException;
import io.ably.lib.types.ChannelOptions;
Expand Down Expand Up @@ -83,6 +84,15 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan
if(options.autoConnect) connection.connect();
}

/**
* Package-private constructor implementation to be able to have proxy based on this class
*/
AblyRealtime(AblyRealtime underlyingClient, DerivedClientOptions derivedOptions) {
super(underlyingClient, derivedOptions);
this.channels = underlyingClient.channels;
this.connection = underlyingClient.connection;
}

/**
* Calls {@link Connection#connect} and causes the connection to open,
* entering the connecting state. Explicitly calling connect() is unnecessary
Expand Down Expand Up @@ -118,6 +128,15 @@ public void close() {
connection.close();
}

/**
* [Internal Method]
* <p/>
* We use this method to implement proxy Realtime / Rest clients that add additional data to the underlying client.
*/
public AblyRealtime createDerivedClient(DerivedClientOptions derivedOptions) {
return new AblyRealtime(this, derivedOptions);
}

/**
* Authentication token has changed.
*/
Expand Down
14 changes: 14 additions & 0 deletions lib/src/main/java/io/ably/lib/rest/AblyBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ public AblyBase(ClientOptions options, PlatformAgentProvider platformAgentProvid
push = new Push(this);
}

/**
* We use empty constructor to be able to create proxy implementation of Realtime and Rest client
*/
protected AblyBase(AblyBase underlyingClient, DerivedClientOptions derivedOptions) {
this.options = underlyingClient.options;
this.auth = underlyingClient.auth;
this.httpCore = underlyingClient.httpCore.applyDerivedOptions(derivedOptions);
this.http = underlyingClient.http.exchangeHttpCore(this.httpCore);
this.platform = underlyingClient.platform;
this.push = underlyingClient.push;
this.channels = underlyingClient.channels;
this.platformAgentProvider = underlyingClient.platformAgentProvider;
}

/**
* Causes the connection to close, entering the [{@link io.ably.lib.realtime.ConnectionState#closing} state.
* Once closed, the library does not attempt to re-establish the connection without an explicit call to
Expand Down
33 changes: 33 additions & 0 deletions lib/src/main/java/io/ably/lib/rest/DerivedClientOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.ably.lib.rest;

import java.util.HashMap;
import java.util.Map;

public final class DerivedClientOptions {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of notes about naming — people didn't like the "derived client" naming on the DR, so in the ably-cocoa PR (ably/ably-cocoa#2014) I settled on the following more explicit naming:

  • the options class is called WrapperSDKProxyOptions
  • the method is called createWrapperSDKProxy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! Do you think it would be less obvious if we removed ‘SDK’ from the name, e.g., WrapperProxyOptions and createWrapperProxy?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I feel that it's clearer with "SDK"…

private final Map<String, String> agents;

DerivedClientOptions(Map<String, String> agents) {
this.agents = agents;
}

public static DerivedClientOptionsBuilder builder() {
return new DerivedClientOptionsBuilder();
}

public Map<String, String> getAgents() {
return this.agents;
}

public static class DerivedClientOptionsBuilder {
private final Map<String, String> agents = new HashMap<>();

public DerivedClientOptionsBuilder addAgent(String agent, String version) {
this.agents.put(agent, version);
return this;
}

public DerivedClientOptions build() {
return new DerivedClientOptions(this.agents);
}
}
}
17 changes: 16 additions & 1 deletion lib/src/main/java/io/ably/lib/util/AgentHeaderCreator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ably.lib.util;

import io.ably.lib.rest.DerivedClientOptions;
import io.ably.lib.transport.Defaults;

import java.util.Map;
Expand All @@ -15,7 +16,7 @@ public class AgentHeaderCreator {
*/
public static final String AGENT_DIVIDER = "/";

public static String create(Map<String, String> additionalAgents, PlatformAgentProvider platformAgentProvider) {
public static String create(Map<String, String> additionalAgents, PlatformAgentProvider platformAgentProvider, DerivedClientOptions derivedOptions) {
StringBuilder agentStringBuilder = new StringBuilder();
agentStringBuilder.append(Defaults.ABLY_AGENT_VERSION);
if (additionalAgents != null && !additionalAgents.isEmpty()) {
Expand All @@ -27,9 +28,23 @@ public static String create(Map<String, String> additionalAgents, PlatformAgentP
agentStringBuilder.append(AGENT_ENTRY_SEPARATOR);
agentStringBuilder.append(platformAgent);
}

if (derivedOptions != null) {
derivedOptions.getAgents().entrySet().forEach(entry -> {
agentStringBuilder.append(AGENT_ENTRY_SEPARATOR);
agentStringBuilder.append(entry.getKey());
agentStringBuilder.append(AGENT_DIVIDER);
agentStringBuilder.append(entry.getValue());
});
}

return agentStringBuilder.toString();
}

public static String create(Map<String, String> additionalAgents, PlatformAgentProvider platformAgentProvider) {
return create(additionalAgents, platformAgentProvider, null);
}

private static String getAdditionalAgentEntries(Map<String, String> additionalAgents) {
StringBuilder additionalAgentsBuilder = new StringBuilder();
for (String additionalAgentName : additionalAgents.keySet()) {
Expand Down
Loading
Loading