All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.easimart.EasimartApacheHttpClient Maven / Gradle / Ivy

package co.easimart;

import android.net.SSLCertificateSocketFactory;
import android.net.SSLSessionCache;
import android.net.http.AndroidHttpClient;

import co.easimart.http.EasimartHttpBody;
import co.easimart.http.EasimartHttpRequest;
import co.easimart.http.EasimartHttpResponse;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * An implementation of EasimartHttpClient using Apache httpclient library
 */
@SuppressWarnings("deprecation")
/** package */ class EasimartApacheHttpClient extends EasimartHttpClient {
  private DefaultHttpClient apacheClient;

  public EasimartApacheHttpClient(int socketOperationTimeout, SSLSessionCache sslSessionCache) {
    // Most of this is from AndroidHttpClient#newInstance() except [1] and [2]
    HttpParams params = new BasicHttpParams();

    // Turn off stale checking.  Our connections break all the time anyway,
    // and it's not worth it to pay the penalty of checking every time.
    HttpConnectionParams.setStaleCheckingEnabled(params, false);

    HttpConnectionParams.setConnectionTimeout(params, socketOperationTimeout);
    HttpConnectionParams.setSoTimeout(params, socketOperationTimeout);
    HttpConnectionParams.setSocketBufferSize(params, 8192);

    // Don't handle redirects. We copy the setting from AndroidHttpClient.
    // For detail, check https://quip.com/Px8jAxnaun2r
    HttpClientParams.setRedirecting(params, false);

    // Register standard protocols.
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    schemeRegistry.register(new Scheme("https", SSLCertificateSocketFactory.getHttpSocketFactory(
        socketOperationTimeout, sslSessionCache), 443));

    // [1] AndroidHttpClient defaults to 2 connections per route. Not fun. AND you can't set these
    // properties after AndroidHttpClient#newInstance(context)
    String maxConnectionsStr = System.getProperty("http.maxConnections");
    if (maxConnectionsStr != null) {
      int maxConnections = Integer.parseInt(maxConnectionsStr);
      ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(maxConnections));
      ConnManagerParams.setMaxTotalConnections(params, maxConnections);
    }

    // [2] Originally from EasimartCommand, check proxy
    String host = System.getProperty("http.proxyHost");
    String portString = System.getProperty("http.proxyPort");
    if (host != null && host.length() != 0 && portString != null && portString.length() != 0) {
      int port = Integer.parseInt(portString);
      HttpHost proxy = new HttpHost(host, port, "http");
      params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
    }

    ClientConnectionManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
    apacheClient = new DefaultHttpClient(manager, params);

    // Disable retry logic by ApacheHttpClient. When we leave the app idle for 3 - 5 min, the next
    // request will always fail with NoHttpResponseException: The target server failed to respond,
    // in this situation, the Apache httpClient will try to retry the request,
    // however, since we use InputStreamEntity which is non-repeatable, we will see the
    // NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.
    // We disable the retry logic by ApacheHttpClient to expose the real issue
    apacheClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
  }

  @Override
  /* package */ EasimartHttpResponse executeInternal(EasimartHttpRequest request) throws IOException {
    HttpUriRequest apacheRequest = getRequest(request);

    HttpResponse apacheResponse = apacheClient.execute(apacheRequest);

    return getResponse(apacheResponse);
  }

  @Override
  /* package */ EasimartHttpResponse getResponse(HttpResponse apacheResponse)
      throws IOException {
    if (apacheResponse == null) {
      throw new IllegalArgumentException(
          "HttpResponse passed to getResponse should not be null."
      );
    }

    // Status code
    int statusCode = apacheResponse.getStatusLine().getStatusCode();

    // Content
    InputStream content = disableHttpLibraryAutoDecompress() ?
        apacheResponse.getEntity().getContent() :
        AndroidHttpClient.getUngzippedContent(apacheResponse.getEntity());

    // Total size
    int totalSize = -1;
    Header[] contentLengthHeader = apacheResponse.getHeaders("Content-Length");
    // Some encodings, such as chunked encoding, forbid the
    // content-length header.
    if (contentLengthHeader.length > 0) {
      totalSize = Integer.parseInt(contentLengthHeader[0].getValue());
    }

    // Reason phrase
    String reasonPhrase = apacheResponse.getStatusLine().getReasonPhrase();

    // Headers
    Map headers = new HashMap<>();
    for (Header header : apacheResponse.getAllHeaders()) {
      headers.put(header.getName(), header.getValue());
    }

    // Content type
    String contentType = null;
    HttpEntity entity = apacheResponse.getEntity();
    if (entity != null && entity.getContentType() != null) {
      contentType = entity.getContentType().getValue();
    }

    return new EasimartHttpResponse.Builder()
        .setStatusCode(statusCode)
        .setContent(content)
        .setTotalSize(totalSize)
        .setReasonPhrase(reasonPhrase)
        .setHeaders(headers)
        .setContentType(contentType)
        .build();
  }

  @Override
  /* package */ HttpUriRequest getRequest(EasimartHttpRequest httpRequest)
      throws IOException {
    if (httpRequest == null) {
      throw new IllegalArgumentException(
          "EasimartHttpRequest passed to getApacheRequest should not be null."
      );
    }

    HttpUriRequest apacheRequest;
    EasimartHttpRequest.Method method = httpRequest.getMethod();
    String url = httpRequest.getUrl();
    switch (method) {
      case GET:
        apacheRequest = new HttpGet(url);
        break;
      case DELETE:
        apacheRequest = new HttpDelete(url);
        break;
      case POST:
        apacheRequest = new HttpPost(url);
        break;
      case PUT:
        apacheRequest = new HttpPut(url);
        break;
      default:
        // This case will never be reached since we have already handled this case in
        // EasimartRequest.newRequest().
        throw new IllegalStateException("Unsupported http method " + method.toString());
    }

    // Set header
    for (Map.Entry entry : httpRequest.getAllHeaders().entrySet()) {
      apacheRequest.setHeader(entry.getKey(), entry.getValue());
    }
    AndroidHttpClient.modifyRequestToAcceptGzipResponse(apacheRequest);

    // Set entity
    EasimartHttpBody body = httpRequest.getBody();
    switch (method) {
      case POST:
        ((HttpPost) apacheRequest).setEntity(new EasimartApacheHttpEntity(body));
        break;
      case PUT:
        ((HttpPut) apacheRequest).setEntity(new EasimartApacheHttpEntity(body));
        break;
      default:
        break;
    }
    return apacheRequest;
  }

  /**
   * An wrapper of Apache InputStreamEntity. It takes a EasimartHttpBody
   * and transfer it to a HttpEntity
   */
  private static class EasimartApacheHttpEntity extends InputStreamEntity {
    private EasimartHttpBody body;

    public EasimartApacheHttpEntity(EasimartHttpBody body) throws IOException {
      super(body.getContent(), body.getContentLength());
      super.setContentType(body.getContentType());
      this.body = body;
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
      body.writeTo(out);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy