Skip to content

Commit e378dfa

Browse files
bullet-toothdmitry-timofeev
authored andcommitted
Make port optional; add path prefix support [ECR-3223] (#997)
* now the port is optional in the host URL * an optional path prefix can be set to all requests
1 parent ae81ce0 commit e378dfa

File tree

10 files changed

+254
-53
lines changed

10 files changed

+254
-53
lines changed

exonum-light-client/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1616
## Unreleased
1717

1818
### Added
19-
* `ExonumClient#findNonEmptyBlocks` to find a certain number of the most recent non-empty
19+
- `ExonumClient#findNonEmptyBlocks` to find a certain number of the most recent non-empty
2020
blocks (from the last block and up to the genesis block). (#953)
21+
- Prefix URL can be set for routing all Light Client requests. (#997)
2122

2223
### Changed
2324
- `ExonumClient#getBlocks` accepts a closed range of block heights _[from; to]_
@@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2728
- `ExonumClient#getLastBlocks` returns only the blocks from the closed range
2829
_[max(0, blockchainHeight - count + 1), blockchainHeight]_. The size of
2930
the range _(min(count, blockchainHeight + 1))_ is no longer limited. (#953)
31+
- Now port is optional in the Exonum host URL. (#997)
3032

3133
## 0.2.0 - 2019-05-27
3234

exonum-light-client/src/main/java/com/exonum/client/ExonumClient.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class Builder {
201201

202202
private URL exonumHost;
203203
private OkHttpClient httpClient = DEFAULT_CLIENT;
204+
private String prefix = "";
204205

205206
/**
206207
* Sets Exonum host url.
@@ -235,13 +236,23 @@ public Builder setHttpClient(OkHttpClient client) {
235236
return this;
236237
}
237238

239+
/**
240+
* Sets an optional URL prefix to be applied to all requests made by the client.
241+
* Can be helpful in case of using middleware routing proxy on the blockchain node side.
242+
* There is no prefix by default.
243+
*/
244+
public Builder setPrefix(String prefix) {
245+
this.prefix = checkNotNull(prefix);
246+
return this;
247+
}
248+
238249
/**
239250
* Creates Exonum client instance.
240251
* @throws IllegalStateException if required fields weren't set
241252
*/
242253
public ExonumClient build() {
243254
checkRequiredFieldsSet();
244-
return new ExonumHttpClient(httpClient, exonumHost);
255+
return new ExonumHttpClient(httpClient, exonumHost, prefix);
245256
}
246257

247258
private void checkRequiredFieldsSet() {

exonum-light-client/src/main/java/com/exonum/client/ExonumHttpClient.java

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919

2020
import static com.exonum.client.ExonumApi.MAX_BLOCKS_PER_REQUEST;
2121
import static com.exonum.client.ExonumIterables.indexOf;
22-
import static com.exonum.client.ExonumUrls.BLOCK;
2322
import static com.exonum.client.ExonumUrls.BLOCKS;
2423
import static com.exonum.client.ExonumUrls.HEALTH_CHECK;
2524
import static com.exonum.client.ExonumUrls.MEMORY_POOL;
2625
import static com.exonum.client.ExonumUrls.TRANSACTIONS;
2726
import static com.exonum.client.ExonumUrls.USER_AGENT;
27+
import static com.exonum.client.HttpUrlHelper.getFullUrl;
2828
import static com.exonum.client.request.BlockFilteringOption.INCLUDE_EMPTY;
2929
import static com.exonum.client.request.BlockFilteringOption.SKIP_EMPTY;
3030
import static com.exonum.client.request.BlockTimeOption.INCLUDE_COMMIT_TIME;
@@ -34,6 +34,7 @@
3434
import static java.lang.Math.max;
3535
import static java.lang.Math.min;
3636
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
37+
import static java.util.Collections.emptyMap;
3738

3839
import com.exonum.binding.common.hash.HashCode;
3940
import com.exonum.binding.common.message.TransactionMessage;
@@ -46,6 +47,7 @@
4647
import com.exonum.client.response.HealthCheckInfo;
4748
import com.exonum.client.response.TransactionResponse;
4849
import com.google.common.collect.ImmutableList;
50+
import com.google.common.collect.ImmutableMap;
4951
import com.google.common.collect.Lists;
5052
import java.io.IOException;
5153
import java.net.URL;
@@ -72,49 +74,48 @@ class ExonumHttpClient implements ExonumClient {
7274

7375
private final OkHttpClient httpClient;
7476
private final URL exonumHost;
77+
private final String prefix;
7578

76-
ExonumHttpClient(OkHttpClient httpClient, URL exonumHost) {
79+
ExonumHttpClient(OkHttpClient httpClient, URL exonumHost, String prefix) {
7780
this.httpClient = httpClient;
7881
this.exonumHost = exonumHost;
82+
this.prefix = prefix;
7983
}
8084

8185
@Override
8286
public HashCode submitTransaction(TransactionMessage transactionMessage) {
83-
Request request = post(toFullUrl(TRANSACTIONS),
87+
Request request = post(url(TRANSACTIONS),
8488
ExplorerApiHelper.createSubmitTxBody(transactionMessage));
8589

8690
return blockingExecuteAndParse(request, ExplorerApiHelper::parseSubmitTxResponse);
8791
}
8892

8993
@Override
9094
public int getUnconfirmedTransactionsCount() {
91-
Request request = get(toFullUrl(MEMORY_POOL));
95+
Request request = get(url(MEMORY_POOL));
9296

9397
return blockingExecuteAndParse(request, SystemApiHelper::parseMemoryPoolJson);
9498
}
9599

96100
@Override
97101
public HealthCheckInfo healthCheck() {
98-
Request request = get(toFullUrl(HEALTH_CHECK));
102+
Request request = get(url(HEALTH_CHECK));
99103

100104
return blockingExecuteAndParse(request, SystemApiHelper::parseHealthCheckJson);
101105
}
102106

103107
@Override
104108
public String getUserAgentInfo() {
105-
Request request = get(toFullUrl(USER_AGENT));
109+
Request request = get(url(USER_AGENT));
106110

107111
return blockingExecutePlainText(request);
108112
}
109113

110114
@Override
111115
public Optional<TransactionResponse> getTransaction(HashCode id) {
112116
HashCode hash = checkNotNull(id);
113-
HttpUrl url = urlBuilder()
114-
.encodedPath(TRANSACTIONS)
115-
.addQueryParameter("hash", hash.toString())
116-
.build();
117-
Request request = get(url);
117+
Map<String, String> query = ImmutableMap.of("hash", hash.toString());
118+
Request request = get(url(TRANSACTIONS, query));
118119

119120
return blockingExecute(request, response -> {
120121
if (response.code() == HTTP_NOT_FOUND) {
@@ -140,11 +141,8 @@ public long getBlockchainHeight() {
140141
@Override
141142
public BlockResponse getBlockByHeight(long height) {
142143
checkArgument(0 <= height, "Height can't be negative, but was %s", height);
143-
HttpUrl url = urlBuilder()
144-
.encodedPath(BLOCK)
145-
.addQueryParameter("height", String.valueOf(height))
146-
.build();
147-
Request request = get(url);
144+
Map<String, String> query = ImmutableMap.of("height", String.valueOf(height));
145+
Request request = get(url(BLOCKS, query));
148146

149147
return blockingExecuteAndParse(request, ExplorerApiHelper::parseGetBlockResponse);
150148
}
@@ -282,11 +280,7 @@ private BlocksResponse doGetBlocks(int count, BlockFilteringOption blockFilter,
282280
if (heightMax != null) {
283281
query.put("latest", String.valueOf(heightMax));
284282
}
285-
286-
HttpUrl.Builder httpRequest = urlBuilder().encodedPath(BLOCKS);
287-
query.forEach(httpRequest::addQueryParameter);
288-
289-
Request request = get(httpRequest.build());
283+
Request request = get(url(BLOCKS, query));
290284

291285
return blockingExecuteAndParse(request, ExplorerApiHelper::parseGetBlocksResponse);
292286
}
@@ -305,18 +299,12 @@ private static Request post(HttpUrl url, String jsonBody) {
305299
.build();
306300
}
307301

308-
private HttpUrl toFullUrl(String relativeUrl) {
309-
return urlBuilder()
310-
.encodedPath(relativeUrl)
311-
.build();
302+
private HttpUrl url(String path, Map<String, String> query) {
303+
return getFullUrl(exonumHost, prefix, path, query);
312304
}
313305

314-
private HttpUrl.Builder urlBuilder() {
315-
316-
return new HttpUrl.Builder()
317-
.scheme(exonumHost.getProtocol())
318-
.host(exonumHost.getHost())
319-
.port(exonumHost.getPort());
306+
private HttpUrl url(String path) {
307+
return url(path, emptyMap());
320308
}
321309

322310
private <T> T blockingExecute(Request request, Function<Response, T> responseHandler) {

exonum-light-client/src/main/java/com/exonum/client/ExonumUrls.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
* Contains Exonum API URLs.
2222
*/
2323
final class ExonumUrls {
24-
private static final String EXPLORER_PATHS_PREFIX = "/api/explorer/v1";
25-
private static final String SYS_PATHS_PREFIX = "/api/system/v1";
24+
private static final String EXPLORER_PATHS_PREFIX = "api/explorer/v1";
25+
private static final String SYS_PATHS_PREFIX = "api/system/v1";
2626
static final String TRANSACTIONS = EXPLORER_PATHS_PREFIX + "/transactions";
2727
static final String BLOCK = EXPLORER_PATHS_PREFIX + "/block";
2828
static final String BLOCKS = EXPLORER_PATHS_PREFIX + "/blocks";
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2019 The Exonum Team
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.exonum.client;
18+
19+
import static com.google.common.base.Preconditions.checkNotNull;
20+
21+
import java.net.URL;
22+
import java.util.Map;
23+
import okhttp3.HttpUrl;
24+
import okhttp3.HttpUrl.Builder;
25+
26+
final class HttpUrlHelper {
27+
28+
static HttpUrl getFullUrl(URL host, String prefix, String relativeUrl,
29+
Map<String, String> encodedQueryParameters) {
30+
checkNotNull(encodedQueryParameters);
31+
Builder urlBuilder = urlHostBuilder(host)
32+
.addPathSegments(prefix)
33+
.addPathSegments(relativeUrl);
34+
encodedQueryParameters.forEach(urlBuilder::addEncodedQueryParameter);
35+
36+
return urlBuilder.build();
37+
}
38+
39+
private static HttpUrl.Builder urlHostBuilder(URL host) {
40+
checkNotNull(host);
41+
HttpUrl.Builder builder = new HttpUrl.Builder()
42+
.scheme(host.getProtocol())
43+
.host(host.getHost());
44+
if (host.getPort() != -1) {
45+
builder.port(host.getPort());
46+
}
47+
return builder;
48+
}
49+
50+
private HttpUrlHelper() {
51+
}
52+
}

exonum-light-client/src/test/java/com/exonum/client/ExonumHttpClientBlocksIntegrationTest.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import static com.exonum.client.ExonumApi.MAX_BLOCKS_PER_REQUEST;
3434
import static com.exonum.client.ExonumUrls.BLOCK;
3535
import static com.exonum.client.ExonumUrls.BLOCKS;
36+
import static com.exonum.client.TestUtils.assertPath;
3637
import static com.exonum.client.request.BlockFilteringOption.INCLUDE_EMPTY;
3738
import static com.exonum.client.request.BlockFilteringOption.SKIP_EMPTY;
3839
import static com.exonum.client.request.BlockTimeOption.INCLUDE_COMMIT_TIME;
@@ -50,7 +51,6 @@
5051
import static org.hamcrest.Matchers.hasSize;
5152
import static org.hamcrest.Matchers.is;
5253
import static org.hamcrest.Matchers.nullValue;
53-
import static org.hamcrest.Matchers.startsWith;
5454
import static org.junit.jupiter.api.Assertions.assertFalse;
5555
import static org.junit.jupiter.api.Assertions.assertThrows;
5656
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -123,7 +123,7 @@ void getBlockByHeight() throws InterruptedException {
123123
// Assert request params
124124
RecordedRequest recordedRequest = server.takeRequest();
125125
assertThat(recordedRequest.getMethod(), is("GET"));
126-
assertThat(recordedRequest.getPath(), startsWith(BLOCK));
126+
assertPath(recordedRequest, BLOCK);
127127
assertThat(recordedRequest.getRequestUrl().queryParameter("height"),
128128
is(String.valueOf(height)));
129129
}
@@ -160,7 +160,7 @@ void getBlocksSinglePageSkippingEmpty() throws InterruptedException {
160160
// Assert request params
161161
RecordedRequest recordedRequest = server.takeRequest();
162162
assertThat(recordedRequest.getMethod(), is("GET"));
163-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
163+
assertPath(recordedRequest, BLOCKS);
164164
int expectedNumBlocks = Math.toIntExact(toHeight - fromHeight + 1);
165165
assertBlockRequestParams(recordedRequest, expectedNumBlocks, blockFilter, toHeight, timeOption);
166166
}
@@ -197,7 +197,7 @@ void getBlocksSinglePageSkippingEmptyFiltersOutOfRangeBlocks() throws Interrupte
197197
// Assert request params
198198
RecordedRequest recordedRequest = server.takeRequest();
199199
assertThat(recordedRequest.getMethod(), is("GET"));
200-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
200+
assertPath(recordedRequest, BLOCKS);
201201
int expectedNumBlocks = Math.toIntExact(toHeight - fromHeight + 1);
202202
assertBlockRequestParams(recordedRequest, expectedNumBlocks, blockFilter, toHeight, timeOption);
203203
}
@@ -228,7 +228,7 @@ void getBlocksSinglePageNoTime() throws InterruptedException {
228228
// Assert request params
229229
RecordedRequest recordedRequest = server.takeRequest();
230230
assertThat(recordedRequest.getMethod(), is("GET"));
231-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
231+
assertPath(recordedRequest, BLOCKS);
232232
int expectedNumBlocks = Math.toIntExact(toHeight - fromHeight + 1);
233233
assertBlockRequestParams(recordedRequest, expectedNumBlocks, blockFilter, toHeight, timeOption);
234234
}
@@ -400,7 +400,7 @@ void getLastBlocksSkippingEmptySinglePage() throws InterruptedException {
400400
// Assert request params
401401
RecordedRequest recordedRequest = server.takeRequest();
402402
assertThat(recordedRequest.getMethod(), is("GET"));
403-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
403+
assertPath(recordedRequest, BLOCKS);
404404
assertBlockRequestParams(recordedRequest, numBlocks, blockFilter, null, timeOption);
405405
}
406406

@@ -437,7 +437,7 @@ void getLastBlocksSkippingEmptyMoreThanCommitted(int overflow)
437437
// Assert request params
438438
RecordedRequest recordedRequest = server.takeRequest();
439439
assertThat(recordedRequest.getMethod(), is("GET"));
440-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
440+
assertPath(recordedRequest, BLOCKS);
441441
int expectedFirstRequestSize = min(blocksCount, MAX_BLOCKS_PER_REQUEST);
442442
assertBlockRequestParams(recordedRequest, expectedFirstRequestSize, blockFilter, null,
443443
timeOption);
@@ -659,7 +659,7 @@ void getLastBlock() throws InterruptedException {
659659
// Assert request params
660660
RecordedRequest recordedRequest = server.takeRequest();
661661
assertThat(recordedRequest.getMethod(), is("GET"));
662-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
662+
assertPath(recordedRequest, BLOCKS);
663663
assertBlockRequestParams(recordedRequest, 1, INCLUDE_EMPTY, null, INCLUDE_COMMIT_TIME);
664664
}
665665

@@ -685,7 +685,7 @@ void getLastNotEmptyBlock() throws InterruptedException {
685685
// Assert request params
686686
RecordedRequest recordedRequest = server.takeRequest();
687687
assertThat(recordedRequest.getMethod(), is("GET"));
688-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
688+
assertPath(recordedRequest, BLOCKS);
689689
assertBlockRequestParams(recordedRequest, 1, SKIP_EMPTY, null, INCLUDE_COMMIT_TIME);
690690
}
691691

@@ -710,7 +710,7 @@ void getLastNotEmptyBlockNoBlock() throws InterruptedException {
710710
// Assert request params
711711
RecordedRequest recordedRequest = server.takeRequest();
712712
assertThat(recordedRequest.getMethod(), is("GET"));
713-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
713+
assertPath(recordedRequest, BLOCKS);
714714
assertBlockRequestParams(recordedRequest, 1, SKIP_EMPTY, null, INCLUDE_COMMIT_TIME);
715715
}
716716

@@ -738,7 +738,7 @@ void getBlockchainHeight() throws InterruptedException {
738738
// Assert request params
739739
RecordedRequest recordedRequest = server.takeRequest();
740740
assertThat(recordedRequest.getMethod(), is("GET"));
741-
assertThat(recordedRequest.getPath(), startsWith(BLOCKS));
741+
assertPath(recordedRequest, BLOCKS);
742742
assertBlockRequestParams(recordedRequest, 0, INCLUDE_EMPTY, null, NO_COMMIT_TIME);
743743
}
744744

0 commit comments

Comments
 (0)