Skip to content

Commit f1b0200

Browse files
(Client): Introduce the ApiChannel interface
- Add the `ApiChannel` low-level interface for writing and reading encoded bytes to API connection - Add code for obtaining ApiChannel instances from PCloudAPIClient - Update related unit tests
1 parent 55bb17e commit f1b0200

File tree

13 files changed

+735
-73
lines changed

13 files changed

+735
-73
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.pcloud.networking.client;
2+
3+
import com.pcloud.networking.protocol.ProtocolRequestWriter;
4+
import com.pcloud.networking.protocol.ProtocolResponseReader;
5+
import okio.BufferedSink;
6+
7+
import java.io.Closeable;
8+
9+
/**
10+
* A low-level contract for interfacing with an pCloud API Host via
11+
* binary protocol-encoded messages.
12+
* <p>
13+
* The {@linkplain ApiChannel} interface exposes the lowest possible level
14+
* of detail when writing/reading messages from the API by still abstracting away
15+
* the details of connection establishment, TLS handshaking and so on.
16+
* <p>
17+
* The interface can be used in the cases where the flows using the {@linkplain Call} and {@linkplain MultiCall}
18+
* abstractions may yield a considerable amount of memory allocations, usually when making a chain
19+
* of repeating requests. The contract does not restrict in any way how the requests will be written, so
20+
* request pipelining can be implemented by writing multiple requests at once, without immediately reading the
21+
* responses, which may result in considerable speed improvements.
22+
* <p>
23+
* If otherwise stated, instances of this interface will not be thread-safe and reading/writing at the same time
24+
* from multiple threads will result in undetermined behavior. All implementations should allow calling
25+
* {@linkplain #close()} from multiple threads multiple times.
26+
* <p>
27+
* Calling {@linkplain #close()} on an idle {@linkplain ApiChannel} instance will result in connection recycling,
28+
* for other cases the underlying socket will be closed.
29+
* <br>
30+
* A {@linkplain ApiChannel} instance is considered idle when the number of sent requests is equal
31+
* to the number of fully read responses.
32+
* <br>
33+
* Each successful call to {@linkplain ProtocolRequestWriter#endRequest()} is counted as a sent request.
34+
* <br>
35+
* A fully read response is counted after each successful call to {@linkplain ProtocolResponseReader#endResponse()}
36+
* for non-data responses or to {@linkplain ProtocolResponseReader#readData(BufferedSink)} for data-containing responses.
37+
* <p>
38+
* Instances can be obtained via the {@linkplain PCloudAPIClient#newChannel(Endpoint)} and
39+
* {@linkplain PCloudAPIClient#newChannel()} methods.
40+
*/
41+
public interface ApiChannel extends Closeable, AutoCloseable {
42+
/**
43+
* @return the non-null {@linkplain Endpoint} to which this channel is connected.
44+
*/
45+
Endpoint endpoint();
46+
47+
/**
48+
* @return a non-null {@linkplain ProtocolResponseReader} for reading data.
49+
*/
50+
ProtocolResponseReader reader();
51+
52+
/**
53+
* @return a non-null {@linkplain ProtocolRequestWriter} for writing data.
54+
*/
55+
ProtocolRequestWriter writer();
56+
57+
/**
58+
* @return {@code true} if the channel is not closed, {@code false} otherwise.
59+
*/
60+
boolean isOpen();
61+
62+
@Override
63+
void close();
64+
}

binapi-client/src/main/java/com/pcloud/networking/client/PCloudAPIClient.java

+32-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import javax.net.SocketFactory;
2222
import javax.net.ssl.HostnameVerifier;
2323
import javax.net.ssl.SSLSocketFactory;
24+
import java.io.IOException;
2425
import java.util.ArrayList;
2526
import java.util.Collection;
2627
import java.util.Collections;
@@ -192,6 +193,37 @@ public MultiCall newCall(Collection<Request> requests, Endpoint endpoint) {
192193
connectionProvider, endpoint);
193194
}
194195

196+
/**
197+
* Create a new {@linkplain ApiChannel} for a given {@linkplain Endpoint}.
198+
*<p>
199+
* Same as calling {@linkplain #newChannel(Endpoint)} with the endpoint returned
200+
* by this client's {@linkplain EndpointProvider} instance.
201+
*
202+
* @see ApiChannel
203+
* @see #newChannel(Endpoint)
204+
* @see EndpointProvider
205+
* @return a non-null {@linkplain ApiChannel} instance
206+
* @throws IOException on a connection error
207+
*/
208+
public ApiChannel newChannel() throws IOException {
209+
return new RealApiChannel(connectionProvider, endpointProvider.endpoint());
210+
}
211+
212+
/**
213+
* Create a new {@linkplain ApiChannel} for a given {@linkplain Endpoint}.
214+
*<p>
215+
* The call will create and initialize an {@linkplain ApiChannel} instance to a given endpoint,
216+
* or throw an {@linkplain IOException} on error.
217+
*
218+
* @see ApiChannel
219+
* @param endpoint a target {@linkplain Endpoint}
220+
* @return a non-null {@linkplain ApiChannel} instance
221+
* @throws IOException on a connection error
222+
*/
223+
public ApiChannel newChannel(Endpoint endpoint) throws IOException {
224+
return new RealApiChannel(connectionProvider, endpoint);
225+
}
226+
195227
/**
196228
* Returns the maximum amount of time a connection should take to establish itself in milliseconds as an int
197229
*
@@ -255,7 +287,6 @@ public ConnectionPool connectionPool() {
255287
return connectionPool;
256288
}
257289

258-
259290
/**
260291
* Returns the {@linkplain ExecutorService} for this client
261292
*

0 commit comments

Comments
 (0)