/*
* Copyright (c) 2010 Sonatype, Inc. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.ning.http.client;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.ssl.SSLContext;
import com.ning.http.client.resumable.ResumableAsyncHandler;
import com.ning.http.client.resumable.ResumableIOExceptionFilter;
import com.ning.http.client.simple.HeaderMap;
import com.ning.http.client.simple.SimpleAHCTransferListener;
/**
* Simple implementation of {@link AsyncHttpClient} and it's related builders (
* {@link com.ning.http.client.AsyncHttpClientConfig}, {@link Realm},
* {@link com.ning.http.client.ProxyServer} and
* {@link com.ning.http.client.AsyncHandler}. You can build powerful application
* by just using this class.
* <p/>
* This class rely on {@link BodyGenerator} and {@link BodyConsumer} for
* handling the request and response body. No {@link AsyncHandler} are required.
* As simple as: <blockquote>
*
* <pre>
* SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder()
* .setIdleConnectionInPoolTimeoutInMs(100)
* .setMaximumConnectionsTotal(50)
* .setRequestTimeoutInMs(5 * 60 * 1000)
* .setUrl(getTargetUrl())
* .setHeader("Content-Type", "text/html").build();
* <p/>
* StringBuilder s = new StringBuilder();
* Future<Response> future = client.post(new InputStreamBodyGenerator(new ByteArrayInputStream(MY_MESSAGE.getBytes())), new AppendableBodyConsumer(s));
* </pre>
*
* </blockquote> or <blockquote>
*
* <pre>
* public void ByteArrayOutputStreamBodyConsumerTest() throws Throwable {
* <p/>
* SimpleAsyncHttpClient client = new SimpleAsyncHttpClient.Builder()
* .setUrl(getTargetUrl())
* .build();
* <p/>
* ByteArrayOutputStream o = new ByteArrayOutputStream(10);
* Future<Response> future = client.post(new FileodyGenerator(myFile), new OutputStreamBodyConsumer(o));
* </pre>
*
* </blockquote>
*/
public class SimpleAsyncHttpClient {
private final AsyncHttpClientConfig config;
private final RequestBuilder requestBuilder;
private AsyncHttpClient asyncHttpClient;
private final ThrowableHandler defaultThrowableHandler;
private final boolean resumeEnabled;
private final ErrorDocumentBehaviour errorDocumentBehaviour;
private final SimpleAHCTransferListener listener;
private final boolean derived;
private SimpleAsyncHttpClient(final AsyncHttpClientConfig config,
final RequestBuilder requestBuilder,
final ThrowableHandler defaultThrowableHandler,
final ErrorDocumentBehaviour errorDocumentBehaviour,
final boolean resumeEnabled, final AsyncHttpClient ahc,
final SimpleAHCTransferListener listener) {
this.config = config;
this.requestBuilder = requestBuilder;
this.defaultThrowableHandler = defaultThrowableHandler;
this.resumeEnabled = resumeEnabled;
this.errorDocumentBehaviour = errorDocumentBehaviour;
this.asyncHttpClient = ahc;
this.listener = listener;
this.derived = ahc != null;
}
public Future<Response> post(final Part... parts) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
for (final Part part : parts) {
r.addBodyPart(part);
}
return execute(r, null, null);
}
public Future<Response> post(final BodyConsumer consumer,
final Part... parts) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
for (final Part part : parts) {
r.addBodyPart(part);
}
return execute(r, consumer, null);
}
public Future<Response> post(final BodyGenerator bodyGenerator)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
r.setBody(bodyGenerator);
return execute(r, null, null);
}
public Future<Response> post(final BodyGenerator bodyGenerator,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
r.setBody(bodyGenerator);
return execute(r, null, throwableHandler);
}
public Future<Response> post(final BodyGenerator bodyGenerator,
final BodyConsumer bodyConsumer) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
r.setBody(bodyGenerator);
return execute(r, bodyConsumer, null);
}
public Future<Response> post(final BodyGenerator bodyGenerator,
final BodyConsumer bodyConsumer,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
r.setBody(bodyGenerator);
return execute(r, bodyConsumer, throwableHandler);
}
public Future<Response> put(final Part... parts) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
for (final Part part : parts) {
r.addBodyPart(part);
}
return execute(r, null, null);
}
public Future<Response> put(final BodyConsumer consumer,
final Part... parts) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("POST");
for (final Part part : parts) {
r.addBodyPart(part);
}
return execute(r, consumer, null);
}
public Future<Response> put(final BodyGenerator bodyGenerator,
final BodyConsumer bodyConsumer) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("PUT");
r.setBody(bodyGenerator);
return execute(r, bodyConsumer, null);
}
public Future<Response> put(final BodyGenerator bodyGenerator,
final BodyConsumer bodyConsumer,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("PUT");
r.setBody(bodyGenerator);
return execute(r, bodyConsumer, throwableHandler);
}
public Future<Response> put(final BodyGenerator bodyGenerator)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("PUT");
r.setBody(bodyGenerator);
return execute(r, null, null);
}
public Future<Response> put(final BodyGenerator bodyGenerator,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("PUT");
r.setBody(bodyGenerator);
return execute(r, null, throwableHandler);
}
public Future<Response> get() throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
return execute(r, null, null);
}
public Future<Response> get(final ThrowableHandler throwableHandler)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
return execute(r, null, throwableHandler);
}
public Future<Response> get(final BodyConsumer bodyConsumer)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
return execute(r, bodyConsumer, null);
}
public Future<Response> get(final BodyConsumer bodyConsumer,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
return execute(r, bodyConsumer, throwableHandler);
}
public Future<Response> delete() throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("DELETE");
return execute(r, null, null);
}
public Future<Response> delete(final ThrowableHandler throwableHandler)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("DELETE");
return execute(r, null, throwableHandler);
}
public Future<Response> delete(final BodyConsumer bodyConsumer)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("DELETE");
return execute(r, bodyConsumer, null);
}
public Future<Response> delete(final BodyConsumer bodyConsumer,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("DELETE");
return execute(r, bodyConsumer, throwableHandler);
}
public Future<Response> head() throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("HEAD");
return execute(r, null, null);
}
public Future<Response> head(final ThrowableHandler throwableHandler)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("HEAD");
return execute(r, null, throwableHandler);
}
public Future<Response> options() throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("OPTIONS");
return execute(r, null, null);
}
public Future<Response> options(final ThrowableHandler throwableHandler)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("OPTIONS");
return execute(r, null, throwableHandler);
}
public Future<Response> options(final BodyConsumer bodyConsumer)
throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("OPTIONS");
return execute(r, bodyConsumer, null);
}
public Future<Response> options(final BodyConsumer bodyConsumer,
final ThrowableHandler throwableHandler) throws IOException {
final RequestBuilder r = rebuildRequest(requestBuilder.build());
r.setMethod("OPTIONS");
return execute(r, bodyConsumer, throwableHandler);
}
private RequestBuilder rebuildRequest(final Request rb) {
return new RequestBuilder(rb);
}
private Future<Response> execute(final RequestBuilder rb,
final BodyConsumer bodyConsumer, ThrowableHandler throwableHandler)
throws IOException {
if (throwableHandler == null) {
throwableHandler = defaultThrowableHandler;
}
final Request request = rb.build();
ProgressAsyncHandler<Response> handler = new BodyConsumerAsyncHandler(
bodyConsumer, throwableHandler, errorDocumentBehaviour,
request.getUrl(), listener);
if (resumeEnabled && request.getMethod().equals("GET")
&& bodyConsumer != null
&& bodyConsumer instanceof ResumableBodyConsumer) {
final ResumableBodyConsumer fileBodyConsumer = (ResumableBodyConsumer) bodyConsumer;
final long length = fileBodyConsumer.getTransferredBytes();
fileBodyConsumer.resume();
handler = new ResumableBodyConsumerAsyncHandler(length, handler);
}
return asyncHttpClient().executeRequest(request, handler);
}
private AsyncHttpClient asyncHttpClient() {
synchronized (config) {
if (asyncHttpClient == null) {
asyncHttpClient = new AsyncHttpClient(config);
}
}
return asyncHttpClient;
}
/**
* Close the underlying AsyncHttpClient for this instance.
* <p/>
* If this instance is derived from another instance, this method does
* nothing as the client instance is managed by the original
* SimpleAsyncHttpClient.
*
* @see #derive()
* @see AsyncHttpClient#close()
*/
public void close() {
if (!derived && asyncHttpClient != null) {
asyncHttpClient.close();
}
}
/**
* Returns a Builder for a derived SimpleAsyncHttpClient that uses the same
* instance of {@link AsyncHttpClient} to execute requests.
* <p/>
* <p/>
* <p/>
* The original SimpleAsyncHttpClient is responsible for managing the
* underlying AsyncHttpClient. For the derived instance, {@link #close()} is
* a NOOP. If the original SimpleAsyncHttpClient is closed, all derived
* instances become invalid.
*
* @return a Builder for a derived SimpleAsyncHttpClient that uses the same
* instance of {@link AsyncHttpClient} to execute requests, never
* {@code null}.
*/
public DerivedBuilder derive() {
return new Builder(this);
}
public enum ErrorDocumentBehaviour {
/**
* Write error documents as usual via
* {@link BodyConsumer#consume(java.nio.ByteBuffer)}.
*/
WRITE,
/**
* Accumulate error documents in memory but do not consume.
*/
ACCUMULATE,
/**
* Omit error documents. An error document will neither be available in
* the response nor written via a {@link BodyConsumer}.
*/
OMIT;
}
/**
* This interface contains possible configuration changes for a derived
* SimpleAsyncHttpClient.
*
* @see SimpleAsyncHttpClient#derive()
*/
public interface DerivedBuilder {
DerivedBuilder setFollowRedirects(boolean followRedirects);
DerivedBuilder setVirtualHost(String virtualHost);
DerivedBuilder setUrl(String url);
DerivedBuilder setParameters(FluentStringsMap parameters)
throws IllegalArgumentException;
DerivedBuilder setParameters(Map<String, Collection<String>> parameters)
throws IllegalArgumentException;
DerivedBuilder setHeaders(Map<String, Collection<String>> headers);
DerivedBuilder setHeaders(FluentCaseInsensitiveStringsMap headers);
DerivedBuilder setHeader(String name, String value);
DerivedBuilder addQueryParameter(String name, String value);
DerivedBuilder addParameter(String key, String value)
throws IllegalArgumentException;
DerivedBuilder addHeader(String name, String value);
DerivedBuilder addCookie(Cookie cookie);
DerivedBuilder addBodyPart(Part part) throws IllegalArgumentException;
DerivedBuilder setResumableDownload(boolean resume);
SimpleAsyncHttpClient build();
}
public final static class Builder implements DerivedBuilder {
private final RequestBuilder requestBuilder;
private final AsyncHttpClientConfig.Builder configBuilder = new AsyncHttpClientConfig.Builder();
private Realm.RealmBuilder realmBuilder = null;
private ProxyServer.Protocol proxyProtocol = null;
private String proxyHost = null;
private String proxyPrincipal = null;
private String proxyPassword = null;
private int proxyPort = 80;
private ThrowableHandler defaultThrowableHandler = null;
private boolean enableResumableDownload = false;
private ErrorDocumentBehaviour errorDocumentBehaviour = ErrorDocumentBehaviour.WRITE;
private AsyncHttpClient ahc = null;
private SimpleAHCTransferListener listener = null;
public Builder() {
requestBuilder = new RequestBuilder("GET", false);
}
private Builder(final SimpleAsyncHttpClient client) {
this.requestBuilder = new RequestBuilder(
client.requestBuilder.build());
this.defaultThrowableHandler = client.defaultThrowableHandler;
this.errorDocumentBehaviour = client.errorDocumentBehaviour;
this.enableResumableDownload = client.resumeEnabled;
this.ahc = client.asyncHttpClient();
this.listener = client.listener;
}
@Override
public Builder addBodyPart(final Part part)
throws IllegalArgumentException {
requestBuilder.addBodyPart(part);
return this;
}
@Override
public Builder addCookie(final Cookie cookie) {
requestBuilder.addCookie(cookie);
return this;
}
@Override
public Builder addHeader(final String name, final String value) {
requestBuilder.addHeader(name, value);
return this;
}
@Override
public Builder addParameter(final String key, final String value)
throws IllegalArgumentException {
requestBuilder.addParameter(key, value);
return this;
}
@Override
public Builder addQueryParameter(final String name, final String value) {
requestBuilder.addQueryParameter(name, value);
return this;
}
@Override
public Builder setHeader(final String name, final String value) {
requestBuilder.setHeader(name, value);
return this;
}
@Override
public Builder setHeaders(final FluentCaseInsensitiveStringsMap headers) {
requestBuilder.setHeaders(headers);
return this;
}
@Override
public Builder setHeaders(final Map<String, Collection<String>> headers) {
requestBuilder.setHeaders(headers);
return this;
}
@Override
public Builder setParameters(
final Map<String, Collection<String>> parameters)
throws IllegalArgumentException {
requestBuilder.setParameters(parameters);
return this;
}
@Override
public Builder setParameters(final FluentStringsMap parameters)
throws IllegalArgumentException {
requestBuilder.setParameters(parameters);
return this;
}
@Override
public Builder setUrl(final String url) {
requestBuilder.setUrl(url);
return this;
}
@Override
public Builder setVirtualHost(final String virtualHost) {
requestBuilder.setVirtualHost(virtualHost);
return this;
}
@Override
public Builder setFollowRedirects(final boolean followRedirects) {
requestBuilder.setFollowRedirects(followRedirects);
return this;
}
public Builder setMaximumConnectionsTotal(
final int defaultMaxTotalConnections) {
configBuilder
.setMaximumConnectionsTotal(defaultMaxTotalConnections);
return this;
}
public Builder setMaximumConnectionsPerHost(
final int defaultMaxConnectionPerHost) {
configBuilder
.setMaximumConnectionsPerHost(defaultMaxConnectionPerHost);
return this;
}
public Builder setConnectionTimeoutInMs(final int connectionTimeuot) {
configBuilder.setConnectionTimeoutInMs(connectionTimeuot);
return this;
}
public Builder setIdleConnectionInPoolTimeoutInMs(
final int defaultIdleConnectionInPoolTimeoutInMs) {
configBuilder
.setIdleConnectionInPoolTimeoutInMs(defaultIdleConnectionInPoolTimeoutInMs);
return this;
}
public Builder setRequestTimeoutInMs(final int defaultRequestTimeoutInMs) {
configBuilder.setRequestTimeoutInMs(defaultRequestTimeoutInMs);
return this;
}
public Builder setMaximumNumberOfRedirects(final int maxDefaultRedirects) {
configBuilder.setMaximumNumberOfRedirects(maxDefaultRedirects);
return this;
}
public Builder setCompressionEnabled(final boolean compressionEnabled) {
configBuilder.setCompressionEnabled(compressionEnabled);
return this;
}
public Builder setUserAgent(final String userAgent) {
configBuilder.setUserAgent(userAgent);
return this;
}
public Builder setAllowPoolingConnection(
final boolean allowPoolingConnection) {
configBuilder.setAllowPoolingConnection(allowPoolingConnection);
return this;
}
public Builder setScheduledExecutorService(
final ScheduledExecutorService reaper) {
configBuilder.setScheduledExecutorService(reaper);
return this;
}
public Builder setExecutorService(
final ExecutorService applicationThreadPool) {
configBuilder.setExecutorService(applicationThreadPool);
return this;
}
public Builder setSSLEngineFactory(
final SSLEngineFactory sslEngineFactory) {
configBuilder.setSSLEngineFactory(sslEngineFactory);
return this;
}
public Builder setSSLContext(final SSLContext sslContext) {
configBuilder.setSSLContext(sslContext);
return this;
}
public Builder setRequestCompressionLevel(
final int requestCompressionLevel) {
configBuilder.setRequestCompressionLevel(requestCompressionLevel);
return this;
}
public Builder setRealmDomain(final String domain) {
realm().setDomain(domain);
return this;
}
public Builder setRealmPrincipal(final String principal) {
realm().setPrincipal(principal);
return this;
}
public Builder setRealmPassword(final String password) {
realm().setPassword(password);
return this;
}
public Builder setRealmScheme(final Realm.AuthScheme scheme) {
realm().setScheme(scheme);
return this;
}
public Builder setRealmName(final String realmName) {
realm().setRealmName(realmName);
return this;
}
public Builder setRealmUsePreemptiveAuth(final boolean usePreemptiveAuth) {
realm().setUsePreemptiveAuth(usePreemptiveAuth);
return this;
}
public Builder setRealmEnconding(final String enc) {
realm().setEnconding(enc);
return this;
}
public Builder setProxyProtocol(final ProxyServer.Protocol protocol) {
this.proxyProtocol = protocol;
return this;
}
public Builder setProxyHost(final String host) {
this.proxyHost = host;
return this;
}
public Builder setProxyPrincipal(final String principal) {
this.proxyPrincipal = principal;
return this;
}
public Builder setProxyPassword(final String password) {
this.proxyPassword = password;
return this;
}
public Builder setProxyPort(final int port) {
this.proxyPort = port;
return this;
}
public Builder setDefaultThrowableHandler(
final ThrowableHandler throwableHandler) {
this.defaultThrowableHandler = throwableHandler;
return this;
}
/**
* This setting controls whether an error document should be written via
* the {@link BodyConsumer} after an error status code was received
* (e.g. 404). Default is {@link ErrorDocumentBehaviour#WRITE}.
*/
public Builder setErrorDocumentBehaviour(
final ErrorDocumentBehaviour behaviour) {
this.errorDocumentBehaviour = behaviour;
return this;
}
/**
* Enable resumable downloads for the SimpleAHC. Resuming downloads will
* only work for GET requests with an instance of
* {@link ResumableBodyConsumer}.
*/
@Override
public Builder setResumableDownload(
final boolean enableResumableDownload) {
this.enableResumableDownload = enableResumableDownload;
return this;
}
private Realm.RealmBuilder realm() {
if (realmBuilder == null) {
realmBuilder = new Realm.RealmBuilder();
}
return realmBuilder;
}
/**
* Set the listener to notify about connection progress.
*/
public Builder setListener(final SimpleAHCTransferListener listener) {
this.listener = listener;
return this;
}
/**
* Set the number of time a request will be retried when an
* {@link java.io.IOException} occurs because of a Network exception.
*
* @param maxRequestRetry
* the number of time a request will be retried
* @return this
*/
public Builder setMaxRequestRetry(final int maxRequestRetry) {
configBuilder.setMaxRequestRetry(maxRequestRetry);
return this;
}
@Override
public SimpleAsyncHttpClient build() {
if (realmBuilder != null) {
configBuilder.setRealm(realmBuilder.build());
}
if (proxyHost != null) {
configBuilder.setProxyServer(new ProxyServer(proxyProtocol,
proxyHost, proxyPort, proxyPrincipal, proxyPassword));
}
configBuilder
.addIOExceptionFilter(new ResumableIOExceptionFilter());
final SimpleAsyncHttpClient sc = new SimpleAsyncHttpClient(
configBuilder.build(), requestBuilder,
defaultThrowableHandler, errorDocumentBehaviour,
enableResumableDownload, ahc, listener);
return sc;
}
}
private final static class ResumableBodyConsumerAsyncHandler extends
ResumableAsyncHandler<Response> implements
ProgressAsyncHandler<Response> {
private final ProgressAsyncHandler<Response> delegate;
public ResumableBodyConsumerAsyncHandler(final long byteTransferred,
final ProgressAsyncHandler<Response> delegate) {
super(byteTransferred, delegate);
this.delegate = delegate;
}
@Override
public com.ning.http.client.AsyncHandler.STATE onHeaderWriteCompleted() {
return delegate.onHeaderWriteCompleted();
}
@Override
public com.ning.http.client.AsyncHandler.STATE onContentWriteCompleted() {
return delegate.onContentWriteCompleted();
}
@Override
public com.ning.http.client.AsyncHandler.STATE onContentWriteProgress(
final long amount, final long current, final long total) {
return delegate.onContentWriteProgress(amount, current, total);
}
}
private final static class BodyConsumerAsyncHandler extends
AsyncCompletionHandlerBase {
private final BodyConsumer bodyConsumer;
private final ThrowableHandler exceptionHandler;
private final ErrorDocumentBehaviour errorDocumentBehaviour;
private final String url;
private final SimpleAHCTransferListener listener;
private boolean accumulateBody = false;
private boolean omitBody = false;
private int amount = 0;
private long total = -1;
public BodyConsumerAsyncHandler(final BodyConsumer bodyConsumer,
final ThrowableHandler exceptionHandler,
final ErrorDocumentBehaviour errorDocumentBehaviour,
final String url, final SimpleAHCTransferListener listener) {
this.bodyConsumer = bodyConsumer;
this.exceptionHandler = exceptionHandler;
this.errorDocumentBehaviour = errorDocumentBehaviour;
this.url = url;
this.listener = listener;
}
@Override
public void onThrowable(final Throwable t) {
try {
if (exceptionHandler != null) {
exceptionHandler.onThrowable(t);
} else {
super.onThrowable(t);
}
} finally {
closeConsumer();
}
}
/**
* {@inheritDoc}
*/
@Override
public STATE onBodyPartReceived(final HttpResponseBodyPart content)
throws Exception {
fireReceived(content);
if (omitBody) {
return STATE.CONTINUE;
}
if (!accumulateBody && bodyConsumer != null) {
bodyConsumer.consume(content.getBodyByteBuffer());
} else {
return super.onBodyPartReceived(content);
}
return STATE.CONTINUE;
}
/**
* {@inheritDoc}
*/
@Override
public Response onCompleted(final Response response) throws Exception {
fireCompleted(response);
closeConsumer();
return super.onCompleted(response);
}
private void closeConsumer() {
try {
if (bodyConsumer != null) {
bodyConsumer.close();
}
} catch (final IOException ex) {
}
}
@Override
public STATE onStatusReceived(final HttpResponseStatus status)
throws Exception {
fireStatus(status);
if (isErrorStatus(status)) {
switch (errorDocumentBehaviour) {
case ACCUMULATE:
accumulateBody = true;
break;
case OMIT:
omitBody = true;
break;
default:
break;
}
}
return super.onStatusReceived(status);
}
private boolean isErrorStatus(final HttpResponseStatus status) {
return status.getStatusCode() >= 400;
}
@Override
public STATE onHeadersReceived(final HttpResponseHeaders headers)
throws Exception {
calculateTotal(headers);
fireHeaders(headers);
return super.onHeadersReceived(headers);
}
private void calculateTotal(final HttpResponseHeaders headers) {
final String length = headers.getHeaders().getFirstValue(
"Content-Length");
try {
total = Integer.valueOf(length);
} catch (final Exception e) {
total = -1;
}
}
@Override
public STATE onContentWriteProgress(final long amount,
final long current, final long total) {
fireSent(url, amount, current, total);
return super.onContentWriteProgress(amount, current, total);
}
private void fireStatus(final HttpResponseStatus status) {
if (listener != null) {
listener.onStatus(url, status.getStatusCode(),
status.getStatusText());
}
}
private void fireReceived(final HttpResponseBodyPart content) {
final int remaining = content.getBodyByteBuffer().remaining();
amount += remaining;
if (listener != null) {
listener.onBytesReceived(url, amount, remaining, total);
}
}
private void fireHeaders(final HttpResponseHeaders headers) {
if (listener != null) {
listener.onHeaders(url, new HeaderMap(headers.getHeaders()));
}
}
private void fireSent(final String url, final long amount,
final long current, final long total) {
if (listener != null) {
listener.onBytesSent(url, amount, current, total);
}
}
private void fireCompleted(final Response response) {
if (listener != null) {
listener.onCompleted(url, response.getStatusCode(),
response.getStatusText());
}
}
}
}