Project: zirco-browser
package org.emergent.android.weave.client;
 
import org.apache.http.Header; 
import org.apache.http.HttpEntity; 
import org.apache.http.HttpException; 
import org.apache.http.HttpHost; 
import org.apache.http.HttpMessage; 
import org.apache.http.HttpRequest; 
import org.apache.http.HttpRequestInterceptor; 
import org.apache.http.HttpResponse; 
import org.apache.http.HttpVersion; 
import org.apache.http.StatusLine; 
import org.apache.http.auth.AuthScope; 
import org.apache.http.auth.AuthState; 
import org.apache.http.auth.Credentials; 
import org.apache.http.auth.UsernamePasswordCredentials; 
import org.apache.http.client.CredentialsProvider; 
import org.apache.http.client.HttpClient; 
import org.apache.http.client.HttpResponseException; 
import org.apache.http.client.ResponseHandler; 
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.HttpRequestBase; 
import org.apache.http.client.protocol.ClientContext; 
import org.apache.http.conn.ClientConnectionManager; 
import org.apache.http.conn.params.ConnManagerPNames; 
import org.apache.http.conn.params.ConnPerRouteBean; 
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.conn.scheme.SocketFactory; 
import org.apache.http.conn.ssl.AbstractVerifier; 
import org.apache.http.conn.ssl.SSLSocketFactory; 
import org.apache.http.conn.util.InetAddressUtils; 
import org.apache.http.impl.auth.BasicScheme; 
import org.apache.http.impl.client.DefaultHttpClient; 
import org.apache.http.impl.conn.SingleClientConnManager; 
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 
import org.apache.http.params.BasicHttpParams; 
import org.apache.http.params.HttpParams; 
import org.apache.http.params.HttpProtocolParams; 
import org.apache.http.protocol.ExecutionContext; 
import org.apache.http.protocol.HttpContext; 
 
import javax.net.ssl.SSLException; 
import java.io.IOException; 
import java.net.InetAddress; 
import java.net.URI; 
import java.net.UnknownHostException; 
import java.util.Arrays; 
import java.util.Collection; 
import java.util.Date; 
import java.util.HashSet; 
 
/**
 * @author Patrick Woodworth 
 */
 
class WeaveTransport { 
 
  private static final int HTTP_PORT_DEFAULT = 80
  private static final int HTTPS_PORT_DEFAULT = 443
 
  private static final HttpRequestInterceptor sm_preemptiveAuth = new MyInterceptor(); 
  private static final MyResponseHandler sm_responseHandler = new MyResponseHandler(); 
 
  private static final HttpParams sm_httpParams; 
 
  static { 
    HttpParams params = new BasicHttpParams(); 
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
    HttpProtocolParams.setContentCharset(params, "UTF-8"); 
    HttpProtocolParams.setUserAgent(params, WeaveConstants.USER_AGENT); 
    HttpProtocolParams.setUseExpectContinue(params, false); 
//    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); 
    params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30); 
    params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30)); 
    sm_httpParams = params; 
  } 
 
  private final SocketFactory m_sslSocketFactory; 
 
  private final ClientConnectionManager m_clientConMgr; 
 
  public WeaveTransport() { 
    this(WeaveConstants.CONNECTION_POOL_ENABLED_DEFAULT); 
  } 
 
  public WeaveTransport(boolean useConnectionPool) { 
    this(useConnectionPool, WeaveConstants.ALLOW_INVALID_CERTS_DEFAULT); 
  } 
 
  public WeaveTransport(boolean useConnectionPool, boolean allowInvalidCerts) { 
    m_sslSocketFactory = createSocketFactory(allowInvalidCerts); 
    m_clientConMgr = useConnectionPool ? createClientConnectionManager(true) : null
  } 
 
  public void shutdown() { 
//    if (m_clientConMgr != null) 
//      m_clientConMgr.shutdown(); 
  } 
 
  public WeaveResponse execDeleteMethod(String username, String password, URI uri) throws IOException, WeaveException { 
    HttpDelete method = new HttpDelete(uri); 
    return execGenericMethod(username, password, uri, method); 
  } 
 
  public WeaveResponse execGetMethod(String username, String password, URI uri) throws IOException, WeaveException { 
    HttpGet method = new HttpGet(uri); 
    return execGenericMethod(username, password, uri, method); 
  } 
 
  public WeaveResponse execPostMethod(String username, String password, URI uri, HttpEntity entity) 
      throws IOException, WeaveException { 
    HttpPost method = new HttpPost(uri); 
    method.setEntity(entity); 
    return execGenericMethod(username, password, uri, method); 
  } 
 
  public WeaveResponse execPutMethod(String username, String password, URI uri, HttpEntity entity) 
      throws IOException, WeaveException { 
    HttpPut method = new HttpPut(uri); 
    method.setEntity(entity); 
    return execGenericMethod(username, password, uri, method); 
  } 
 
  private void setMethodHeaders(HttpMessage method) { 
    method.addHeader("Pragma","no-cache"); 
    method.addHeader("Cache-Control","no-cache"); 
  } 
 
  private WeaveResponse execGenericMethod(String username, String password, URI uri, HttpRequestBase method) 
      throws IOException, WeaveException { 
    HttpClient client = null
    try { 
      client = createHttpClient(username, password); 
      return execGenericMethod(client, uri, method); 
//    } catch (IOException e) { 
//      throw new WeaveException("Unable to communicate with Weave server.", e); 
    } finally { 
      if (m_clientConMgr == null && client != null) { 
        client.getConnectionManager().shutdown(); 
      } 
    } 
  } 
 
  private WeaveResponse execGenericMethod(HttpClient client, URI uri, HttpRequestBase method) throws IOException, WeaveException { 
    setMethodHeaders(method); 
    MyResponseHandler responseHandler = sm_responseHandler; 
    String scheme = uri.getScheme(); 
    String hostname = uri.getHost(); 
    int port = uri.getPort(); 
    HttpHost httpHost = new HttpHost(hostname, port, scheme); 
    WeaveResponseHeaders responseHeaders = null
    try 
    { 
      WeaveResponse response = client.execute(httpHost, method, responseHandler); 
      response.setUri(uri); 
      responseHeaders = response.getResponseHeaders(); 
      return response; 
    } catch (WeaveResponseException e) { 
      responseHeaders = e.getResponseHeaders(); 
      throw e; 
    } finally { 
      if (responseHeaders != null) { 
//        long backoff = responseHeaders.getBackoffSeconds(); 
//        if (backoff > 0) { 
//          long newbackoff = System.currentTimeMillis() + backoff; 
//          m_backoff.set(newbackoff); 
//        } 
      } 
    } 
  } 
 
  private DefaultHttpClient createDefaultHttpClient() { 
    ClientConnectionManager connectionManager; 
    if (m_clientConMgr != null) { 
      connectionManager = m_clientConMgr; 
    } else { 
      connectionManager = createClientConnectionManager(false); 
    } 
    return new DefaultHttpClient(connectionManager, sm_httpParams); 
  } 
 
  private HttpClient createHttpClient(String userId, String password) { 
    DefaultHttpClient retval = createDefaultHttpClient(); 
    Credentials defaultcreds = new UsernamePasswordCredentials(userId, password); 
    retval.getCredentialsProvider().setCredentials(AuthScope.ANY, defaultcreds); 
    retval.addRequestInterceptor(sm_preemptiveAuth, 0); 
    return retval; 
  } 
 
 
  private ClientConnectionManager createClientConnectionManager(boolean threadSafe) { 
    SchemeRegistry schemeRegistry = new SchemeRegistry(); 
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), HTTP_PORT_DEFAULT)); 
    schemeRegistry.register(new Scheme("https", m_sslSocketFactory, HTTPS_PORT_DEFAULT)); 
    if (threadSafe) { 
      return new ThreadSafeClientConnManager(sm_httpParams, schemeRegistry); 
    } else { 
      return new SingleClientConnManager(sm_httpParams, schemeRegistry); 
    } 
  } 
 
  private static SocketFactory createSocketFactory(boolean allowInvalidCerts) { 
    SocketFactory sslSocketFactory; 
    if (allowInvalidCerts) { 
      sslSocketFactory = new WeaveSSLSocketFactory(); 
    } else { 
      sslSocketFactory = SSLSocketFactory.getSocketFactory(); 
      ((SSLSocketFactory)sslSocketFactory).setHostnameVerifier(new WeaveHostnameVerifier()); 
    } 
    return sslSocketFactory; 
  } 
 
  public static class WeaveResponseHeaders { 
    private final Header[] m_headers; 
 
    public WeaveResponseHeaders(HttpResponse response) { 
      m_headers = response.getAllHeaders(); 
    } 
 
    public Header[] getHeaders() { 
      return m_headers; 
    } 
 
    public Date getServerTimestamp() { 
      Date retval = null
      String ststamp = getHeaderValue(WeaveHeader.X_WEAVE_TIMESTAMP); 
      if (ststamp != null
        retval = WeaveUtil.toModifiedTimeDate(ststamp); 
      return retval; 
    } 
 
    public long getBackoffSeconds() { 
      long retval = 0
      try 
      { 
        String valStr = getHeaderValue(WeaveHeader.X_WEAVE_BACKOFF); 
        if (valStr != null
          retval = Long.parseLong(valStr); 
      } catch (Exception ignored) { } 
      return retval; 
    } 
 
    private String getHeaderValue(WeaveHeader header) { 
      return getHeaderValue(header.getName()); 
    } 
 
    private String getHeaderValue(String headerName) { 
      for (Header header : m_headers) { 
        if (headerName.equals(header.getName())) 
          return header.getValue(); 
      } 
      return null
    } 
  } 
 
  @SuppressWarnings("serial"
  public static class WeaveResponseException extends HttpResponseException { 
 
    private final WeaveResponseHeaders m_responseHeaders; 
 
    public WeaveResponseException(int statusCode, String reasonPhrase, HttpResponse response) { 
//      super(statusCode, String.format("statusCode = %s ; reason = %s", statusCode, reasonPhrase)); 
      super(statusCode, reasonPhrase); 
      m_responseHeaders = new WeaveResponseHeaders(response); 
    } 
 
    public WeaveResponseHeaders getResponseHeaders() { 
      return m_responseHeaders; 
    } 
 
    @Override 
    public String toString() { 
      String s = getClass().getName(); 
      s += ": (statusCode=" + getStatusCode() + ")"
      String message = getLocalizedMessage(); 
      return (message != null) ? (s + " : " + message) : s; 
    } 
  } 
 
  /**
   * Based on BasicResponseHandler 
   */
 
  private static class MyResponseHandler implements ResponseHandler<WeaveResponse> { 
 
    /**
     * Returns the response body as a String if the response was successful (a 2xx status code). If no response body 
     * exists, this returns null. If the response was unsuccessful (>= 300 status code), throws an 
     * {@link org.apache.http.client.HttpResponseException}. 
     */
 
    public WeaveResponse handleResponse(final HttpResponse response) throws HttpResponseException, IOException { 
      StatusLine statusLine = response.getStatusLine(); 
      if (statusLine.getStatusCode() >= 300) { 
        throw new WeaveResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase(), response); 
      } 
      return new WeaveResponse(response); 
    } 
  } 
 
  private static class MyInterceptor implements HttpRequestInterceptor { 
 
    public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { 
      AuthState authState = (AuthState)context.getAttribute(ClientContext.TARGET_AUTH_STATE); 
      CredentialsProvider credsProvider = (CredentialsProvider)context.getAttribute(ClientContext.CREDS_PROVIDER); 
      HttpHost targetHost = (HttpHost)context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); 
      if (authState.getAuthScheme() == null) { 
        AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); 
        Credentials creds = credsProvider.getCredentials(authScope); 
        if (creds != null) { 
          authState.setAuthScheme(new BasicScheme()); 
          authState.setCredentials(creds); 
        } 
      } 
    } 
  } 
 
  /**
   * @author Patrick Woodworth 
   */
 
  static class WeaveHostnameVerifier extends AbstractVerifier { 
 
    public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException { 
      if (isIPAddress(host) && cns != null && cns.length > 0 && cns[0] != null) { 
        HashSet<String> expandedAlts = new HashSet<String>(); 
        resolveHostAddresses(cns[0], expandedAlts); 
        if (subjectAlts != null
          expandedAlts.addAll(Arrays.asList(subjectAlts)); 
        subjectAlts = expandedAlts.toArray(new String[expandedAlts.size()]); 
      } 
      verify(host, cns, subjectAlts, false); 
    } 
 
    private static void resolveHostAddresses(String cn, Collection<String> retval) { 
      try { 
        InetAddress[] addresses = InetAddress.getAllByName(cn); 
        for (InetAddress address : addresses) { 
          retval.add(address.getHostAddress()); 
        } 
      } catch (UnknownHostException e) { 
        Dbg.d(e); 
      } 
    } 
 
    private static boolean isIPAddress(final String hostname) { 
      return hostname != null && 
          (InetAddressUtils.isIPv4Address(hostname) || 
              InetAddressUtils.isIPv6Address(hostname)); 
    } 
  } 
}