From 8ebc82acf3924923633ec31a0ff4906ce9d3b19c Mon Sep 17 00:00:00 2001 From: Sean Gilligan Date: Sat, 23 Sep 2023 14:27:45 -0700 Subject: [PATCH] Add missing JsonRpcTransport from previous commit --- .../consensusj/jsonrpc/JsonRpcTransport.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 consensusj-jsonrpc/src/main/java/org/consensusj/jsonrpc/JsonRpcTransport.java diff --git a/consensusj-jsonrpc/src/main/java/org/consensusj/jsonrpc/JsonRpcTransport.java b/consensusj-jsonrpc/src/main/java/org/consensusj/jsonrpc/JsonRpcTransport.java new file mode 100644 index 000000000..72b528b39 --- /dev/null +++ b/consensusj-jsonrpc/src/main/java/org/consensusj/jsonrpc/JsonRpcTransport.java @@ -0,0 +1,104 @@ +package org.consensusj.jsonrpc; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; + +// TODO: Have JsonRpcClientJavaNet and JsonRpcClientHttpUrlConnection directly implement this class +// and AbstractRpcClient become DefaultRpcClient and take a transport instance in its constructor. +/** + * Defines the interface for a network-layer implementation of a JSON-RPC client. + */ +public interface JsonRpcTransport extends AsyncSupport { + /** + * Get the URI of the remote server + * @return URI of remote server + */ + URI getServerURI(); + + /** + * Send a {@link JsonRpcRequest} for a {@link JsonRpcResponse} + *

Synchronous subclasses should override this method to prevent {@link CompletableFuture#supplyAsync(Supplier)} from + * being called twice when {@link AsyncSupport} is being used. Eventually we'll migrate more of the codebase to native + * async, and then we won't have to worry about calling {@code supplyAsync} twice. + * @param Type of result object + * @param request The request to send + * @param responseType The response type expected (used by Jackson for conversion) + * @return A JSON RPC Response with `result` of type `R` + * @throws IOException network error + * @throws JsonRpcStatusException JSON RPC status error + */ + default JsonRpcResponse sendRequestForResponse(JsonRpcRequest request, T responseType) throws IOException, JsonRpcStatusException { + return syncGet(sendRequestForResponseAsync(request, responseType)); + } + + /** + * Send a {@link JsonRpcRequest} for a {@link JsonRpcResponse} asynchronously. + * @param Type of result object + * @param request The request to send + * @param responseType The response type expected (used by Jackson for conversion) + * @return A future JSON RPC Response with `result` of type `R` + */ + CompletableFuture> sendRequestForResponseAsync(JsonRpcRequest request, T responseType); + + /** + * Synchronously complete a JSON-RPC request by calling {@link CompletableFuture#get()}, unwrapping nested + * {@link JsonRpcException} or {@link IOException} from {@link ExecutionException}. + * @param future The {@code CompletableFuture} (result of JSON-RPC request) to unwrap + * @return A JSON-RPC result + * @param The expected result type + * @throws IOException If {@link CompletableFuture#get} threw {@code ExecutionException} caused by {@code IOException} + * @throws JsonRpcException If {@link CompletableFuture#get} threw {@code ExecutionException} caused by {@code JsonRpcException} + * @throws RuntimeException If {@link CompletableFuture#get} threw {@link InterruptedException} or other {@link ExecutionException}. + */ + default R syncGet(CompletableFuture future) throws IOException, JsonRpcException { + try { + return future.get(); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } catch (ExecutionException ee) { + Throwable cause = ee.getCause(); + if (cause instanceof JsonRpcException) { + throw (JsonRpcException) cause; + } else if (cause instanceof IOException) { + throw (IOException) cause; + } else { + throw new RuntimeException(ee); + } + } + } + + + + /** + * Encode username password as Base64 for basic authentication + *

+ * We're using {@link java.util.Base64}, which requires Android 8.0 or later. + * + * @param authString An authorization string of the form `username:password` + * @return A compliant Base64 encoding of `authString` + */ + static String base64Encode(String authString) { + return Base64.getEncoder().encodeToString(authString.getBytes()).trim(); + } + + /** + * + * Return the default {@link SSLContext} without declaring a checked exception + * @return The default {@code SSLContext} + */ + static SSLContext getDefaultSSLContext() { + try { + return SSLContext.getDefault(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + +}