Skip to content

Commit

Permalink
docs: add http engine in-code docs
Browse files Browse the repository at this point in the history
Also add algorithm how to add new engine in the `CONTRIBUTING.md` guide
  • Loading branch information
ttypic committed Oct 7, 2024
1 parent 6f38c17 commit 576374b
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 11 deletions.
28 changes: 28 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,34 @@ The Android-specific library AAR is built with:

(The `ANDROID_HOME` environment variable must be set appropriately.)

## Adding a New Network Engine Implementation

Currently, `ably-java` supports two different engines for network operations (HTTP calls and WebSocket connections):

- **Default Engine**: Utilizes the built-in `HttpUrlConnection` for HTTP calls and the TooTallNate/Java-WebSocket library for WebSocket connections.
- **OkHttp Engine**: Utilizes the OkHttp library for both HTTP and WebSocket connections.

These engines are designed to be swappable. By default, the library comes with the default engine, but you can easily replace it with the OkHttp engine:

```kotlin
implementation("io.ably:ably-java:$ABLY_VERSION") {
exclude(group = "io.ably", module = "network-client-default")
}
runtimeOnly("io.ably:network-client-okhttp:$ABLY_VERSION")
```

### How to Add a New Network Engine

To add a new network engine, follow these steps:

1. **Implement the interfaces**:
- Implement the `HttpEngineFactory` and `WebSocketEngineFactory` interfaces for your custom engine.

2. **Register the engine**:
- Modify the `getFirstAvailable()` methods in these interfaces to include your new implementation.

Once done, your custom network engine will be available for use within `ably-java`.

### Code Standard

#### Checkstyle
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
package io.ably.lib.network;

/**
* Cancelable Http request call
* <p/>
* Implementation should be thread-safe
*/
public interface HttpCall {
/**
* Synchronously execute Http request and return response from te server
*/
HttpResponse execute();

/**
* Cancel pending Http request
*/
void cancel();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
package io.ably.lib.network;

/**
* An HTTP engine instance that can make cancelable HTTP requests.
* It contains some engine-wide configurations, such as proxy settings,
* if it operates under a corporate proxy.
*/
public interface HttpEngine {
/**
* @return cancelable Http request call
*/
HttpCall call(HttpRequest request);

/**
* @return <code>true</code> if it uses proxy, <code>false</code> otherwise
*/
boolean isUsingProxy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

import java.lang.reflect.InvocationTargetException;

/**
* The <code>HttpEngineFactory</code> is a utility class that produces a common HTTP Engine API
* for different implementations. Currently, it supports:
* - HttpURLConnection ({@link EngineType#DEFAULT})
* - OkHttp ({@link EngineType#OKHTTP})
* <p>
* Please note that all methods in <code>HttpEngineFactory</code> are static.
*/
public interface HttpEngineFactory {

HttpEngine create(HttpEngineConfig config);
EngineType getEngineType();

static HttpEngineFactory getFirstAvailable() {
HttpEngineFactory okHttpFactory = tryGetOkHttpFactory();
if (okHttpFactory != null) return okHttpFactory;
Expand All @@ -19,7 +24,8 @@ static HttpEngineFactory tryGetOkHttpFactory() {
try {
Class<?> okHttpFactoryClass = Class.forName("io.ably.lib.network.OkHttpEngineFactory");
return (HttpEngineFactory) okHttpFactoryClass.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException |
InvocationTargetException e) {
return null;
}
}
Expand All @@ -28,8 +34,13 @@ static HttpEngineFactory tryGetDefaultFactory() {
try {
Class<?> defaultFactoryClass = Class.forName("io.ably.lib.network.DefaultHttpEngineFactory");
return (HttpEngineFactory) defaultFactoryClass.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException |
InvocationTargetException e) {
return null;
}
}

HttpEngine create(HttpEngineConfig config);

EngineType getEngineType();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package io.ably.lib.network;

/**
* WebSocketClient instance bind to the specified URI.
* The connection will be established once you call <var>connect</var>.
*/
public interface WebSocketClient {

/**
* Establish connection to the Websocket server
*/
void connect();

/**
Expand All @@ -12,7 +19,7 @@ public interface WebSocketClient {
/**
* Sends the closing handshake. May be sent in response to any other handshake.
*
* @param code the closing code
* @param code the closing code
* @param reason the closing message
*/
void close(int code, String reason);
Expand All @@ -21,13 +28,23 @@ public interface WebSocketClient {
* This will close the connection immediately without a proper close handshake. The code and the
* message therefore won't be transferred over the wire also they will be forwarded to `onClose`.
*
* @param code the closing code
* @param code the closing code
* @param reason the closing message
**/
void cancel(int code, String reason);

/**
* Sends binary <var>message</var> to the connected webSocket server.
*
* @param message The byte-Array of data to send to the WebSocket server.
*/
void send(byte[] message);

/**
* Sends <var>message</var> to the connected websocket server.
*
* @param message The string which will be transmitted.
*/
void send(String message);

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.ably.lib.network;

/**
* Create WebSocket client bind to the specific URL
*/
public interface WebSocketEngine {
WebSocketClient create(String url, WebSocketListener listener);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

import java.lang.reflect.InvocationTargetException;

/**
* The <code>WebSocketEngineFactory</code> is a utility class that produces a common WebSocket Engine API
* for different implementations. Currently, it supports:
* - TooTallNate/Java-WebSocket ({@link EngineType#DEFAULT})
* - OkHttp ({@link EngineType#OKHTTP})
* <p>
* Please note that all methods in <code>WebSocketEngineFactory</code> are static.
*/
public interface WebSocketEngineFactory {
WebSocketEngine create(WebSocketEngineConfig config);
EngineType getEngineType();

static WebSocketEngineFactory getFirstAvailable() {
WebSocketEngineFactory okWebSocketFactory = tryGetOkWebSocketFactory();
if (okWebSocketFactory != null) return okWebSocketFactory;
Expand All @@ -28,8 +33,13 @@ static WebSocketEngineFactory tryGetDefaultFactory() {
try {
Class<?> defaultFactoryClass = Class.forName("io.ably.lib.network.DefaultWebSocketEngineFactory");
return (WebSocketEngineFactory) defaultFactoryClass.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException |
InvocationTargetException e) {
return null;
}
}

WebSocketEngine create(WebSocketEngineConfig config);

EngineType getEngineType();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,56 @@

import java.nio.ByteBuffer;

/**
* WebSocket Listener
*/
public interface WebSocketListener {
/**
* Called after an opening handshake has been performed and the given websocket is ready to be
* written on.
*/
void onOpen();

/**
* Callback for binary messages received from the remote host
*
* @param blob The binary message that was received.
* @see #onMessage(String)
**/
void onMessage(ByteBuffer blob);

/**
* Callback for string messages received from the remote host
*
* @param string The UTF-8 decoded message that was received.
* @see #onMessage(ByteBuffer)
**/
void onMessage(String string);

/**
* Callback for receiving ping frame if it supported by websocket engine
*/
void onWebsocketPing();

/**
* Called after the websocket connection has been closed.
*
* @param reason Additional information string
**/
void onClose(int code, String reason);

/**
* Called when errors occurs. If an error causes the websocket connection to fail {@link
* WebSocketListener#onClose(int, String)} will be called additionally.<br> This method will be called
* primarily because of IO or protocol errors.<br> If the given exception is an RuntimeException
* that probably means that you encountered a bug.<br>
*
* @param throwable The exception causing this error
**/
void onError(Throwable throwable);

/**
* We invoke this callback when runtime is not able to use secure https algorithms (TLS 1.2 +)
*/
void onOldJavaVersionDetected(Throwable throwable);
}

0 comments on commit 576374b

Please sign in to comment.