44import java .io .Closeable ;
55import java .io .IOException ;
66import java .net .SocketTimeoutException ;
7+ import java .net .URI ;
8+ import java .net .URISyntaxException ;
9+ import java .time .temporal .ChronoUnit ;
710import java .util .HashSet ;
11+ import java .util .Map ;
812import java .util .Set ;
9- import java .util .concurrent .TimeUnit ;
1013import lombok .Getter ;
1114import lombok .Setter ;
12- import okhttp3 .*;
13- import okhttp3 .Response ;
14- import org .stellar .sdk .exception .AccountRequiresMemoException ;
15- import org .stellar .sdk .exception .BadRequestException ;
16- import org .stellar .sdk .exception .ConnectionErrorException ;
17- import org .stellar .sdk .exception .RequestTimeoutException ;
18- import org .stellar .sdk .exception .TooManyRequestsException ;
19- import org .stellar .sdk .operations .AccountMergeOperation ;
20- import org .stellar .sdk .operations .Operation ;
21- import org .stellar .sdk .operations .PathPaymentStrictReceiveOperation ;
22- import org .stellar .sdk .operations .PathPaymentStrictSendOperation ;
23- import org .stellar .sdk .operations .PaymentOperation ;
15+ import org .stellar .sdk .exception .*;
16+ import org .stellar .sdk .http .IHttpClient ;
17+ import org .stellar .sdk .http .Jdk11HttpClient ;
18+ import org .stellar .sdk .http .PostRequest ;
19+ import org .stellar .sdk .http .StringResponse ;
20+ import org .stellar .sdk .http .sse .ISseClient ;
21+ import org .stellar .sdk .operations .*;
2422import org .stellar .sdk .requests .*;
25- import org .stellar .sdk .responses .*;
23+ import org .stellar .sdk .responses .AccountResponse ;
24+ import org .stellar .sdk .responses .FeeStatsResponse ;
25+ import org .stellar .sdk .responses .SubmitTransactionAsyncResponse ;
26+ import org .stellar .sdk .responses .TransactionResponse ;
2627import org .stellar .sdk .xdr .CryptoKeyType ;
2728
2829/** Main class used to connect to Horizon server. */
2930public class Server implements Closeable {
30- private final HttpUrl serverURI ;
31- @ Getter @ Setter private OkHttpClient httpClient ;
31+ private final URI serverURI ;
32+ @ Getter @ Setter private IHttpClient httpClient ;
3233
3334 /** submitHttpClient is used only for submitting transactions. The read timeout is longer. */
34- @ Getter @ Setter private OkHttpClient submitHttpClient ;
35+ @ Getter @ Setter private IHttpClient submitHttpClient ;
36+
37+ @ Getter @ Setter private ISseClient sseClient ;
3538
3639 /**
3740 * HORIZON_SUBMIT_TIMEOUT is a time in seconds after Horizon sends a timeout response after
@@ -54,31 +57,43 @@ public class Server implements Closeable {
5457 * @param uri The URI of the Horizon server.
5558 */
5659 public Server (String uri ) {
57- this (
58- uri ,
59- new OkHttpClient .Builder ()
60- .addInterceptor (new ClientIdentificationInterceptor ())
61- .connectTimeout (10 , TimeUnit .SECONDS )
62- .readTimeout (30 , TimeUnit .SECONDS )
63- .retryOnConnectionFailure (true )
64- .build (),
65- new OkHttpClient .Builder ()
66- .addInterceptor (new ClientIdentificationInterceptor ())
67- .connectTimeout (10 , TimeUnit .SECONDS )
68- .readTimeout (HORIZON_SUBMIT_TIMEOUT + 5 , TimeUnit .SECONDS )
69- .retryOnConnectionFailure (true )
70- .build ());
60+ this (uri , normalHttpClient (), submitHttpClient ());
61+ }
62+
63+ private static IHttpClient normalHttpClient () {
64+ return new Jdk11HttpClient .Builder ()
65+ .withDefaultHeader ("X-Client-Name" , "java-stellar-sdk" )
66+ .withDefaultHeader ("X-Client-Version" , Util .getSdkVersion ())
67+ .withConnectTimeout (10 , ChronoUnit .SECONDS )
68+ .withReadTimeout (30 , ChronoUnit .SECONDS )
69+ .withRetryOnConnectionFailure (true )
70+ .build ();
71+ }
72+
73+ private static IHttpClient submitHttpClient () {
74+ return new Jdk11HttpClient .Builder ()
75+ .withDefaultHeader ("X-Client-Name" , "java-stellar-sdk" )
76+ .withDefaultHeader ("X-Client-Version" , Util .getSdkVersion ())
77+ .withConnectTimeout (10 , ChronoUnit .SECONDS )
78+ .withReadTimeout (HORIZON_SUBMIT_TIMEOUT + 5 , ChronoUnit .SECONDS )
79+ .withRetryOnConnectionFailure (true )
80+ .build ();
7181 }
7282
7383 /**
7484 * Constructs a new Server object with custom HTTP clients.
7585 *
7686 * @param serverURI The URI of the Horizon server.
77- * @param httpClient The OkHttpClient to use for general requests.
78- * @param submitHttpClient The OkHttpClient to use for submitting transactions.
87+ * @param httpClient The IHttpClient to use for general requests.
88+ * @param submitHttpClient The IHttpClient to use for submitting transactions.
7989 */
80- public Server (String serverURI , OkHttpClient httpClient , OkHttpClient submitHttpClient ) {
81- this .serverURI = HttpUrl .parse (serverURI );
90+ public Server (String serverURI , IHttpClient httpClient , IHttpClient submitHttpClient ) {
91+ try {
92+ this .serverURI = new URI (serverURI );
93+ } catch (URISyntaxException e ) {
94+ throw new RuntimeException ("Invalid URI: " + serverURI );
95+ }
96+
8297 this .httpClient = httpClient ;
8398 this .submitHttpClient = submitHttpClient ;
8499 }
@@ -114,77 +129,77 @@ public TransactionBuilderAccount loadAccount(String address) {
114129 * @return {@link RootRequestBuilder} instance.
115130 */
116131 public RootRequestBuilder root () {
117- return new RootRequestBuilder (httpClient , serverURI );
132+ return new RootRequestBuilder (httpClient , sseClient , serverURI );
118133 }
119134
120135 /**
121136 * @return {@link AccountsRequestBuilder} instance.
122137 */
123138 public AccountsRequestBuilder accounts () {
124- return new AccountsRequestBuilder (httpClient , serverURI );
139+ return new AccountsRequestBuilder (httpClient , sseClient , serverURI );
125140 }
126141
127142 /**
128143 * @return {@link AssetsRequestBuilder} instance.
129144 */
130145 public AssetsRequestBuilder assets () {
131- return new AssetsRequestBuilder (httpClient , serverURI );
146+ return new AssetsRequestBuilder (httpClient , sseClient , serverURI );
132147 }
133148
134149 /**
135150 * @return {@link ClaimableBalancesRequestBuilder} instance.
136151 */
137152 public ClaimableBalancesRequestBuilder claimableBalances () {
138- return new ClaimableBalancesRequestBuilder (httpClient , serverURI );
153+ return new ClaimableBalancesRequestBuilder (httpClient , sseClient , serverURI );
139154 }
140155
141156 /**
142157 * @return {@link EffectsRequestBuilder} instance.
143158 */
144159 public EffectsRequestBuilder effects () {
145- return new EffectsRequestBuilder (httpClient , serverURI );
160+ return new EffectsRequestBuilder (httpClient , sseClient , serverURI );
146161 }
147162
148163 /**
149164 * @return {@link LedgersRequestBuilder} instance.
150165 */
151166 public LedgersRequestBuilder ledgers () {
152- return new LedgersRequestBuilder (httpClient , serverURI );
167+ return new LedgersRequestBuilder (httpClient , sseClient , serverURI );
153168 }
154169
155170 /**
156171 * @return {@link OffersRequestBuilder} instance.
157172 */
158173 public OffersRequestBuilder offers () {
159- return new OffersRequestBuilder (httpClient , serverURI );
174+ return new OffersRequestBuilder (httpClient , sseClient , serverURI );
160175 }
161176
162177 /**
163178 * @return {@link OperationsRequestBuilder} instance.
164179 */
165180 public OperationsRequestBuilder operations () {
166- return new OperationsRequestBuilder (httpClient , serverURI );
181+ return new OperationsRequestBuilder (httpClient , sseClient , serverURI );
167182 }
168183
169184 /**
170185 * @return {@link FeeStatsResponse} instance.
171186 */
172187 public FeeStatsRequestBuilder feeStats () {
173- return new FeeStatsRequestBuilder (httpClient , serverURI );
188+ return new FeeStatsRequestBuilder (httpClient , sseClient , serverURI );
174189 }
175190
176191 /**
177192 * @return {@link OrderBookRequestBuilder} instance.
178193 */
179194 public OrderBookRequestBuilder orderBook () {
180- return new OrderBookRequestBuilder (httpClient , serverURI );
195+ return new OrderBookRequestBuilder (httpClient , sseClient , serverURI );
181196 }
182197
183198 /**
184199 * @return {@link TradesRequestBuilder} instance.
185200 */
186201 public TradesRequestBuilder trades () {
187- return new TradesRequestBuilder (httpClient , serverURI );
202+ return new TradesRequestBuilder (httpClient , sseClient , serverURI );
188203 }
189204
190205 /**
@@ -198,42 +213,50 @@ public TradeAggregationsRequestBuilder tradeAggregations(
198213 long resolution ,
199214 long offset ) {
200215 return new TradeAggregationsRequestBuilder (
201- httpClient , serverURI , baseAsset , counterAsset , startTime , endTime , resolution , offset );
216+ httpClient ,
217+ sseClient ,
218+ serverURI ,
219+ baseAsset ,
220+ counterAsset ,
221+ startTime ,
222+ endTime ,
223+ resolution ,
224+ offset );
202225 }
203226
204227 /**
205228 * @return {@link StrictReceivePathsRequestBuilder} instance.
206229 */
207230 public StrictReceivePathsRequestBuilder strictReceivePaths () {
208- return new StrictReceivePathsRequestBuilder (httpClient , serverURI );
231+ return new StrictReceivePathsRequestBuilder (httpClient , sseClient , serverURI );
209232 }
210233
211234 /**
212235 * @return {@link StrictSendPathsRequestBuilder} instance.
213236 */
214237 public StrictSendPathsRequestBuilder strictSendPaths () {
215- return new StrictSendPathsRequestBuilder (httpClient , serverURI );
238+ return new StrictSendPathsRequestBuilder (httpClient , sseClient , serverURI );
216239 }
217240
218241 /**
219242 * @return {@link PaymentsRequestBuilder} instance.
220243 */
221244 public PaymentsRequestBuilder payments () {
222- return new PaymentsRequestBuilder (httpClient , serverURI );
245+ return new PaymentsRequestBuilder (httpClient , sseClient , serverURI );
223246 }
224247
225248 /**
226249 * @return {@link TransactionsRequestBuilder} instance.
227250 */
228251 public TransactionsRequestBuilder transactions () {
229- return new TransactionsRequestBuilder (httpClient , serverURI );
252+ return new TransactionsRequestBuilder (httpClient , sseClient , serverURI );
230253 }
231254
232255 /**
233256 * @return {@link LiquidityPoolsRequestBuilder} instance.
234257 */
235258 public LiquidityPoolsRequestBuilder liquidityPools () {
236- return new LiquidityPoolsRequestBuilder (httpClient , serverURI );
259+ return new LiquidityPoolsRequestBuilder (httpClient , sseClient , serverURI );
237260 }
238261
239262 /**
@@ -259,20 +282,22 @@ public LiquidityPoolsRequestBuilder liquidityPools() {
259282 * due to cancellation or connectivity problems, etc.
260283 */
261284 public TransactionResponse submitTransactionXdr (String transactionXdr ) {
262- HttpUrl transactionsURI = serverURI .newBuilder ().addPathSegment ("transactions" ).build ();
263- RequestBody requestBody = new FormBody .Builder ().add ("tx" , transactionXdr ).build ();
264- Request submitTransactionRequest =
265- new Request .Builder ().url (transactionsURI ).post (requestBody ).build ();
285+ final var transactionsURI = new UriBuilder (serverURI ).addPathSegment ("transactions" ).build ();
286+ final var form = Map .of ("tx" , transactionXdr );
287+ final var post = PostRequest .formBody (transactionsURI , form );
266288 TypeToken <TransactionResponse > type = new TypeToken <TransactionResponse >() {};
267-
268289 ResponseHandler <TransactionResponse > responseHandler = new ResponseHandler <>(type );
269- Response response ;
290+ StringResponse response ;
270291 try {
271- response = this .submitHttpClient .newCall ( submitTransactionRequest ). execute ( );
292+ response = this .submitHttpClient .post ( post );
272293 } catch (SocketTimeoutException e ) {
273294 throw new RequestTimeoutException (e );
274295 } catch (IOException e ) {
275- throw new ConnectionErrorException (e );
296+ if (e .getMessage ().contains ("request timed out" )) {
297+ throw new RequestTimeoutException (e );
298+ } else {
299+ throw new ConnectionErrorException (e );
300+ }
276301 }
277302 return responseHandler .handleResponse (response );
278303 }
@@ -431,17 +456,17 @@ public TransactionResponse submitTransaction(FeeBumpTransaction transaction) {
431456 * a Transaction Asynchronously</a>
432457 */
433458 public SubmitTransactionAsyncResponse submitTransactionXdrAsync (String transactionXdr ) {
434- HttpUrl transactionsURI = serverURI . newBuilder (). addPathSegment ( "transactions_async" ). build ();
435- RequestBody requestBody = new FormBody . Builder (). add ( "tx" , transactionXdr ).build ();
436- Request submitTransactionRequest =
437- new Request . Builder (). url ( transactionsURI ). post ( requestBody ). build ( );
459+ final var transactionsURI =
460+ new UriBuilder ( serverURI ). addPathSegment ( "transactions_async" ).build ();
461+ final var form = Map . of ( "tx" , transactionXdr );
462+ final var post = PostRequest . formBody ( transactionsURI , form );
438463 TypeToken <SubmitTransactionAsyncResponse > type =
439464 new TypeToken <SubmitTransactionAsyncResponse >() {};
440465
441466 ResponseHandler <SubmitTransactionAsyncResponse > responseHandler = new ResponseHandler <>(type );
442- Response response ;
467+ StringResponse response ;
443468 try {
444- response = this .submitHttpClient .newCall ( submitTransactionRequest ). execute ( );
469+ response = this .submitHttpClient .post ( post );
445470 } catch (SocketTimeoutException e ) {
446471 throw new RequestTimeoutException (e );
447472 } catch (IOException e ) {
@@ -653,9 +678,16 @@ private void checkMemoRequired(Transaction transaction) {
653678
654679 @ Override
655680 public void close () {
656- // workaround for https://github.com/square/okhttp/issues/3372
657- // sometimes, the connection pool keeps running and this can prevent a clean shut down.
658- this .httpClient .connectionPool ().evictAll ();
659- this .submitHttpClient .connectionPool ().evictAll ();
681+ try {
682+ this .httpClient .close ();
683+ } catch (Exception e ) {
684+ e .printStackTrace ();
685+ }
686+
687+ try {
688+ this .submitHttpClient .close ();
689+ } catch (Exception e ) {
690+ e .printStackTrace ();
691+ }
660692 }
661693}
0 commit comments