package glaze.client.async; import glaze.GlazeException; import glaze.client.BaseClient; import glaze.client.Client; import glaze.client.Response; import glaze.client.handlers.ErrorHandler; import glaze.client.interceptors.PreemptiveAuthorizer; import glaze.spi.Registry; import glaze.util.RequestUtil; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.Future; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponseInterceptor; import org.apache.http.auth.AuthSchemeFactory; import org.apache.http.auth.params.AuthPNames; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.utils.URIUtils; import org.apache.http.concurrent.FutureCallback; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.impl.nio.client.AbstractHttpAsyncClient; import org.apache.http.impl.nio.client.DefaultHttpAsyncClient; import org.apache.http.nio.client.HttpAsyncClient; import org.apache.http.nio.conn.scheme.AsyncScheme; import org.apache.http.nio.conn.ssl.SSLLayeringStrategy; import org.apache.http.nio.entity.EntityAsyncContentProducer; import org.apache.http.nio.entity.HttpAsyncContentProducer; import org.apache.http.nio.protocol.BasicAsyncRequestProducer; import org.apache.http.nio.protocol.HttpAsyncRequestProducer; import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; import org.apache.http.nio.reactor.IOReactorException; import org.apache.http.nio.reactor.IOReactorStatus; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParamBean; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.apache.http.util.VersionInfo; import com.google.common.base.Preconditions; /** * Default implementation of {@link AsyncClient}. * */ // TODO migrate to the new building pattern HttpAsyncClients.custom(), // RequestConfig, etc. // Nowadays in Beta, wait until an stable release is delivered public class DefaultAsyncClient extends BaseClient implements AsyncClient { static class RequestProducerImpl extends BasicAsyncRequestProducer { public RequestProducerImpl(HttpHost target, HttpRequest request) { super(target, request); } protected RequestProducerImpl(HttpHost target, HttpEntityEnclosingRequest request, HttpAsyncContentProducer producer) { super(target, request, producer); } } private static HttpAsyncClient createDefaultHttpClient() { HttpAsyncClient httpClient; if (Registry.isRegitered(HttpAsyncClient.class)) { httpClient = Registry.lookup(HttpAsyncClient.class); } else { try { // Defaults to PoolingClientAsyncConnectionManager DefaultHttpAsyncClient hc = new DefaultHttpAsyncClient(); HttpParams params = hc.getParams(); HttpProtocolParamBean protocolBean = new HttpProtocolParamBean(params); VersionInfo versionInfo = VersionInfo.loadVersionInfo("glaze", DefaultAsyncClient.class.getClassLoader()); protocolBean.setUserAgent(String.format("Glaze-AsyncHttpClient/%s", versionInfo.getRelease())); httpClient = hc; } catch (IOReactorException e) { throw new GlazeException(e); } } return httpClient; } private HttpAsyncClient httpClient; public DefaultAsyncClient() { this(createDefaultHttpClient()); } public DefaultAsyncClient(HttpAsyncClient httpClient) { super(); this.httpClient = httpClient; } public Client authPreemptive(String schemeName) { DefaultHttpAsyncClient httpClient = (DefaultHttpAsyncClient) getHttpClient(); httpClient.addRequestInterceptor(new PreemptiveAuthorizer(schemeName), 0); return this; } @Override public HttpAsyncRequestProducer createAsyncProducer(HttpUriRequest request) { HttpAsyncRequestProducer producer; HttpHost target = URIUtils.extractHost(request.getURI()); if (RequestUtil.isEnclosingEntity(request)) { producer = createRequestProducer(request, target); } else { producer = new RequestProducerImpl(target, request); } return producer; } @Override public <T> Future<T> execute(HttpAsyncRequestProducer producer, HttpAsyncResponseConsumer<T> consumer) { return execute(producer, consumer, null); } @Override public <T> Future<T> execute(HttpAsyncRequestProducer producer, HttpAsyncResponseConsumer<T> consumer, FutureCallback<T> futureCallback) { return execute(producer, consumer, prepareLocalContext(), futureCallback); } @Override public <T> Future<T> execute(HttpAsyncRequestProducer producer, HttpAsyncResponseConsumer<T> consumer, HttpContext context, FutureCallback<T> futureCallback) { return activateIfNeeded().execute(producer, consumer, context, futureCallback); } @Override public Future<Response> execute(HttpUriRequest request, ErrorHandler errorHandler) { return execute(request, null, errorHandler); } @Override public Future<Response> execute(HttpUriRequest request, FutureCallback<Response> futureCallback, ErrorHandler errorHandler) { return execute(request, prepareLocalContext(), futureCallback, errorHandler); } @Override public Future<Response> execute(HttpUriRequest request, HttpContext context, FutureCallback<Response> futureCallback, ErrorHandler errorHandler) { return activateIfNeeded().execute(createAsyncProducer(request), new ResponseConsumer(errorHandler), context, futureCallback); } public HttpAsyncClient getHttpClient() { return this.httpClient; } @Override public Client interceptRequest(HttpRequestInterceptor interceptor) { ((AbstractHttpAsyncClient) getHttpClient()).addRequestInterceptor(interceptor); return this; } @Override public Client interceptRequest(HttpRequestInterceptor interceptor, int position) { ((AbstractHttpAsyncClient) getHttpClient()).addRequestInterceptor(interceptor, position); return this; } @Override public Client interceptResponse(HttpResponseInterceptor interceptor) { ((AbstractHttpAsyncClient) getHttpClient()).addResponseInterceptor(interceptor); return this; } @Override public Client interceptResponse(HttpResponseInterceptor interceptor, int position) { ((AbstractHttpAsyncClient) getHttpClient()).addResponseInterceptor(interceptor, position); return this; } @Override public <T> Future<T> map(AsyncMap<T> mapRequest) { HttpContext context = mapRequest.hasContext() ? mapRequest.getContext() : prepareLocalContext(); return activateIfNeeded().execute(createAsyncProducer(mapRequest.getRequest()), mapRequest.getConsumer(), context, mapRequest.getFutureCallback()); } public void proxyAuthPref(String... authpref) { Preconditions.checkNotNull(authpref, "Please, specify a valid auth policy chain."); httpClient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, Arrays.asList(authpref)); } @Override public void registerAuthScheme(String schemeName, AuthSchemeFactory schemeFactory) { ((AbstractHttpAsyncClient) httpClient).getAuthSchemes().register(schemeName, schemeFactory); } public void registerScheme(final AsyncScheme scheme) { httpClient.getConnectionManager().getSchemeRegistry().register(scheme); } public AsyncClient reset() { shutdown(); httpClient = createDefaultHttpClient(); return this; } public void shutdown() { try { httpClient.shutdown(); } catch (InterruptedException e) { // } } public void trustSelfSignedCertificates() { try { SSLLayeringStrategy sslls = new SSLLayeringStrategy(new TrustSelfSignedStrategy(), SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); registerScheme(new AsyncScheme("https", 443, sslls)); } catch (Exception e) { throw new GlazeException(e); } } public void unregisterScheme(final String name) { httpClient.getConnectionManager().getSchemeRegistry().unregister(name); } @Override protected BasicHttpContext prepareLocalContext() { BasicHttpContext ctx = super.prepareLocalContext(); // XXX check this ctx.removeAttribute(ClientContext.AUTH_CACHE); return ctx; } private HttpAsyncClient activateIfNeeded() { IOReactorStatus status = httpClient.getStatus(); if (IOReactorStatus.INACTIVE.equals(status)) { httpClient.start(); } return httpClient; } private HttpAsyncRequestProducer createRequestProducer(HttpUriRequest request, HttpHost target) { HttpAsyncRequestProducer producer; HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request; HttpEntity entity = entityRequest.getEntity(); if (HttpAsyncContentProducer.class.isAssignableFrom(entity.getClass())) { producer = new RequestProducerImpl(target, entityRequest, (HttpAsyncContentProducer) entity); } else if (MultipartEntity.class.isAssignableFrom(entity.getClass())) { // TODO investigate zero copy multipart (not zero copy raw file // transfer...) try { producer = new RequestProducerImpl(target, entityRequest, new EntityAsyncContentProducer(new BufferedMultipartEntity((MultipartEntity) entity))); } catch (IOException e) { throw new GlazeException(e); } } else { producer = new RequestProducerImpl(target, entityRequest, new EntityAsyncContentProducer(entity)); } return producer; } }