11package com .github .scribejava .httpclient .armeria ;
22
3+ import static java .util .Objects .requireNonNull ;
4+
35import com .github .scribejava .core .httpclient .AbstractAsyncOnlyHttpClient ;
4- import com .github .scribejava .core .httpclient .HttpClientConfig ;
56import com .github .scribejava .core .httpclient .multipart .MultipartPayload ;
67import com .github .scribejava .core .httpclient .multipart .MultipartUtils ;
78import com .github .scribejava .core .model .OAuthAsyncRequestCallback ;
89import com .github .scribejava .core .model .OAuthConstants ;
910import com .github .scribejava .core .model .OAuthRequest ;
1011import com .github .scribejava .core .model .Response ;
1112import com .github .scribejava .core .model .Verb ;
12- import com .linecorp .armeria .client .ClientFactory ;
13- import com .linecorp .armeria .client .ClientFactoryBuilder ;
14- import com .linecorp .armeria .client .ClientFactoryOptionValue ;
15- import com .linecorp .armeria .client .ClientOptions ;
16- import com .linecorp .armeria .client .Endpoint ;
1713import com .linecorp .armeria .client .WebClient ;
18- import com .linecorp .armeria .client .WebClientBuilder ;
19- import com .linecorp .armeria .client .logging .LoggingClient ;
2014import com .linecorp .armeria .common .AggregatedHttpResponse ;
2115import com .linecorp .armeria .common .HttpData ;
2216import com .linecorp .armeria .common .HttpMethod ;
2317import com .linecorp .armeria .common .HttpResponse ;
2418import com .linecorp .armeria .common .RequestHeaders ;
2519import com .linecorp .armeria .common .RequestHeadersBuilder ;
26- import com .linecorp .armeria .common .SessionProtocol ;
2720
2821import java .io .File ;
2922import java .io .IOException ;
3023import java .net .URI ;
3124import java .nio .file .Files ;
32- import java .util .Collection ;
3325import java .util .HashMap ;
3426import java .util .Map ;
3527import java .util .concurrent .CompletableFuture ;
3830import java .util .concurrent .locks .ReentrantReadWriteLock ;
3931import java .util .function .Supplier ;
4032
33+ /**
34+ * An implementation of {@link AbstractAsyncOnlyHttpClient} based on
35+ * <a href="https://line.github.io/armeria/">Armeria HTTP client</a>.
36+ */
4137public class ArmeriaHttpClient extends AbstractAsyncOnlyHttpClient {
4238
43- private final ArmeriaHttpClientConfig config ;
39+ /**
40+ * A builder of new instances of Armeria's {@link WebClient}
41+ */
42+ private final ArmeriaWebClientBuilder clientBuilder ;
43+ /**
44+ * A list of cached Endpoints. It helps avoiding building a new Endpoint per each request.
45+ */
4446 private final Map <String , WebClient > httpClients = new HashMap <>();
47+ /**
48+ * A read/write lock to access the list of cached Endpoints concurrently.
49+ */
4550 private final ReentrantReadWriteLock httpClientsLock = new ReentrantReadWriteLock ();
4651
4752 public ArmeriaHttpClient () {
4853 this (ArmeriaHttpClientConfig .defaultConfig ());
4954 }
5055
5156 public ArmeriaHttpClient (ArmeriaHttpClientConfig config ) {
52- this .config = config ;
53- }
54-
55- public HttpClientConfig getConfig () {
56- return config ;
57+ this .clientBuilder = config .builder ();
5758 }
5859
5960 /**
60- * Cleans up HTTP clients collection .
61+ * Cleans up the list of cached Endpoints .
6162 */
6263 @ Override
6364 public void close () {
@@ -112,7 +113,7 @@ private <T> CompletableFuture<T> doExecuteAsync(String userAgent,
112113 final String path = getServicePath (uri );
113114
114115 // Fetch/Create WebClient instance for a given Endpoint
115- final WebClient client = getHttpClient (uri );
116+ final WebClient client = getClient (uri );
116117
117118 // Build HTTP request
118119 final RequestHeadersBuilder headersBuilder =
@@ -148,55 +149,19 @@ private <T> CompletableFuture<T> doExecuteAsync(String userAgent,
148149 //------------------------------------------------------------------------------------------------
149150
150151 /**
151- * Extracts Protocol + Host + Port part from a given endpoint {@link URI}
152- * @param uri an endpoint {@link URI}
153- * @return a portion of {@link URI} including a combination of Protocol + Host + Port
154- * @see #getHttpClient(URI uri)
155- */
156- private String getHostUri (final URI uri ) {
157- final StringBuilder builder = new StringBuilder ();
158- builder .append (uri .getScheme ()).append ("://" ).append (uri .getHost ());
159- final int port = uri .getPort ();
160- if (port > 0 ) {
161- builder .append (":" ).append (port );
162- }
163- return builder .toString ();
164- }
165-
166- /**
167- * Extracts service path from a given endpoint {@link URI}
168- * @param uri an endpoint {@link URI}
169- * @return service path portion of the {@link URI}
170- */
171- private static String getServicePath (final URI uri ) {
172- final StringBuilder builder = new StringBuilder ();
173- final String path = uri .getRawPath ();
174- if (path != null && path .length () > 0 ) {
175- builder .append (path );
176- } else {
177- builder .append ("/" );
178- }
179- final String query = uri .getRawQuery ();
180- if (query != null && query .length () > 0 ) {
181- builder .append ("?" ).append (query );
182- }
183- return builder .toString ();
184- }
185-
186- /**
187- * Provides and instance of {@link WebClient} for a given endpoint {@link URI} based on
188- * a combination of Protocol + Host + Port.
152+ * Provides an instance of {@link WebClient} for a given endpoint {@link URI} based on an endpoint
153+ * as {@code scheme://authority}.
189154 * @param uri an endpoint {@link URI}
190155 * @return {@link WebClient} instance
191156 */
192- private WebClient getHttpClient (final URI uri ) {
193- final String hostUri = getHostUri (uri );
157+ private WebClient getClient (final URI uri ) {
158+ final String endpoint = getEndPoint (uri );
194159
195160 WebClient client ;
196161 final Lock readLock = httpClientsLock .readLock ();
197162 readLock .lock ();
198163 try {
199- client = httpClients .get (hostUri );
164+ client = httpClients .get (endpoint );
200165 } finally {
201166 readLock .unlock ();
202167 }
@@ -205,88 +170,62 @@ private WebClient getHttpClient(final URI uri) {
205170 return client ;
206171 }
207172
208- client = buildNewHttpClient (uri );
173+ client = clientBuilder .newWebClient (
174+ requireNonNull (uri .getScheme (), "scheme" ),
175+ requireNonNull (uri .getAuthority (), "authority" ));
209176
210177 final Lock writeLock = httpClientsLock .writeLock ();
211178 writeLock .lock ();
212179 try {
213- if (!httpClients .containsKey (hostUri )) {
214- httpClients .put (hostUri , client );
180+ if (!httpClients .containsKey (endpoint )) {
181+ httpClients .put (endpoint , client );
215182 return client ;
216183 } else {
217- return httpClients .get (hostUri );
184+ return httpClients .get (endpoint );
218185 }
219186 } finally {
220187 writeLock .unlock ();
221188 }
222189 }
223190
224191 /**
225- * Builds a brand-new instance of {@link WebClient} for a given endpoint {@link URI}.
226- * Uses {@link ArmeriaHttpClientConfig} to configure the builder and the client.
227- * @param uri an endpoint {@link URI}
228- * @return Brand-new {@link WebClient} instance
192+ * Extracts {@code scheme} and {@code authority} portion of the {@link URI}.
193+ * Assuming the {@link URI} as the following:
194+ * {@code URI = scheme:[//authority]path[?query][#fragment]}
229195 */
230- private WebClient buildNewHttpClient (final URI uri ) {
231- // Build the client and the headers
232- WebClientBuilder clientBuilder =
233- getHttpClientBuilder (uri , config .getSessionProtocolPreference ());
234-
235- final ClientOptions clientOptions = config .getClientOptions ();
236- if (clientOptions != null ) {
237- clientBuilder .options (clientOptions );
238- }
239-
240- final ClientFactoryBuilder clientFactoryBuilder = ClientFactory .builder ();
241- final Collection <ClientFactoryOptionValue <?>> clientFactoryOptions = config .getClientFactoryOptions ();
242- if (clientFactoryOptions != null && !clientFactoryOptions .isEmpty ()) {
243- clientFactoryOptions .forEach (clientFactoryBuilder ::option );
244- }
245- clientBuilder .factory (clientFactoryBuilder .build ());
246-
247- if (config .isLogging ()) {
248- clientBuilder = clientBuilder .decorator (LoggingClient .newDecorator ());
249- }
250-
251- return clientBuilder .build ();
196+ @ SuppressWarnings ("StringBufferReplaceableByString" )
197+ private static String getEndPoint (final URI uri ) {
198+ final StringBuilder builder = new StringBuilder ()
199+ .append (requireNonNull (uri .getScheme (), "scheme" ))
200+ .append ("://" ).append (requireNonNull (uri .getAuthority (), "authority" ));
201+ return builder .toString ();
252202 }
253203
254204 /**
255- * Provides an instance of {@link WebClientBuilder} for a given endpoint {@link URI}
256- * @param uri an endpoint {@link URI}
257- * @param protocolPreference an HTTP protocol generation preference; acceptable values: "h1" or "h2".
258- * @return {@link WebClientBuilder} instance to build a new {@link WebClient}
205+ * Extracts {@code path}, {@code query) and {@code fragment} portion of the {@link URI}.
206+ * Assuming the {@link URI} as the following:
207+ * {@code URI = scheme:[//authority]path[?query][#fragment]}
259208 */
260- private WebClientBuilder getHttpClientBuilder (final URI uri , final SessionProtocol protocolPreference ) {
261- final SessionProtocol sessionProtocol = SessionProtocol .of (uri .getScheme ());
262- final String host = uri .getHost ();
263- final int port = uri .getPort ();
264- final Endpoint endpoint = (port > 0 ) ? Endpoint .of (host , port ) : Endpoint .of (host );
265- switch (sessionProtocol ) {
266- case HTTP :
267- if (protocolPreference == SessionProtocol .H1 ) {
268- // enforce HTTP/1 protocol
269- return WebClient .builder (SessionProtocol .H1C , endpoint );
270- }
271- break ;
272- case HTTPS :
273- if (protocolPreference == SessionProtocol .H1 ) {
274- // enforce HTTP/1 protocol
275- return WebClient .builder (SessionProtocol .H1 , endpoint );
276- }
277- break ;
278- default :
279- break ;
209+ private static String getServicePath (final URI uri ) {
210+ final StringBuilder builder = new StringBuilder ()
211+ .append (requireNonNull (uri .getPath (), "path" ));
212+ final String query = uri .getQuery ();
213+ if (query != null ) {
214+ builder .append ('?' ).append (query );
280215 }
281- return WebClient .builder (sessionProtocol , endpoint );
216+ final String fragment = uri .getFragment ();
217+ if (fragment != null ) {
218+ builder .append ('#' ).append (fragment );
219+ }
220+ return builder .toString ();
282221 }
283222
284223 /**
285224 * Maps {@link Verb} to {@link HttpMethod}
286225 * @param httpVerb a {@link Verb} to match with {@link HttpMethod}
287226 * @return {@link HttpMethod} corresponding to the parameter
288227 */
289- private HttpMethod getHttpMethod (final Verb httpVerb ) {
228+ private static HttpMethod getHttpMethod (final Verb httpVerb ) {
290229 switch (httpVerb ) {
291230 case GET :
292231 return HttpMethod .GET ;
0 commit comments