+++ /dev/null
-package io.bytom.http;\r
-\r
-import com.google.gson.Gson;\r
-import com.google.gson.JsonElement;\r
-import com.google.gson.JsonParser;\r
-import com.squareup.okhttp.*;\r
-import io.bytom.common.Configuration;\r
-import io.bytom.common.Utils;\r
-import io.bytom.exception.APIException;\r
-import io.bytom.exception.BytomException;\r
-import io.bytom.exception.ConfigurationException;\r
-import io.bytom.exception.JSONException;\r
-import org.apache.log4j.Logger;\r
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;\r
-\r
-import javax.net.ssl.*;\r
-import java.io.*;\r
-import java.lang.reflect.Type;\r
-import java.net.MalformedURLException;\r
-import java.net.Proxy;\r
-import java.net.URL;\r
-import java.nio.file.Files;\r
-import java.nio.file.Paths;\r
-import java.security.GeneralSecurityException;\r
-import java.security.KeyFactory;\r
-import java.security.KeyStore;\r
-import java.security.cert.Certificate;\r
-import java.security.cert.CertificateFactory;\r
-import java.security.cert.X509Certificate;\r
-import java.security.spec.KeySpec;\r
-import java.security.spec.PKCS8EncodedKeySpec;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Objects;\r
-import java.util.Random;\r
-import java.util.concurrent.TimeUnit;\r
-\r
-/**\r
- * The Client object contains all information necessary to\r
- * perform an HTTP request against a remote API. Typically,\r
- * an application will have a client that makes requests to\r
- * a Chain Core, and a separate Client that makes requests\r
- * to an HSM server.\r
- */\r
-public class Client {\r
- private String url;\r
- private String accessToken;\r
- private OkHttpClient httpClient;\r
-\r
- // Used to create empty, in-memory key stores.\r
- private static final char[] DEFAULT_KEYSTORE_PASSWORD = "123456".toCharArray();\r
- private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");\r
- private static String version = "dev"; // updated in the static initializer\r
-\r
- public static Logger logger = Logger.getLogger(Client.class);\r
-\r
- private static class BuildProperties {\r
- public String version;\r
- }\r
-\r
- static {\r
- InputStream in = Client.class.getClassLoader().getResourceAsStream("properties.json");\r
- if (in != null) {\r
- InputStreamReader inr = new InputStreamReader(in);\r
- version = Utils.serializer.fromJson(inr, BuildProperties.class).version;\r
- }\r
- }\r
-\r
- public static Client generateClient() throws BytomException {\r
-\r
- String coreURL = Configuration.getValue("bytom.api.url");\r
- String accessToken = Configuration.getValue("client.access.token");\r
-\r
- if (coreURL == null || coreURL.isEmpty()) {\r
- coreURL = "http://47.91.254.104:8888";\r
- }\r
-\r
- if (coreURL.endsWith("/")) {\r
- //split the last char "/"\r
- coreURL = coreURL.substring(0, coreURL.length() - 1);\r
- logger.info("check the coreURL is right.");\r
- }\r
-\r
- return new Client(coreURL, accessToken);\r
- }\r
-\r
- public Client(Builder builder) throws ConfigurationException {\r
- if (builder.url.endsWith("/")) {\r
- //split the last char "/"\r
- builder.url = builder.url.substring(0, builder.url.length() - 1);\r
- }\r
- this.url = builder.url;\r
- this.accessToken = builder.accessToken;\r
- this.httpClient = buildHttpClient(builder);\r
- }\r
-\r
- /**\r
- * Create a new http Client object using the default development host URL.\r
- */\r
- public Client() throws BytomException {\r
- this(new Builder());\r
- }\r
-\r
- /**\r
- * Create a new http Client object\r
- *\r
- * @param url the URL of the Chain Core or HSM\r
- */\r
- public Client(String url) throws BytomException {\r
- this(new Builder().setUrl(url));\r
- }\r
-\r
- /**\r
- * Create a new http Client object\r
- *\r
- * @param url the URL of the Chain Core or HSM\r
- * @param accessToken a Client API access token\r
- */\r
- public Client(String url, String accessToken) throws BytomException {\r
- this(new Builder().setUrl(url).setAccessToken(accessToken));\r
- }\r
-\r
- /**\r
- * Perform a single HTTP POST request against the API for a specific action.\r
- *\r
- * @param action The requested API action\r
- * @param body Body payload sent to the API as JSON\r
- * @param tClass Type of object to be deserialized from the response JSON\r
- * @return the result of the post request\r
- * @throws BytomException\r
- */\r
- public <T> T request(String action, Object body, final Type tClass) throws BytomException {\r
- ResponseCreator<T> rc =\r
- new ResponseCreator<T>() {\r
- public T create(Response response, Gson deserializer) throws IOException, BytomException {\r
- JsonElement root = new JsonParser().parse(response.body().charStream());\r
- JsonElement status = root.getAsJsonObject().get("status");\r
- JsonElement data = root.getAsJsonObject().get("data");\r
- if (status != null && status.toString().contains("fail")) {\r
- throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
- } else if (data != null) {\r
- return deserializer.fromJson(data, tClass);\r
- } else {\r
- return deserializer.fromJson(response.body().charStream(), tClass);\r
- }\r
- }\r
- };\r
- return post(action, body, rc);\r
- }\r
-\r
- /**\r
- * Perform a single HTTP POST request against the API for a specific action,\r
- * ignoring the body of the response.\r
- *\r
- * @param action The requested API action\r
- * @param body Body payload sent to the API as JSON\r
- * @throws BytomException\r
- */\r
- public void request(String action, Object body) throws BytomException {\r
- ResponseCreator<Object> rc =\r
- new ResponseCreator<Object>() {\r
- public Object create(Response response, Gson deserializer) throws IOException, BytomException {\r
- JsonElement root = new JsonParser().parse(response.body().charStream());\r
- JsonElement status = root.getAsJsonObject().get("status");\r
- JsonElement data = root.getAsJsonObject().get("data");\r
- if (status != null && status.toString().contains("fail")) {\r
- throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
- }\r
- return null;\r
- }\r
- };\r
- post(action, body, rc);\r
- }\r
-\r
- /**\r
- * return the value of named as key from json\r
- *\r
- * @param action\r
- * @param body\r
- * @param key\r
- * @param tClass\r
- * @param <T>\r
- * @return\r
- * @throws BytomException\r
- */\r
- public <T> T requestGet(String action, Object body, final String key, final Type tClass)\r
- throws BytomException {\r
- ResponseCreator<T> rc = new ResponseCreator<T>() {\r
- public T create(Response response, Gson deserializer) throws IOException,\r
- BytomException {\r
- JsonElement root = new JsonParser().parse(response.body().charStream());\r
- JsonElement status = root.getAsJsonObject().get("status");\r
- JsonElement data = root.getAsJsonObject().get("data");\r
-\r
- if (status != null && status.toString().contains("fail"))\r
- throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
- else if (data != null)\r
- return deserializer.fromJson(data.getAsJsonObject().get(key), tClass);\r
- else\r
- return deserializer.fromJson(response.body().charStream(), tClass);\r
- }\r
- };\r
- return post(action, body, rc);\r
- }\r
-\r
- /**\r
- * Perform a single HTTP POST request against the API for a specific action.\r
- * Use this method if you want batch semantics, i.e., the endpoint response\r
- * is an array of valid objects interleaved with arrays, once corresponding to\r
- * each input object.\r
- *\r
- * @param action The requested API action\r
- * @param body Body payload sent to the API as JSON\r
- * @param tClass Type of object to be deserialized from the response JSON\r
- * @return the result of the post request\r
- * @throws BytomException\r
- */\r
- /*\r
- public <T> T requestBatch(String action, Object body, final Type tClass) throws BytomException {\r
- ResponseCreator<T> rc =\r
- new ResponseCreator<T>() {\r
- public T create(Response response, Gson deserializer) throws IOException, BytomException {\r
- JsonElement root = new JsonParser().parse(response.body().charStream());\r
- JsonElement status = root.getAsJsonObject().get("status");\r
- JsonElement data = root.getAsJsonObject().get("data");\r
- if (status != null && status.toString().contains("fail")) {\r
- throw new BytomException(root.getAsJsonObject().get("msg").toString());\r
- } else if (data != null) {\r
- return deserializer.fromJson(data, tClass);\r
- } else {\r
- return deserializer.fromJson(response.body().charStream(), tClass);\r
- }\r
- }\r
- };\r
- //把object转换为json对象数组(有点难)\r
-\r
- //根据数组的大小循环调用post()方法\r
-\r
- //重写create()接口方法,对成功和失败做不同的处理\r
-\r
- //调用BatchResponse(Map<Integer, T> successes, Map<Integer, APIException> errors)\r
- //构造方法,最后返回BatchResponse实例对象\r
-\r
- return post(action, body, rc);\r
- }\r
- */\r
-\r
-\r
- /**\r
- * Returns true if a client access token stored in the client.\r
- *\r
- * @return a boolean\r
- */\r
- public boolean hasAccessToken() {\r
- return this.accessToken != null && !this.accessToken.isEmpty();\r
- }\r
-\r
- /**\r
- * Returns the client access token (possibly null).\r
- *\r
- * @return the client access token\r
- */\r
- public String accessToken() {\r
- return accessToken;\r
- }\r
-\r
- public String getUrl() {\r
- return url;\r
- }\r
-\r
- /**\r
- * Pins a public key to the HTTP client.\r
- *\r
- * @param provider certificate provider\r
- * @param subjPubKeyInfoHash public key hash\r
- */\r
- public void pinCertificate(String provider, String subjPubKeyInfoHash) {\r
- CertificatePinner cp =\r
- new CertificatePinner.Builder().add(provider, subjPubKeyInfoHash).build();\r
- this.httpClient.setCertificatePinner(cp);\r
- }\r
-\r
- /**\r
- * Sets the default connect timeout for new connections. A value of 0 means no timeout.\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public void setConnectTimeout(long timeout, TimeUnit unit) {\r
- this.httpClient.setConnectTimeout(timeout, unit);\r
- }\r
-\r
- /**\r
- * Sets the default read timeout for new connections. A value of 0 means no timeout.\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public void setReadTimeout(long timeout, TimeUnit unit) {\r
- this.httpClient.setReadTimeout(timeout, unit);\r
- }\r
-\r
- /**\r
- * Sets the default write timeout for new connections. A value of 0 means no timeout.\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public void setWriteTimeout(long timeout, TimeUnit unit) {\r
- this.httpClient.setWriteTimeout(timeout, unit);\r
- }\r
-\r
- /**\r
- * Sets the proxy information for the HTTP client.\r
- *\r
- * @param proxy proxy object\r
- */\r
- public void setProxy(Proxy proxy) {\r
- this.httpClient.setProxy(proxy);\r
- }\r
-\r
- /**\r
- * Defines an interface for deserializing HTTP responses into objects.\r
- *\r
- * @param <T> the type of object to return\r
- */\r
- public interface ResponseCreator<T> {\r
- /**\r
- * Deserializes an HTTP response into a Java object of type T.\r
- *\r
- * @param response HTTP response object\r
- * @param deserializer json deserializer\r
- * @return an object of type T\r
- * @throws BytomException\r
- * @throws IOException\r
- */\r
- T create(Response response, Gson deserializer) throws BytomException, IOException;\r
- }\r
-\r
- /**\r
- * Builds and executes an HTTP Post request.\r
- *\r
- * @param path the path to the endpoint\r
- * @param body the request body\r
- * @param respCreator object specifying the response structure\r
- * @return a response deserialized into type T\r
- * @throws BytomException\r
- */\r
- private <T> T post(String path, Object body, ResponseCreator<T> respCreator)\r
- throws BytomException {\r
-\r
- RequestBody requestBody = RequestBody.create(this.JSON, Utils.serializer.toJson(body));\r
- Request req;\r
-\r
- BytomException exception = null;\r
- URL endpointURL = null;\r
-\r
- try {\r
- endpointURL = new URL(url + "/" + path);\r
- } catch (MalformedURLException e) {\r
- e.printStackTrace();\r
- }\r
-\r
- Request.Builder builder =\r
- new Request.Builder()\r
- .header("User-Agent", "bytom-sdk-java/" + version)\r
- .url(endpointURL)\r
- .method("POST", requestBody);\r
- if (hasAccessToken()) {\r
- builder = builder.header("Authorization", buildCredentials());\r
- }\r
- req = builder.build();\r
-\r
- Response resp = null;\r
-\r
- T object = null;\r
-\r
- try {\r
- resp = this.checkError(this.httpClient.newCall(req).execute());\r
- object = respCreator.create(resp, Utils.serializer);\r
- } catch (IOException e) {\r
- e.printStackTrace();\r
- }\r
-\r
- return object;\r
- }\r
-\r
- private OkHttpClient buildHttpClient(Builder builder) throws ConfigurationException {\r
- OkHttpClient httpClient = builder.baseHttpClient.clone();\r
-\r
- try {\r
- if (builder.trustManagers != null) {\r
- SSLContext sslContext = SSLContext.getInstance("TLS");\r
- sslContext.init(builder.keyManagers, builder.trustManagers, null);\r
- httpClient.setSslSocketFactory(sslContext.getSocketFactory());\r
- }\r
- } catch (GeneralSecurityException ex) {\r
- throw new ConfigurationException("Unable to configure TLS", ex);\r
- }\r
- if (builder.readTimeoutUnit != null) {\r
- httpClient.setReadTimeout(builder.readTimeout, builder.readTimeoutUnit);\r
- }\r
- if (builder.writeTimeoutUnit != null) {\r
- httpClient.setWriteTimeout(builder.writeTimeout, builder.writeTimeoutUnit);\r
- }\r
- if (builder.connectTimeoutUnit != null) {\r
- httpClient.setConnectTimeout(builder.connectTimeout, builder.connectTimeoutUnit);\r
- }\r
- if (builder.pool != null) {\r
- httpClient.setConnectionPool(builder.pool);\r
- }\r
- if (builder.proxy != null) {\r
- httpClient.setProxy(builder.proxy);\r
- }\r
- if (builder.cp != null) {\r
- httpClient.setCertificatePinner(builder.cp);\r
- }\r
-\r
- return httpClient;\r
- }\r
-\r
- private static final Random randomGenerator = new Random();\r
- private static final int MAX_RETRIES = 10;\r
- private static final int RETRY_BASE_DELAY_MILLIS = 40;\r
-\r
- // the max amount of time cored leader election could take\r
- private static final int RETRY_MAX_DELAY_MILLIS = 15000;\r
-\r
- private static int retryDelayMillis(int retryAttempt) {\r
- // Calculate the max delay as base * 2 ^ (retryAttempt - 1).\r
- int max = RETRY_BASE_DELAY_MILLIS * (1 << (retryAttempt - 1));\r
- max = Math.min(max, RETRY_MAX_DELAY_MILLIS);\r
-\r
- // To incorporate jitter, use a pseudo random delay between [max/2, max] millis.\r
- return randomGenerator.nextInt(max / 2) + max / 2 + 1;\r
- }\r
-\r
- private static final int[] RETRIABLE_STATUS_CODES = {\r
- 408, // Request Timeout\r
- 429, // Too Many Requests\r
- 500, // Internal Server Error\r
- 502, // Bad Gateway\r
- 503, // Service Unavailable\r
- 504, // Gateway Timeout\r
- 509, // Bandwidth Limit Exceeded\r
- };\r
-\r
- private static boolean isRetriableStatusCode(int statusCode) {\r
- for (int i = 0; i < RETRIABLE_STATUS_CODES.length; i++) {\r
- if (RETRIABLE_STATUS_CODES[i] == statusCode) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- private Response checkError(Response response) throws BytomException {\r
- /*\r
- String rid = response.headers().get("Bytom-Request-ID");\r
- if (rid == null || rid.length() == 0) {\r
- // Header field Bytom-Request-ID is set by the backend\r
- // API server. If this field is set, then we can expect\r
- // the body to be well-formed JSON. If it's not set,\r
- // then we are probably talking to a gateway or proxy.\r
- throw new ConnectivityException(response);\r
- } */\r
-\r
- if ((response.code() / 100) != 2) {\r
- try {\r
- APIException err =\r
- Utils.serializer.fromJson(response.body().charStream(), APIException.class);\r
- if (err.code != null) {\r
- //err.requestId = rid;\r
- err.statusCode = response.code();\r
- throw err;\r
- }\r
- } catch (IOException ex) {\r
- //throw new JSONException("Unable to read body. " + ex.getMessage(), rid);\r
- throw new JSONException("Unable to read body. ");\r
- }\r
- }\r
- return response;\r
- }\r
-\r
- private String buildCredentials() {\r
- String user = "";\r
- String pass = "";\r
- if (hasAccessToken()) {\r
- String[] parts = accessToken.split(":");\r
- if (parts.length >= 1) {\r
- user = parts[0];\r
- }\r
- if (parts.length >= 2) {\r
- pass = parts[1];\r
- }\r
- }\r
- return Credentials.basic(user, pass);\r
- }\r
-\r
- /**\r
- * Overrides {@link Object#hashCode()}\r
- *\r
- * @return the hash code\r
- */\r
- @Override\r
- public int hashCode() {\r
- int code = this.url.hashCode();\r
- if (this.hasAccessToken()) {\r
- code = code * 31 + this.accessToken.hashCode();\r
- }\r
- return code;\r
- }\r
-\r
- /**\r
- * Overrides {@link Object#equals(Object)}\r
- *\r
- * @param o the object to compare\r
- * @return a boolean specifying equality\r
- */\r
- @Override\r
- public boolean equals(Object o) {\r
- if (o == null) return false;\r
- if (!(o instanceof Client)) return false;\r
-\r
- Client other = (Client) o;\r
- if (!this.url.equalsIgnoreCase(other.url)) {\r
- return false;\r
- }\r
- return Objects.equals(this.accessToken, other.accessToken);\r
- }\r
-\r
- /**\r
- * A builder class for creating client objects\r
- */\r
- public static class Builder {\r
-\r
- private String url;\r
-\r
- private OkHttpClient baseHttpClient;\r
- private String accessToken;\r
- private CertificatePinner cp;\r
- private KeyManager[] keyManagers;\r
- private TrustManager[] trustManagers;\r
- private long connectTimeout;\r
- private TimeUnit connectTimeoutUnit;\r
- private long readTimeout;\r
- private TimeUnit readTimeoutUnit;\r
- private long writeTimeout;\r
- private TimeUnit writeTimeoutUnit;\r
- private Proxy proxy;\r
- private ConnectionPool pool;\r
-\r
- public Builder() {\r
- this.baseHttpClient = new OkHttpClient();\r
- this.baseHttpClient.setFollowRedirects(false);\r
- this.setDefaults();\r
- }\r
-\r
- public Builder(Client client) {\r
- this.baseHttpClient = client.httpClient.clone();\r
- this.url = client.url;\r
- this.accessToken = client.accessToken;\r
- }\r
-\r
- private void setDefaults() {\r
- this.setReadTimeout(30, TimeUnit.SECONDS);\r
- this.setWriteTimeout(30, TimeUnit.SECONDS);\r
- this.setConnectTimeout(30, TimeUnit.SECONDS);\r
- this.setConnectionPool(50, 2, TimeUnit.MINUTES);\r
- }\r
-\r
- public Builder setUrl(String url) {\r
- this.url = url;\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the access token for the client\r
- *\r
- * @param accessToken The access token for the Chain Core or HSM\r
- */\r
- public Builder setAccessToken(String accessToken) {\r
- this.accessToken = accessToken;\r
- return this;\r
- }\r
-\r
-\r
- /**\r
- * Trusts the given CA certs, and no others. Use this if you are running\r
- * your own CA, or are using a self-signed server certificate.\r
- *\r
- * @param is input stream of the certificates to trust, in PEM format.\r
- */\r
- public Builder setTrustedCerts(InputStream is) throws ConfigurationException {\r
- try {\r
- // Extract certs from PEM-encoded input.\r
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");\r
- Collection<? extends Certificate> certificates =\r
- certificateFactory.generateCertificates(is);\r
- if (certificates.isEmpty()) {\r
- throw new IllegalArgumentException("expected non-empty set of trusted certificates");\r
- }\r
-\r
- // Create a new key store and input the cert.\r
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());\r
- keyStore.load(null, DEFAULT_KEYSTORE_PASSWORD);\r
- int index = 0;\r
- for (Certificate certificate : certificates) {\r
- String certificateAlias = Integer.toString(index++);\r
- keyStore.setCertificateEntry(certificateAlias, certificate);\r
- }\r
-\r
- // Use key store to build an X509 trust manager.\r
- KeyManagerFactory keyManagerFactory =\r
- KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());\r
- keyManagerFactory.init(keyStore, DEFAULT_KEYSTORE_PASSWORD);\r
- TrustManagerFactory trustManagerFactory =\r
- TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\r
- trustManagerFactory.init(keyStore);\r
- TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();\r
- if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {\r
- throw new IllegalStateException(\r
- "Unexpected default trust managers:" + Arrays.toString(trustManagers));\r
- }\r
-\r
- this.trustManagers = trustManagers;\r
- return this;\r
- } catch (GeneralSecurityException | IOException ex) {\r
- throw new ConfigurationException("Unable to configure trusted CA certs", ex);\r
- }\r
- }\r
-\r
- /**\r
- * Trusts the given CA certs, and no others. Use this if you are running\r
- * your own CA, or are using a self-signed server certificate.\r
- *\r
- * @param path The path of a file containing certificates to trust, in PEM format.\r
- */\r
- public Builder setTrustedCerts(String path) throws ConfigurationException {\r
- try (InputStream is = new FileInputStream(path)) {\r
- return setTrustedCerts(is);\r
- } catch (IOException ex) {\r
- throw new ConfigurationException("Unable to configure trusted CA certs", ex);\r
- }\r
- }\r
-\r
- /**\r
- * Sets the certificate pinner for the client\r
- *\r
- * @param provider certificate provider\r
- * @param subjPubKeyInfoHash public key hash\r
- */\r
- public Builder pinCertificate(String provider, String subjPubKeyInfoHash) {\r
- this.cp = new CertificatePinner.Builder().add(provider, subjPubKeyInfoHash).build();\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the connect timeout for the client\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public Builder setConnectTimeout(long timeout, TimeUnit unit) {\r
- this.connectTimeout = timeout;\r
- this.connectTimeoutUnit = unit;\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the read timeout for the client\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public Builder setReadTimeout(long timeout, TimeUnit unit) {\r
- this.readTimeout = timeout;\r
- this.readTimeoutUnit = unit;\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the write timeout for the client\r
- *\r
- * @param timeout the number of time units for the default timeout\r
- * @param unit the unit of time\r
- */\r
- public Builder setWriteTimeout(long timeout, TimeUnit unit) {\r
- this.writeTimeout = timeout;\r
- this.writeTimeoutUnit = unit;\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the proxy for the client\r
- *\r
- * @param proxy\r
- */\r
- public Builder setProxy(Proxy proxy) {\r
- this.proxy = proxy;\r
- return this;\r
- }\r
-\r
- /**\r
- * Sets the connection pool for the client\r
- *\r
- * @param maxIdle the maximum number of idle http connections in the pool\r
- * @param timeout the number of time units until an idle http connection in the pool is closed\r
- * @param unit the unit of time\r
- */\r
- public Builder setConnectionPool(int maxIdle, long timeout, TimeUnit unit) {\r
- this.pool = new ConnectionPool(maxIdle, unit.toMillis(timeout));\r
- return this;\r
- }\r
-\r
- /**\r
- * Builds a client with all of the provided parameters.\r
- */\r
- public Client build() throws ConfigurationException {\r
- return new Client(this);\r
- }\r
- }\r
-}\r