lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From markrmil...@apache.org
Subject [06/26] lucene-solr:starburst: The Star Burst Upgrade - a work in progress - the branch gets replaced often.
Date Sun, 29 Jul 2018 15:08:28 GMT
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
new file mode 100644
index 0000000..5a06c66
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
@@ -0,0 +1,1143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.solrj.impl;
+
+import static org.apache.solr.common.util.Utils.getObjectByPath;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.invoke.MethodHandles;
+import java.net.ConnectException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpStatus;
+import org.apache.solr.client.solrj.ResponseParser;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.V2RequestSupport;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.request.RequestWriter;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.request.V2Request;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.SolrResponseBase;
+import org.apache.solr.client.solrj.response.UpdateResponse;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.QoSParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.UpdateParams;
+import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.ObjectReleaseTracker;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Authentication;
+import org.eclipse.jetty.client.api.AuthenticationStore;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Response.CompleteListener;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.client.util.BasicAuthentication;
+import org.eclipse.jetty.client.util.BufferingResponseListener;
+import org.eclipse.jetty.client.util.BytesContentProvider;
+import org.eclipse.jetty.client.util.InputStreamResponseListener;
+import org.eclipse.jetty.client.util.MultiPartContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: finish error handling, multiple streams, small HttpSolrClient features, basic auth, security, ssl, apiV2 ...
+
+/**
+ * @lucene.experimental
+ */
+public class Http2SolrClient extends SolrClient {
+
+  private static final String AGENT = "Solr[" + Http2SolrClient.class.getName() + "] 2.0";
+  
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private static final String UTF_8 = StandardCharsets.UTF_8.name();
+  private static final String POST = "POST";
+  private static final String PUT = "PUT";
+  private static final String GET = "GET";
+  private static final String DELETE = "DELETE";
+  private static final String HEAD = "HEAD";
+  
+  private static final String DEFAULT_PATH = "/select";
+
+  private static final List<String> errPath = Arrays.asList("metadata", "error-class");
+
+  private final SolrInternalHttpClient httpClient;
+
+  private volatile Set<String> queryParams = Collections.emptySet();
+
+  private final Phaser phaser = new Phaser(1) {
+    @Override
+    protected boolean onAdvance(int phase, int parties) {
+      return false;
+    }
+  };
+ // private final Semaphore available = new Semaphore(SolrInternalHttpClient.MAX_OUTSTANDING_REQUESTS, false); // nocommit: what about shared
+                                                                                      // instances?
+
+  private volatile ResponseParser parser = new BinaryResponseParser();
+  private volatile RequestWriter requestWriter = new BinaryRequestWriter();
+
+  private Request.QueuedListener requestQueuedListener = new Request.QueuedListener() {
+
+    @Override
+    public void onQueued(Request request) {
+      
+      phaser.register();
+      
+  //    try {
+  //      available.acquire();
+  //    } catch (InterruptedException e) {
+
+   //   }
+
+    }
+  };
+  
+  private CompleteListener completeListener = new CompleteListener() {
+    
+    @Override
+    public void onComplete(Result arg0) {
+    //  phaser.arriveAndDeregister();
+    }
+  };
+
+  private Request.BeginListener requestBeginListener = new Request.BeginListener() {
+
+    @Override
+    public void onBegin(Request arg0) {
+     
+    }
+  };
+
+  /**
+   * The URL of the Solr server.
+   */
+  private volatile String serverBaseUrl;
+
+  private boolean closeClient;
+
+  private Builder builder;
+
+  private volatile String closed = null;
+
+  protected Http2SolrClient(String serverBaseUrl, Builder builder) {
+    this.builder = builder;
+
+    if (!serverBaseUrl.equals("/") && serverBaseUrl.endsWith("/")) {
+      serverBaseUrl = serverBaseUrl.substring(0, serverBaseUrl.length() - 1);
+    }
+
+    if (serverBaseUrl.startsWith("//")) {
+      serverBaseUrl = serverBaseUrl.substring(1, serverBaseUrl.length());
+    }
+
+    this.serverBaseUrl = serverBaseUrl;
+
+    if (builder.responseParser != null) {
+      this.parser = builder.responseParser;
+    }
+
+    if (builder.httpClient == null) {
+      httpClient = new SolrInternalHttpClient(getClass().getSimpleName() + "-internal");
+      closeClient = true;
+    } else {
+      httpClient = builder.httpClient;
+    }
+    if (!httpClient.isStarted()) {
+      // Start HttpClient
+      try {
+        httpClient.start();
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    assert ObjectReleaseTracker.track(this);
+  }
+
+  // public static HttpClientTransport createClientTransport() {
+  // HTTP2Client transport = new HTTP2Client();
+  // // TODO
+  // transport.setSelectors(2);
+  // HttpClientTransportOverHTTP2 clientTransport = new HttpClientTransportOverHTTP2(transport);
+  // return clientTransport;
+  // }
+
+  public void close() throws IOException {
+    // TODO: cleanup
+    
+    
+    if (this.closed != null) {
+      throw new IllegalStateException("Already closed! " + this.closed);
+    }
+
+    waitForAsyncRequests(false);
+    
+    phaser.arriveAndDeregister();
+    
+    if (closeClient) {
+      IOUtils.closeQuietly(httpClient);
+    }
+
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    new RuntimeException("Already closed at: ").printStackTrace(pw);
+    this.closed = sw.toString();
+    
+    assert ObjectReleaseTracker.release(this);
+  }
+  
+  public SolrInternalHttpClient getHttpClient() {
+    return httpClient;
+  }
+  
+  public void waitForAsyncRequests() {
+    waitForAsyncRequests(true);
+  }
+
+  public void waitForAsyncRequests(boolean checkClosed) {
+    if (checkClosed && this.closed != null) {
+      throw new IllegalStateException("Already closed! " + this.closed);
+    }
+
+    // we wait for async requests, so far devs don't want to give sugar for this
+    logPhaser("waitForAsyncRequests-start");
+    try {
+
+      phaser.arriveAndAwaitAdvance();
+
+      // phaser.awaitAdvance(phaser.arriveAndDeregister());
+      // phaser.register();
+    } catch (IllegalStateException e) {
+      // nocommit
+      // work around something I don't understand - 1 party, 1 arrived, and then we arrive again - why isn't it a new
+      // phase?
+    }
+    logPhaser("waitForAsyncRequests-end");
+  }
+
+  private void logPhaser(String location) {
+    if (log.isDebugEnabled()) {
+      log.debug(" ------------ phaser -> {} {} t:{} i:{}", location, phaser, Thread.currentThread().getId(), this);
+    }
+  }
+
+  private boolean isV2ApiRequest(final SolrRequest request) {
+    return request instanceof V2Request || request.getPath().contains("/____v2");
+  }
+
+  public Http2ClientResponse request(SolrRequest request, String collection, OnComplete onComplete)
+      throws SolrServerException, IOException {
+    return request(request, collection, onComplete, false);
+  }
+
+  private Http2ClientResponse request(SolrRequest request, String collection, OnComplete onComplete,
+      boolean returnStream)
+      throws SolrServerException, IOException {
+    long startNanos = System.nanoTime();
+
+    ResponseParser parser = request.getResponseParser();
+
+    if (request instanceof V2RequestSupport) {
+      request = ((V2RequestSupport) request).getV2Request();
+      if (parser != null) {
+        request.setResponseParser(parser);
+      }
+    }
+
+    RequestWriter.ContentWriter contentWriter = requestWriter.getContentWriter(request);
+
+    Collection<ContentStream> streams = contentWriter == null ? requestWriter.getContentStreams(request) : null;
+
+    boolean isV2Api = isV2ApiRequest(request);
+
+    SolrParams params = request.getParams();
+
+    if (parser == null) {
+      parser = this.parser;
+    }
+
+    ModifiableSolrParams wparams = new ModifiableSolrParams(params);
+    if (parser != null) {
+      if (wparams.get(CommonParams.WT) == null) {
+        wparams.set(CommonParams.WT, parser.getWriterType());
+      }
+      wparams.set(CommonParams.VERSION, parser.getVersion());
+    }
+
+    String path = requestWriter.getPath(request);
+    if (path == null || !path.startsWith("/")) {
+      path = DEFAULT_PATH;
+    }
+
+    String basePath;
+    String requestBasePath = request.getBasePath();
+    if (requestBasePath != null) {
+      basePath = requestBasePath;
+    } else {
+      basePath = serverBaseUrl;
+    }
+
+    if (collection != null)
+      basePath += "/" + collection;
+
+    if (request instanceof V2Request) {
+      if (System.getProperty("solr.v2RealPath") == null) {
+        basePath = serverBaseUrl.replace("/solr", "/api");
+      } else {
+        basePath = serverBaseUrl + "/____v2";
+      }
+    }
+
+    try {
+      if (SolrRequest.METHOD.GET == request.getMethod() || SolrRequest.METHOD.DELETE == request.getMethod()) {
+        if (streams != null || contentWriter != null) {
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "GET and DELETE can't send streams!");
+        }
+        return getOrDelete(request, onComplete, startNanos, wparams, parser, path, basePath, returnStream, isV2Api);
+      } else if (SolrRequest.METHOD.POST == request.getMethod()) {
+
+        if (contentWriter != null) {
+          // "Content-Type", contentWriter.getContentType() request.
+          String url = basePath + path;
+          String fullQueryUrl = url + wparams.toQueryString();
+          ByteArrayOutputStream baos = new ByteArrayOutputStream();
+          contentWriter.write(baos);
+          Request req = newRequest(fullQueryUrl);
+          req.header("Content-Type", contentWriter.getContentType());
+          return postWithContent(request, req, onComplete, startNanos, fullQueryUrl, baos, parser, isV2Api);
+        }
+
+        String url = basePath + path;
+        // Collection<ContentStream> streams = requestWriter.getContentStreams(request);
+        // String url = basePath + path;
+        // boolean hasNullStreamName = false;
+        // if (streams != null) {
+        // for (ContentStream cs : streams) {
+        // if (cs.getName() == null) {
+        // hasNullStreamName = true;
+        // break;
+        // }
+        // }
+        // }
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        requestWriter.write(request, baos);
+
+        if (baos.size() == 0) {
+          // System.out.println("query params:" + request.getQueryParams());
+          // System.out.println("reg params:" + request.getParams());
+          // send server list and request list as query string params
+          ModifiableSolrParams queryParams = calculateQueryParams(this.queryParams, wparams);
+          queryParams.add(calculateQueryParams(request.getQueryParams(), wparams));
+          String fullQueryUrl = url + queryParams.toQueryString();
+          // System.out.println("POST WITH NO CONTENT:" + fullQueryUrl + " wparams:" + wparams);
+          // return postWithNoContentStream(request, onComplete, startNanos, wparams, fullQueryUrl, isV2Api);
+          Request req = newRequest(fullQueryUrl);
+          for (Entry<String,String[]> param : wparams) {
+            String key = param.getKey();
+            for (String value : param.getValue()) {
+              req.param(key, value);
+            }
+          }
+
+          return postWithContent(request, req, onComplete, startNanos, fullQueryUrl, baos, parser, isV2Api);
+        }
+
+        String fullQueryUrl = url + wparams.toQueryString();
+        Request req = newRequest(fullQueryUrl);
+
+        return postWithContent(request, req, onComplete, startNanos, fullQueryUrl, baos, parser, isV2Api);
+      }
+
+    } catch (InterruptedException e) { // TODO: finish error handling
+      throw new RuntimeException(e);
+    } catch (TimeoutException e) { // TODO: finish error handling
+      throw new SolrServerException(e.getMessage(), e);
+    } catch (ExecutionException e) {
+      Throwable cause = e.getCause();
+      // cause.printStackTrace();
+      // e.printStackTrace();
+      if (cause instanceof ConnectException) {
+        throw new SolrServerException("Server refused connection at: "
+            + getBaseURL(), cause);
+      }
+      if (cause instanceof SolrServerException) {
+        throw (SolrServerException) cause;
+      } else if (cause instanceof IOException) {
+        throw (IOException) cause;
+      }
+      throw new SolrServerException(cause.getMessage(), cause);
+    }
+
+    throw new UnsupportedOperationException(request.getMethod().toString());
+  }
+
+  private void addBasicAuth(String url, SolrRequest request) throws UnsupportedEncodingException {
+    if (request.getBasicAuthUser() != null && request.getBasicAuthPassword() != null) {
+      AuthenticationStore a = httpClient.getAuthenticationStore();
+      a.addAuthentication(
+          new BasicAuthentication(URI.create(url), Authentication.ANY_REALM, request.getBasicAuthUser(),
+              request.getBasicAuthPassword()));
+      // nocommit method.setHeader(new BasicHeader("Authorization", "Basic " + encoded));
+    }
+  }
+
+  private Request newRequest(String url) throws SolrServerException {
+    Request req;
+    try {
+      req = httpClient.newRequest(url);
+      for (Entry<String,String> entry : builder.headers.entrySet()) {
+        req.header(entry.getKey(), entry.getValue());
+      }
+    } catch (IllegalArgumentException e) {
+      throw new SolrServerException("Error parsing URL: " + url, e);
+    }
+    return req;
+  }
+
+  private Http2ClientResponse getOrDelete(SolrRequest request, OnComplete<SolrResponse> onComplete, long startNanos,
+      ModifiableSolrParams wparams, ResponseParser parser, String path, String basePath, boolean returnStream,
+      boolean isV2Api)
+      throws InterruptedException, TimeoutException, ExecutionException, SolrServerException {
+
+    String url = basePath + path + wparams.toQueryString();
+    Request req = newRequest(url);
+    req.header("User-Agent", AGENT);
+    req.idleTimeout(builder.idleConnectionTimeout, TimeUnit.MILLISECONDS);
+
+    if (SolrRequest.METHOD.GET == request.getMethod()) {
+      req.method(GET);
+    } else if (SolrRequest.METHOD.DELETE == request.getMethod()) {
+      req.method(DELETE);
+    }
+
+    if (onComplete == null) {
+      if (!returnStream) {
+
+        ContentResponse response = req.send();
+   
+        ByteArrayInputStream is = new ByteArrayInputStream(response.getContent());
+        NamedList<Object> rsp = processErrorsAndResponse(url, response, is, response.getEncoding(), parser, isV2Api);
+        Http2ClientResponse arsp = new Http2ClientResponse();
+        arsp.response = rsp;
+        return arsp;
+      } else {
+        InputStreamResponseListener listener = new InputStreamResponseListener();
+        req.send(listener);
+        // Wait for the response headers to arrive
+        Response response = listener.get(5, TimeUnit.SECONDS);
+        // nocommit : process response
+        Http2ClientResponse arsp = new Http2ClientResponse();
+
+        arsp.stream = listener.getInputStream();
+        return arsp;
+      }
+    } else {
+   
+      CountDownLatch latch = new CountDownLatch(1);
+
+      Http2ClientResponse arsp = new Http2ClientResponse();
+   
+      arsp.abortable = new Abortable() {
+        volatile SolrResponse rsp = null;
+
+        @Override
+        public void abort() {
+          req.abort(new RuntimeException("Aborted"));
+        }
+
+        @Override
+        public SolrResponse get() {
+          try {
+            latch.await();
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+          return rsp;
+        }
+
+        @Override
+        void setResponse(SolrResponse response) {
+          this.rsp = response;
+        }
+      };
+
+      BufferingResponseListener listener = new BufferingResponseListener() {
+
+        @Override
+        public void onComplete(Result result) {
+          // TODO: should we stream this?
+          try (InputStream ris = getContentAsInputStream()) {
+            NamedList<Object> rsp;
+            try {
+              rsp = processErrorsAndResponse(url, result.getResponse(), ris, getEncoding(), parser, isV2Api);
+            } catch (Exception e) {
+              onComplete.onFailure(e);
+              return;
+            }
+            SolrResponse res = makeResponse(request, rsp, result.getResponse().getStatus());
+            arsp.abortable.setResponse(res);
+            onComplete.onSuccess(res);
+          } catch (IOException e1) {
+            // TODO: handle right
+            throw new RuntimeException(e1);
+          } finally {
+            phaser.arriveAndDeregister();
+            latch.countDown();
+          }
+        }
+
+      };
+  
+      req.onRequestBegin(requestBeginListener).onRequestQueued(requestQueuedListener).onComplete(completeListener).send(listener);
+
+      return arsp;
+    }
+
+  }
+
+  private NamedList<Object> processErrorsAndResponse(String url, Response response, InputStream is, String encoding,
+      ResponseParser parser, boolean isV2Api)
+      throws SolrServerException {
+    // System.out.println("response headers:" + response.getHeaders());
+    // handle some http level checks before trying to parse the response
+    int httpStatus = response.getStatus();
+    switch (httpStatus) {
+      case HttpStatus.SC_OK:
+      case HttpStatus.SC_BAD_REQUEST:
+      case HttpStatus.SC_CONFLICT: // 409
+        break;
+      case HttpStatus.SC_MOVED_PERMANENTLY:
+      case HttpStatus.SC_MOVED_TEMPORARILY:
+        if (!httpClient.isFollowRedirects()) {
+          throw new SolrServerException("Server at " + getBaseURL()
+              + " sent back a redirect (" + httpStatus + ").");
+        }
+        break;
+      default:
+        if (encoding == null) {
+          encoding = parser.getContentType();
+        }
+        String msg = null;
+        if (!encoding.equalsIgnoreCase(parser.getContentType())) {
+          try {
+            is.reset();
+            msg = IOUtils.toString(is, encoding);
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+
+        if (encoding == null || encoding.length() == 0) {
+          try {
+            is.reset();
+            msg = IOUtils.toString(is, "UTF-8");
+
+          } catch (IOException e1) {
+            throw new RuntimeException(e1);
+          }
+        }
+        
+        if (msg != null) {
+          throw new RemoteSolrException(serverBaseUrl, httpStatus, "non ok status: " + httpStatus
+              + ", message:" + response.getReason() + " " + msg,
+              null);
+        }
+    }
+    NamedList<Object> rsp;
+    try {
+      rsp = parser.processResponse(is, encoding);
+    } catch (Exception e) {
+      try {
+        // nocommit
+        e.printStackTrace();
+        is.reset();
+        String msg = IOUtils.toString(is);
+        System.out.println("fail:" + msg + " " + response.getReason() + " " + httpStatus);
+      } catch (IOException e1) {
+        throw new RuntimeException(e1);
+      }
+      throw new RemoteSolrException(serverBaseUrl, httpStatus, "Parser: "+ parser.getClass().getSimpleName() + " Parse error: " + e.getMessage(), e);
+    }
+
+    Object error = rsp == null ? null : rsp.get("error");
+    if (error != null
+        && (isV2Api || String.valueOf(getObjectByPath(error, true, errPath)).endsWith("ExceptionWithErrObject"))) {
+      throw RemoteExecutionException.create(serverBaseUrl, rsp);
+    }
+
+    if (httpStatus != HttpStatus.SC_OK && !isV2Api) {
+      NamedList<String> metadata = null;
+      String reason = null;
+      try {
+        NamedList err = (NamedList) rsp.get("error");
+        if (err != null) {
+          reason = (String) err.get("msg");
+          if (reason == null) {
+            reason = (String) err.get("trace");
+          }
+          metadata = (NamedList<String>) err.get("metadata");
+        }
+      } catch (Exception ex) {}
+      if (reason == null) {
+        StringBuilder msg = new StringBuilder();
+        msg.append(response.getReason())
+            .append("\n\n")
+            .append("request: ")
+            .append(url);
+        try {
+          reason = java.net.URLDecoder.decode(msg.toString(), UTF_8);
+        } catch (UnsupportedEncodingException e) {
+          throw new RuntimeException(e);
+        }
+      }
+      RemoteSolrException rss = new RemoteSolrException(serverBaseUrl, httpStatus, reason, null);
+      if (metadata != null) rss.setMetadata(metadata);
+      throw rss;
+    }
+
+    return rsp;
+  }
+
+  private Http2ClientResponse postWithContent(SolrRequest request, Request req, OnComplete<SolrResponse> onComplete,
+      long startNanos,
+      String fullUrl, ByteArrayOutputStream baos, ResponseParser parser, boolean isV2Api)
+      throws InterruptedException, TimeoutException, ExecutionException, SolrServerException {
+    BytesContentProvider contentProvider = new BytesContentProvider(baos.toByteArray());
+
+    req.method(POST).agent(AGENT);
+    if (baos.size() > 0) {
+      req.content(contentProvider, requestWriter.getUpdateContentType());
+    } else {
+      req.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+    }
+    req.idleTimeout(builder.idleConnectionTimeout, TimeUnit.MILLISECONDS);
+    if (onComplete == null) {
+      ContentResponse response = req.send();
+      ByteArrayInputStream is = new ByteArrayInputStream(response.getContent());
+      NamedList<Object> rsp = processErrorsAndResponse(fullUrl, response, is, response.getEncoding(), parser, isV2Api);
+      Http2ClientResponse arsp = new Http2ClientResponse();
+      arsp.response = rsp;
+      return arsp;
+    } else {
+
+      CountDownLatch latch = new CountDownLatch(1);
+
+      Http2ClientResponse arsp = new Http2ClientResponse();
+ 
+      arsp.abortable = new Abortable() {
+        volatile SolrResponse rsp = null;
+
+        @Override
+        public void abort() {
+          boolean abort = req.abort(new RuntimeException("Aborted"));
+        }
+
+        @Override
+        public SolrResponse get() {
+          try {
+            latch.await();
+          } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+          }
+          return rsp;
+        }
+
+        @Override
+        void setResponse(SolrResponse response) {
+          this.rsp = response;
+        }
+      };
+      req.onRequestBegin(requestBeginListener).onRequestQueued(requestQueuedListener)
+      .onComplete(completeListener).send(new BufferingResponseListener() {
+
+            @Override
+            public void onComplete(Result result) {
+              NamedList<Object> rsp;
+              SolrResponse res = null;
+              try {
+                if (result.isFailed()) {
+                  onComplete.onFailure(result.getFailure());
+                  return;
+                }
+
+                try (InputStream ris = getContentAsInputStream()) {
+                  try {
+                    rsp = processErrorsAndResponse(fullUrl, result.getResponse(), ris, getEncoding(), parser, isV2Api);
+                    res = makeResponse(request, rsp, result.getResponse().getStatus());
+                    arsp.abortable.setResponse(res);
+                  } catch (Exception e) {
+                    onComplete.onFailure(e);
+                    return;
+                  }
+                  onComplete.onSuccess(res);
+
+                } catch (IOException e1) {
+                  log.warn("Could not close InputStream.", e1);
+                }
+              } finally {
+                latch.countDown();
+                phaser.arriveAndDeregister();
+              }
+            }
+
+          });
+      return arsp;
+    }
+  }
+
+  private Http2ClientResponse postWithMultipleContentStreams(SolrRequest request, OnComplete<SolrResponse> onComplete,
+      long startNanos,
+      ModifiableSolrParams wparams, String url, boolean isV2Api)
+      throws InterruptedException, TimeoutException, ExecutionException, SolrServerException {
+
+    Request req = newRequest(url);
+    req.agent(AGENT).method(POST);
+    req.idleTimeout(builder.idleConnectionTimeout, TimeUnit.MILLISECONDS);
+    Iterator<String> iter = wparams.getParameterNamesIterator();
+
+    // encode params in request content
+    // TODO: support leaving some params as query params like HttpSolrClient
+    // nocommit SolrRequestParsers puts this in a tmp file!!
+    MultiPartContentProvider multiPart = new MultiPartContentProvider();
+    try {
+      while (iter.hasNext()) {
+        String p = iter.next();
+        String[] vals = wparams.getParams(p);
+        if (vals != null) {
+          for (String v : vals) {
+            // nocommit System.out.println("Add multi-part: " + p + " : " + v);
+            multiPart.addFieldPart(p, new StringContentProvider(v), null);
+          }
+        }
+      }
+      if (multiPart.getLength() > 1) {
+        req.header("Content-Type", "multipart/form-data");
+      }
+    } finally {
+      multiPart.close();
+    }
+
+    req = req.content(multiPart);
+    if (onComplete == null) {
+      ContentResponse response;
+      response = req.send();
+
+      NamedList<Object> rsp = processErrorsAndResponse(serverBaseUrl, response,
+          new ByteArrayInputStream(response.getContent()),
+          response.getEncoding(), parser, isV2Api);
+      Http2ClientResponse arsp = new Http2ClientResponse();
+      arsp.response = rsp;
+      return arsp;
+
+    } else {
+      // TODO: there is like a 2mb buffer limit?
+      req.onRequestBegin(requestBeginListener).onRequestQueued(requestQueuedListener)
+      .onComplete(completeListener).send(new BufferingResponseListener() {
+            @Override
+            public void onComplete(Result result) {
+              try {
+                if (result.isFailed()) { // nocommit how to handle right
+                  onComplete.onFailure(result.getFailure());
+                  return;
+                }
+
+                try (InputStream ris = getContentAsInputStream()) {
+                  NamedList<Object> rsp;
+                  try {
+                    rsp = processErrorsAndResponse(serverBaseUrl, result.getResponse(), ris, getEncoding(), parser,
+                        isV2Api);
+                  } catch (Exception e) {
+                    onComplete.onFailure(e);
+                    return;
+                  }
+
+                  SolrResponse resp = makeResponse(request, rsp, result.getResponse().getStatus());
+                  onComplete.onSuccess(resp);
+                } catch (IOException e1) {
+                  // TODO: handle right
+                  throw new RuntimeException(e1);
+                }
+              } finally {
+                phaser.arriveAndDeregister();
+             //   available.release();
+              }
+            }
+          });
+      return null;
+    }
+  }
+
+  @Override
+  public NamedList<Object> request(SolrRequest request, String collection) throws SolrServerException, IOException {
+    return request(request, collection, null).response;
+  }
+
+  // sample new async method
+  public void add(String collection, SolrInputDocument doc, int commitWithinMs, OnComplete<UpdateResponse> onComplete)
+      throws SolrServerException, IOException {
+
+    UpdateRequest req = new UpdateRequest();
+    req.add(doc);
+    req.setCommitWithin(commitWithinMs);
+    request(req, collection, onComplete);
+  }
+
+  public static int HEAD(String url) throws InterruptedException, ExecutionException, TimeoutException {
+    ContentResponse response;
+    try (SolrInternalHttpClient httpClient = new SolrInternalHttpClient(Http2SolrClient.class.getSimpleName() + "-HEAD-internal", true)) {
+      Request req = httpClient.newRequest(URI.create(url));
+      response = req.method(HEAD).send();
+      if (response.getStatus() != 200) {
+        throw new RemoteSolrException(url, response.getStatus(), response.getReason(), null);
+      }
+    }
+    return response.getStatus();
+  }
+
+  public static class SimpleResponse {
+    public String asString;
+    public String contentType;
+    public int size;
+    public int status;
+    public byte[] bytes;
+  }
+
+  // inefficient
+  public static SimpleResponse GET(String url) throws InterruptedException, ExecutionException, TimeoutException {
+    try (SolrInternalHttpClient httpClient = new SolrInternalHttpClient(Http2SolrClient.class.getSimpleName() + "-GET-internal", true)) {
+      SimpleResponse resp = GET(url, httpClient);
+      return resp;
+    }
+  }
+
+  public static SimpleResponse GET(String url, HttpClient httpClient)
+      throws InterruptedException, ExecutionException, TimeoutException {
+    return doGet(url, httpClient);
+  }
+
+  public static SimpleResponse POST(String url, HttpClient httpClient, byte[] bytes, String contentType)
+      throws InterruptedException, ExecutionException, TimeoutException {
+    return doPost(url, httpClient, bytes, contentType, Collections.emptyMap());
+  }
+
+  public SimpleResponse httpGet(String url) throws InterruptedException, ExecutionException, TimeoutException {
+    return doGet(url, httpClient);
+  }
+
+  private static SimpleResponse doGet(String url, HttpClient httpClient)
+      throws InterruptedException, ExecutionException, TimeoutException {
+    // nocommit
+    assert url != null;
+    ContentResponse response = httpClient.GET(url);
+    SimpleResponse sResponse = new SimpleResponse();
+    sResponse.asString = response.getContentAsString();
+    sResponse.contentType = response.getEncoding();
+    sResponse.size = response.getContent().length;
+    sResponse.bytes = response.getContent();
+    sResponse.status = response.getStatus();
+    return sResponse;
+  }
+
+  public String httpDelete(String url) throws InterruptedException, ExecutionException, TimeoutException {
+    ContentResponse response = httpClient.newRequest(URI.create(url)).method(DELETE).send();
+    return response.getContentAsString();
+  }
+
+  public SimpleResponse httpPost(String url, byte[] bytes, String contentType)
+      throws InterruptedException, ExecutionException, TimeoutException {
+    return httpPost(url, bytes, contentType, Collections.emptyMap());
+  }
+
+  public SimpleResponse httpPost(String url, byte[] bytes, String contentType, Map<String,String> headers)
+      throws InterruptedException, ExecutionException, TimeoutException {
+    return doPost(url, httpClient, bytes, contentType, headers);
+  }
+
+  private static SimpleResponse doPost(String url, HttpClient httpClient, byte[] bytes, String contentType,
+      Map<String,String> headers) throws InterruptedException, ExecutionException, TimeoutException {
+    Request req = httpClient.newRequest(url).method(POST).content(new BytesContentProvider(contentType, bytes));
+    for (Entry<String,String> entry : headers.entrySet()) {
+      req.header(entry.getKey(), entry.getValue());
+    }
+    ContentResponse response = req.send();
+    SimpleResponse sResponse = new SimpleResponse();
+    sResponse.asString = response.getContentAsString();
+    sResponse.contentType = response.getEncoding();
+    sResponse.size = response.getContent().length;
+    sResponse.status = response.getStatus();
+    return sResponse;
+  }
+
+  public String httpPut(String url, byte[] bytes, String contentType)
+      throws InterruptedException, ExecutionException, TimeoutException, SolrServerException {
+    ContentResponse response = newRequest(url).method(PUT).content(new BytesContentProvider(bytes), contentType).send();
+    return response.getContentAsString();
+  }
+
+  // sample new async method
+  public void query(String collection, SolrParams params, OnComplete<QueryResponse> onComplete)
+      throws SolrServerException, IOException {
+    QueryRequest queryRequest = new QueryRequest(params);
+    request(queryRequest, collection, onComplete);
+  }
+
+  public abstract class Abortable {
+    public abstract void abort();
+
+    public abstract SolrResponse get();
+
+    abstract void setResponse(SolrResponse response);
+  }
+
+  public Abortable abortableRequest(SolrRequest request, OnComplete<SolrResponseBase> onComplete)
+      throws SolrServerException, IOException {
+    assert onComplete != null;
+    Http2ClientResponse rsp = request(request, null, onComplete);
+    return rsp.abortable;
+  }
+
+  public InputStream queryAndStreamResponse(String collection, SolrParams params)
+      throws SolrServerException, IOException {
+    QueryRequest queryRequest = new QueryRequest(params);
+    Http2ClientResponse resp = request(queryRequest, collection, null, true);
+    assert resp.stream != null;
+    return resp.stream;
+  }
+
+  public void commit(String collection, boolean softCommit, boolean waitSearcher, OnComplete<SolrResponse> onComplete)
+      throws SolrServerException, IOException {
+    UpdateRequest req = new UpdateRequest();
+    ModifiableSolrParams params = new ModifiableSolrParams();
+
+    params.set(UpdateParams.COMMIT, "true");
+    params.set(UpdateParams.SOFT_COMMIT, String.valueOf(softCommit));
+
+    params.set(UpdateParams.WAIT_SEARCHER, String.valueOf(waitSearcher));
+    req.setParams(params);
+
+    request(req, collection, onComplete);
+  }
+
+  public void setRequestWriter(RequestWriter requestWriter) {
+    this.requestWriter = requestWriter;
+  }
+
+  private SolrResponse makeResponse(SolrRequest request, NamedList<Object> rsp, int status) {
+    long startNanos = System.nanoTime();
+    SolrResponse res = request.createResponse(Http2SolrClient.this);
+    if (rsp.get("responseHeader") != null) {
+      int index = ((NamedList) rsp.get("responseHeader")).indexOf("status", 0);
+      ((NamedList) rsp.get("responseHeader")).setVal(index, status);
+    }
+    res.setResponse(rsp);
+    long endNanos = System.nanoTime();
+    res.setElapsedTime(TimeUnit.NANOSECONDS.toMillis(endNanos - startNanos));
+    return res;
+  }
+
+  public static interface OnComplete<SolrResponse> {
+    public void onSuccess(SolrResponse result);
+
+    public void onFailure(Throwable e);
+  }
+
+  public void setFollowRedirects(boolean follow) {
+    httpClient.setFollowRedirects(follow);
+  }
+
+  public String getBaseURL() {
+    return serverBaseUrl;
+  }
+
+  public void setBaseURL(String serverBaseUrl) {
+    this.serverBaseUrl = serverBaseUrl;
+  }
+
+  public static class Builder {
+
+    private SolrInternalHttpClient httpClient;
+    private int idleConnectionTimeout = SolrInternalHttpClient.DEFAULT_IDLE_TIMEOUT;
+    private boolean useHttp1_1 = false;
+    private String baseSolrUrl;
+    private Map<String,String> headers = new HashMap<>();
+    private ResponseParser responseParser;
+
+    public Builder(String baseSolrUrl) {
+      this.baseSolrUrl = baseSolrUrl;
+    }
+
+    public Http2SolrClient build() {
+      return new Http2SolrClient(baseSolrUrl, this);
+    }
+
+    public Builder withHttpClient(SolrInternalHttpClient httpClient) {
+      this.httpClient = httpClient;
+      return this;
+    }
+
+    public Builder withHeader(String header, String value) {
+      this.headers.put(header, value);
+      return this;
+    }
+
+    public Builder withHeaders(Map<String,String> headers) {
+      this.headers.putAll(headers);
+      return this;
+    }
+
+    public Builder solrInternal() {
+      this.headers.put(QoSParams.REQUEST_SOURCE, QoSParams.INTERNAL);
+      return this;
+    }
+
+    public Builder withResponseParser(ResponseParser parser) {
+      this.responseParser = parser;
+      return this;
+    }
+
+    public Builder idleConnectionTimeout(int idleConnectionTimeout) {
+      this.idleConnectionTimeout = idleConnectionTimeout;
+      return this;
+    }
+
+    public Builder useHttp1_1(boolean useHttp1_1) {
+      this.useHttp1_1 = useHttp1_1;
+      return this;
+    }
+  }
+
+  /**
+   * Subclass of SolrException that allows us to capture an arbitrary HTTP status code that may have been returned by
+   * the remote server or a proxy along the way.
+   */
+  public static class RemoteSolrException extends SolrException {
+    /**
+     * @param remoteHost
+     *          the host the error was received from
+     * @param code
+     *          Arbitrary HTTP status code
+     * @param msg
+     *          Exception Message
+     * @param th
+     *          Throwable to wrap with this Exception
+     */
+    public RemoteSolrException(String remoteHost, int code, String msg, Throwable th) {
+      super(code, "Error from server at " + remoteHost + ": " + msg, th);
+    }
+  }
+
+  /**
+   * This should be thrown when a server has an error in executing the request and it sends a proper payload back to the
+   * client
+   */
+  public static class RemoteExecutionException extends RemoteSolrException {
+    private NamedList meta;
+
+    public RemoteExecutionException(String remoteHost, int code, String msg, NamedList meta) {
+      super(remoteHost, code, msg, null);
+      this.meta = meta;
+    }
+
+    public static RemoteExecutionException create(String host, NamedList errResponse) {
+      Object errObj = errResponse.get("error");
+      if (errObj != null) {
+        Number code = (Number) getObjectByPath(errObj, true, Collections.singletonList("code"));
+        String msg = (String) getObjectByPath(errObj, true, Collections.singletonList("msg"));
+        return new RemoteExecutionException(host, code == null ? ErrorCode.UNKNOWN.code : code.intValue(),
+            msg == null ? "Unknown Error" : msg, errResponse);
+
+      } else {
+        throw new RuntimeException("No error");
+      }
+    }
+
+    public NamedList getMetaData() {
+      return meta;
+    }
+  }
+
+  private static class Http2ClientResponse {
+    NamedList response;
+    InputStream stream;
+    Abortable abortable;
+  }
+
+  public void setQueryParams(Set<String> queryParams) {
+    this.queryParams = queryParams;
+  }
+
+  protected ModifiableSolrParams calculateQueryParams(Set<String> queryParamNames,
+      ModifiableSolrParams wparams) {
+    ModifiableSolrParams queryModParams = new ModifiableSolrParams();
+    if (queryParamNames != null) {
+      for (String param : queryParamNames) {
+        String[] value = wparams.getParams(param);
+        if (value != null) {
+          for (String v : value) {
+            queryModParams.add(param, v);
+          }
+          wparams.remove(param);
+        }
+      }
+    }
+    return queryModParams;
+  }
+
+  public ResponseParser getParser() {
+    return parser;
+  }
+
+  public void setParser(ResponseParser parser) {
+    this.parser = parser;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
index deb8fbc..ead501b 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
@@ -26,12 +26,12 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.HttpSolrClient.RemoteSolrException;
+import org.apache.solr.client.solrj.impl.Http2SolrClient.RemoteSolrException;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
 import org.apache.solr.common.cloud.Aliases;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.ClusterState.CollectionRef;
@@ -40,6 +40,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.Utils;
+import org.eclipse.jetty.client.HttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,14 +55,12 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
 
   private int cacheTimeout = 5; // the liveNodes and aliases cache will be invalidated after 5 secs
   final HttpClient httpClient;
-  final boolean clientIsInternal;
 
-  public HttpClusterStateProvider(List<String> solrUrls, HttpClient httpClient) throws Exception {
-    this.httpClient = httpClient == null? HttpClientUtil.createClient(null): httpClient;
-    this.clientIsInternal = httpClient == null;
+  public HttpClusterStateProvider(List<String> solrUrls, SolrInternalHttpClient httpClient) throws Exception {
+    this.httpClient = httpClient;
     for (String solrUrl: solrUrls) {
       urlScheme = solrUrl.startsWith("https")? "https": "http";
-      try (SolrClient initialClient = new HttpSolrClient.Builder().withBaseSolrUrl(solrUrl).withHttpClient(httpClient).build()) {
+      try (SolrClient initialClient = new Http2SolrClient.Builder(solrUrl).withHttpClient(httpClient).build()) {
         Set<String> liveNodes = fetchLiveNodes(initialClient); // throws exception if unable to fetch
         this.liveNodes = liveNodes;
         liveNodesTimestamp = System.nanoTime();
@@ -82,20 +81,16 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
 
   @Override
   public void close() throws IOException {
-    if (this.clientIsInternal && this.httpClient != null) {
-      HttpClientUtil.close(httpClient);
-    }
+
   }
 
   @Override
   public CollectionRef getState(String collection) {
     for (String nodeName: liveNodes) {
-      try (HttpSolrClient client = new HttpSolrClient.Builder().
-          withBaseSolrUrl(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).
-          withHttpClient(httpClient).build()) {
+      try (Http2SolrClient client = new Http2SolrClient.Builder(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).build()) {
         ClusterState cs = fetchClusterState(client, collection, null);
         return cs.getCollectionRef(collection);
-      } catch (SolrServerException | RemoteSolrException | IOException e) {
+      } catch (Exception e) {
         if (e.getMessage().contains(collection + " not found")) {
           // Cluster state for the given collection was not found.
           // Lets fetch/update our aliases:
@@ -161,9 +156,7 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
     }
     if (TimeUnit.SECONDS.convert((System.nanoTime() - liveNodesTimestamp), TimeUnit.NANOSECONDS) > getCacheTimeout()) {
       for (String nodeName: liveNodes) {
-        try (HttpSolrClient client = new HttpSolrClient.Builder().
-            withBaseSolrUrl(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).
-            withHttpClient(httpClient).build()) {
+        try (Http2SolrClient client = new Http2SolrClient.Builder(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).build()) {
           Set<String> liveNodes = fetchLiveNodes(client);
           this.liveNodes = (liveNodes);
           liveNodesTimestamp = System.nanoTime();
@@ -210,9 +203,7 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
     if (forceFetch || this.aliases == null ||
         TimeUnit.SECONDS.convert((System.nanoTime() - aliasesTimestamp), TimeUnit.NANOSECONDS) > getCacheTimeout()) {
       for (String nodeName: liveNodes) {
-        try (HttpSolrClient client = new HttpSolrClient.Builder().
-            withBaseSolrUrl(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).
-            withHttpClient(httpClient).build()) {
+        try (Http2SolrClient client = new Http2SolrClient.Builder(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).build()) {
 
           Map<String, List<String>> aliases = new CollectionAdminRequest.ListAliases().process(client).getAliasesAsLists();
           this.aliases = aliases;
@@ -245,9 +236,7 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
   @Override
   public ClusterState getClusterState() throws IOException {
     for (String nodeName: liveNodes) {
-      try (HttpSolrClient client = new HttpSolrClient.Builder().
-          withBaseSolrUrl(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).
-          withHttpClient(httpClient).build()) {
+      try (Http2SolrClient client = new Http2SolrClient.Builder(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).build()) {
         ClusterState cs = fetchClusterState(client, null, null);
         return cs;
       } catch (SolrServerException | RemoteSolrException | IOException e) {
@@ -265,9 +254,7 @@ public class HttpClusterStateProvider implements ClusterStateProvider {
   @Override
   public Map<String, Object> getClusterProperties() {
     for (String nodeName: liveNodes) {
-      try (HttpSolrClient client = new HttpSolrClient.Builder().
-          withBaseSolrUrl(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).
-          withHttpClient(httpClient).build()) {
+      try (Http2SolrClient client = new Http2SolrClient.Builder(Utils.getBaseUrlForNodeName(nodeName, urlScheme)).build()) {
         Map<String, Object> clusterProperties = new HashMap<>();
         fetchClusterState(client, null, clusterProperties);
         return clusterProperties;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
index 2b60e33..a7a72c5 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.client.solrj.impl;
 
+import static org.apache.solr.common.util.Utils.getObjectByPath;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -85,8 +87,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
-import static org.apache.solr.common.util.Utils.getObjectByPath;
-
 /**
  * A SolrClient implementation that talks directly to a Solr server via HTTP
  */
@@ -135,7 +135,7 @@ public class HttpSolrClient extends SolrClient {
    */
   protected volatile RequestWriter requestWriter = new BinaryRequestWriter();
   
-  private final HttpClient httpClient;
+  private HttpClient httpClient = null;
   
   private volatile Boolean followRedirects = false;
   
@@ -152,7 +152,7 @@ public class HttpSolrClient extends SolrClient {
   @Deprecated
   protected HttpSolrClient(String baseURL, HttpClient client, ResponseParser parser, boolean allowCompression) {
     this(new Builder(baseURL)
-        .withHttpClient(client)
+        //.withHttpClient(client)
         .withResponseParser(parser)
         .allowCompression(allowCompression));
   }
@@ -172,7 +172,7 @@ public class HttpSolrClient extends SolrClient {
   protected HttpSolrClient(String baseURL, HttpClient client, ResponseParser parser, boolean allowCompression,
       ModifiableSolrParams invariantParams) {
     this(new Builder(baseURL)
-        .withHttpClient(client)
+        //.withHttpClient(client)
         .withResponseParser(parser)
         .allowCompression(allowCompression)
         .withInvariantParams(invariantParams));
@@ -190,7 +190,7 @@ public class HttpSolrClient extends SolrClient {
     }
     
     if (builder.httpClient != null) {
-      this.httpClient = builder.httpClient;
+     // this.httpClient = builder.httpClient;
       this.internalClient = false;
     } else {
       this.internalClient = true;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java
index 6c2737d..2a88155 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttpSolrClient.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.client.solrj.impl;
 
+import static org.apache.solr.common.params.CommonParams.ADMIN_PATHS;
+
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.net.ConnectException;
@@ -37,26 +39,25 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.http.client.HttpClient;
+import org.apache.commons.io.IOUtils;
 import org.apache.solr.client.solrj.ResponseParser;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.HttpSolrClient.RemoteExecutionException;
+import org.apache.solr.client.solrj.impl.Http2SolrClient.RemoteExecutionException;
 import org.apache.solr.client.solrj.request.IsUpdateRequest;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.QoSParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SolrjNamedThreadFactory;
 import org.slf4j.MDC;
 
-import static org.apache.solr.common.params.CommonParams.ADMIN_PATHS;
-
 /**
  * LBHttpSolrClient or "LoadBalanced HttpSolrClient" is a load balancing wrapper around
  * {@link HttpSolrClient}. This is useful when you
@@ -113,13 +114,14 @@ public class LBHttpSolrClient extends SolrClient {
 
   // changes to aliveServers are reflected in this array, no need to synchronize
   private volatile ServerWrapper[] aliveServerList = new ServerWrapper[0];
-
+  
+  private Set<SolrClient> clients = new ConcurrentHashMap<>().newKeySet();
 
   private ScheduledExecutorService aliveCheckExecutor;
 
-  private final HttpClient httpClient;
-  private final boolean clientIsInternal;
-  private HttpSolrClient.Builder httpSolrClientBuilder;
+  private SolrInternalHttpClient httpClient;
+  private boolean clientIsInternal;
+  private Http2SolrClient.Builder httpSolrClientBuilder;
   private final AtomicInteger counter = new AtomicInteger(-1);
 
   private static final SolrQuery solrQuery = new SolrQuery("*:*");
@@ -131,6 +133,8 @@ public class LBHttpSolrClient extends SolrClient {
 
   private Integer soTimeout;
 
+  private Builder builder;
+
   static {
     solrQuery.setRows(0);
     /**
@@ -147,7 +151,7 @@ public class LBHttpSolrClient extends SolrClient {
 
   protected static class ServerWrapper {
 
-    final HttpSolrClient client;
+    final Http2SolrClient client;
 
     // "standard" servers are used by default.  They normally live in the alive list
     // and move to the zombie list when unavailable.  When they become available again,
@@ -156,7 +160,7 @@ public class LBHttpSolrClient extends SolrClient {
 
     int failedPings = 0;
 
-    public ServerWrapper(HttpSolrClient client) {
+    public ServerWrapper(Http2SolrClient client) {
       this.client = client;
     }
 
@@ -243,8 +247,8 @@ public class LBHttpSolrClient extends SolrClient {
    * @deprecated use {@link LBHttpSolrClient#LBHttpSolrClient(Builder)} instead, as it is a more extension/subclassing-friendly alternative
    */
   @Deprecated
-  protected LBHttpSolrClient(HttpSolrClient.Builder httpSolrClientBuilder,
-                          HttpClient httpClient, String... solrServerUrl) {
+  protected LBHttpSolrClient(Http2SolrClient.Builder httpSolrClientBuilder,
+      SolrInternalHttpClient httpClient, String... solrServerUrl) {
     this(new Builder()
         .withHttpSolrClientBuilder(httpSolrClientBuilder)
         .withHttpClient(httpClient)
@@ -257,7 +261,7 @@ public class LBHttpSolrClient extends SolrClient {
    * @deprecated use {@link LBHttpSolrClient#LBHttpSolrClient(Builder)} instead, as it is a more extension/subclassing-friendly alternative
    */
   @Deprecated
-  protected LBHttpSolrClient(HttpClient httpClient, ResponseParser parser, String... solrServerUrl) {
+  protected LBHttpSolrClient(SolrInternalHttpClient httpClient, ResponseParser parser, String... solrServerUrl) {
     this(new Builder()
         .withBaseSolrUrls(solrServerUrl)
         .withResponseParser(parser)
@@ -265,9 +269,10 @@ public class LBHttpSolrClient extends SolrClient {
   }
 
   protected LBHttpSolrClient(Builder builder) {
+    if (builder == null) return;
     this.clientIsInternal = builder.httpClient == null;
     this.httpSolrClientBuilder = builder.httpSolrClientBuilder;
-    this.httpClient = builder.httpClient == null ? constructClient(builder.baseSolrUrls.toArray(new String[builder.baseSolrUrls.size()])) : builder.httpClient;
+    this.httpClient = builder.httpClient == null ? new SolrInternalHttpClient(getClass().getSimpleName()+ "-internal") : builder.httpClient;
     this.connectionTimeout = builder.connectionTimeoutMillis;
     this.soTimeout = builder.socketTimeoutMillis;    
     this.parser = builder.responseParser;
@@ -279,18 +284,19 @@ public class LBHttpSolrClient extends SolrClient {
       }
     }
     updateAliveList();
+    this.builder = builder;
   }
 
-  private HttpClient constructClient(String[] solrServerUrl) {
-    ModifiableSolrParams params = new ModifiableSolrParams();
-    if (solrServerUrl != null && solrServerUrl.length > 1) {
-      // we prefer retrying another server
-      params.set(HttpClientUtil.PROP_USE_RETRY, false);
-    } else {
-      params.set(HttpClientUtil.PROP_USE_RETRY, true);
-    }
-    return HttpClientUtil.createClient(params);
-  }
+//  private HttpClient constructClient(String[] solrServerUrl) {
+//    ModifiableSolrParams params = new ModifiableSolrParams();
+//    if (solrServerUrl != null && solrServerUrl.length > 1) {
+//      // we prefer retrying another server
+//      params.set(HttpClientUtil.PROP_USE_RETRY, false);
+//    } else {
+//      params.set(HttpClientUtil.PROP_USE_RETRY, true);
+//    }
+//    return HttpClientUtil.createClient(params);
+//  }
 
   public Set<String> getQueryParams() {
     return queryParams;
@@ -313,40 +319,61 @@ public class LBHttpSolrClient extends SolrClient {
     return server;
   }
 
-  protected HttpSolrClient makeSolrClient(String server) {
-    HttpSolrClient client;
+  protected Http2SolrClient makeSolrClient(String server) {
+//    HttpSolrClient client;
+//    if (httpSolrClientBuilder != null) {
+//      synchronized (this) {
+//        httpSolrClientBuilder
+//            .withBaseSolrUrl(server)
+//            .withHttpClient(httpClient);
+//        if (connectionTimeout != null) {
+//          httpSolrClientBuilder.withConnectionTimeout(connectionTimeout);
+//        }
+//        if (soTimeout != null) {
+//          httpSolrClientBuilder.withSocketTimeout(soTimeout);
+//        }
+//        client = httpSolrClientBuilder.build();
+//      }
+//    } else {
+//      final HttpSolrClient.Builder clientBuilder = new HttpSolrClient.Builder(server)
+//          .withHttpClient(httpClient)
+//          .withResponseParser(parser);
+//      if (connectionTimeout != null) {
+//        clientBuilder.withConnectionTimeout(connectionTimeout);
+//      }
+//      if (soTimeout != null) {
+//        clientBuilder.withSocketTimeout(soTimeout);
+//      }
+//      client = clientBuilder.build();
+//    }
+//    if (requestWriter != null) {
+//      client.setRequestWriter(requestWriter);
+//    }
+//    if (queryParams != null) {
+//      client.setQueryParams(queryParams);
+//    }
+    
     if (httpSolrClientBuilder != null) {
-      synchronized (this) {
-        httpSolrClientBuilder
-            .withBaseSolrUrl(server)
-            .withHttpClient(httpClient);
-        if (connectionTimeout != null) {
-          httpSolrClientBuilder.withConnectionTimeout(connectionTimeout);
-        }
-        if (soTimeout != null) {
-          httpSolrClientBuilder.withSocketTimeout(soTimeout);
-        }
-        client = httpSolrClientBuilder.build();
+      if (builder != null) {
+        httpSolrClientBuilder.withHeaders(builder.headers);
       }
+      Http2SolrClient client = httpSolrClientBuilder.withHttpClient(httpClient).build();
+      clients.add(client);
+      return client;
     } else {
-      final HttpSolrClient.Builder clientBuilder = new HttpSolrClient.Builder(server)
-          .withHttpClient(httpClient)
-          .withResponseParser(parser);
-      if (connectionTimeout != null) {
-        clientBuilder.withConnectionTimeout(connectionTimeout);
-      }
-      if (soTimeout != null) {
-        clientBuilder.withSocketTimeout(soTimeout);
+      try {
+        System.out.println("builder:" + builder );
+        Http2SolrClient.Builder clientBuilder = new Http2SolrClient.Builder(server).withHttpClient(httpClient);
+        if (builder != null) {
+          clientBuilder.withHeaders(builder.headers);
+        }
+        Http2SolrClient client = clientBuilder.build();
+        clients.add(client);
+        return client;
+      } catch (Exception e) {
+        throw new RuntimeException(e);
       }
-      client = clientBuilder.build();
-    }
-    if (requestWriter != null) {
-      client.setRequestWriter(requestWriter);
-    }
-    if (queryParams != null) {
-      client.setQueryParams(queryParams);
     }
-    return client;
   }
 
   /**
@@ -407,7 +434,7 @@ public class LBHttpSolrClient extends SolrClient {
           break;
         }
 
-        HttpSolrClient client = makeSolrClient(serverStr);
+        Http2SolrClient client = makeSolrClient(serverStr);
 
         ++numServersTried;
         ex = doRequest(client, req, rsp, isNonRetryable, false, null);
@@ -464,7 +491,7 @@ public class LBHttpSolrClient extends SolrClient {
 
   }
 
-  protected Exception addZombie(HttpSolrClient server, Exception e) {
+  protected Exception addZombie(Http2SolrClient server, Exception e) {
 
     ServerWrapper wrapper;
 
@@ -475,7 +502,7 @@ public class LBHttpSolrClient extends SolrClient {
     return e;
   }  
 
-  protected Exception doRequest(HttpSolrClient client, Req req, Rsp rsp, boolean isNonRetryable,
+  protected Exception doRequest(Http2SolrClient client, Req req, Rsp rsp, boolean isNonRetryable,
       boolean isZombie, String zombieKey) throws SolrServerException, IOException {
     Exception ex = null;
     try {
@@ -550,7 +577,7 @@ public class LBHttpSolrClient extends SolrClient {
   }
 
   public void addSolrServer(String server) throws MalformedURLException {
-    HttpSolrClient client = makeSolrClient(server);
+    Http2SolrClient client = makeSolrClient(server);
     addToAlive(new ServerWrapper(client));
   }
 
@@ -580,12 +607,12 @@ public class LBHttpSolrClient extends SolrClient {
     synchronized (aliveServers) {
       Iterator<ServerWrapper> wrappersIt = aliveServers.values().iterator();
       while (wrappersIt.hasNext()) {
-        wrappersIt.next().client.setConnectionTimeout(timeout);
+       // wrappersIt.next().client.setConnectionTimeout(timeout);
       }
     }
     Iterator<ServerWrapper> wrappersIt = zombieServers.values().iterator();
     while (wrappersIt.hasNext()) {
-      wrappersIt.next().client.setConnectionTimeout(timeout);
+      //wrappersIt.next().client.setConnectionTimeout(timeout);
     }
   }
 
@@ -601,23 +628,54 @@ public class LBHttpSolrClient extends SolrClient {
     synchronized (aliveServers) {
       Iterator<ServerWrapper> wrappersIt = aliveServers.values().iterator();
       while (wrappersIt.hasNext()) {
-        wrappersIt.next().client.setSoTimeout(timeout);
+        //wrappersIt.next().client.setSoTimeout(timeout);
       }
     }
     Iterator<ServerWrapper> wrappersIt = zombieServers.values().iterator();
     while (wrappersIt.hasNext()) {
-      wrappersIt.next().client.setSoTimeout(timeout);
+      //wrappersIt.next().client.setSoTimeout(timeout);
     }
   }
 
   @Override
   public void close() {
+
     if (aliveCheckExecutor != null) {
       aliveCheckExecutor.shutdownNow();
     }
-    if(clientIsInternal) {
-      HttpClientUtil.close(httpClient);
+
+    for (SolrClient client : clients) {
+      IOUtils.closeQuietly(client);
+    }
+    
+    if (clientIsInternal) {
+      IOUtils.closeQuietly(httpClient);
     }
+    
+//    if (zombieServers != null) {
+//      synchronized (zombieServers) {
+//
+//        for (S zombieServer : zombieServers.values()) {
+//          try {
+//            zombieServer.client.close();
+//          } catch (IOException e) {
+//            throw new RuntimeException(e);
+//          }
+//        }
+//      }
+//    }
+//    if (aliveServers != null) {
+//      synchronized (aliveServers) {
+//
+//        for (ServerWrapper aliveServer : aliveServers.values()) {
+//          try {
+//            aliveServer.client.close();
+//          } catch (IOException e) {
+//            throw new RuntimeException(e);
+//          }
+//        }
+//      }
+//    }
   }
 
   /**
@@ -830,7 +888,7 @@ public class LBHttpSolrClient extends SolrClient {
   /**
    * Return the HttpClient this instance uses.
    */
-  public HttpClient getHttpClient() {
+  public SolrInternalHttpClient getHttpClient() {
     return httpClient;
   }
 
@@ -883,17 +941,18 @@ public class LBHttpSolrClient extends SolrClient {
    */
   public static class Builder extends SolrClientBuilder<Builder> {
     protected final List<String> baseSolrUrls;
-    protected HttpSolrClient.Builder httpSolrClientBuilder;
+    protected Http2SolrClient.Builder httpSolrClientBuilder;
+    protected Map<String,String> headers = new HashMap<>();
 
     public Builder() {
       this.baseSolrUrls = new ArrayList<>();
       this.responseParser = new BinaryResponseParser();
     }
 
-    public HttpSolrClient.Builder getHttpSolrClientBuilder() {
+    public Http2SolrClient.Builder getHttpSolrClientBuilder() {
       return httpSolrClientBuilder;
     }
-    
+   
     /**
      * Provide a Solr endpoint to be used when configuring {@link LBHttpSolrClient} instances.
      * 
@@ -955,10 +1014,20 @@ public class LBHttpSolrClient extends SolrClient {
     /**
      * Provides a {@link HttpSolrClient.Builder} to be used for building the internally used clients.
      */
-    public Builder withHttpSolrClientBuilder(HttpSolrClient.Builder builder) {
+    public Builder withHttpSolrClientBuilder(Http2SolrClient.Builder builder) {
       this.httpSolrClientBuilder = builder;
       return this;
     }
+    
+    public Builder withHeader(String header, String value) {
+      this.headers.put(header, value);
+      return this;
+    }
+    
+    public Builder solrInternal() {
+      this.headers.put(QoSParams.REQUEST_SOURCE, QoSParams.INTERNAL);
+      return this;
+    }
 
     /**
      * Create a {@link HttpSolrClient} based on provided configuration.
@@ -972,4 +1041,4 @@ public class LBHttpSolrClient extends SolrClient {
       return this;
     }
   }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientBuilder.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientBuilder.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientBuilder.java
index 74e981d..d19c866 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientBuilder.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientBuilder.java
@@ -16,13 +16,14 @@
  */
 package org.apache.solr.client.solrj.impl;
 
-import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.ResponseParser;
-import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder;
+import org.apache.solr.client.solrj.impl.Http2SolrClient.Builder;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
+import org.eclipse.jetty.client.HttpClient;
 
 public abstract class SolrClientBuilder<B extends SolrClientBuilder<B>> {
 
-  protected HttpClient httpClient;
+  protected SolrInternalHttpClient httpClient;
   protected ResponseParser responseParser;
   protected Integer connectionTimeoutMillis;
   protected Integer socketTimeoutMillis;
@@ -33,7 +34,7 @@ public abstract class SolrClientBuilder<B extends SolrClientBuilder<B>> {
   /**
    * Provides a {@link HttpClient} for the builder to use when creating clients.
    */
-  public B withHttpClient(HttpClient httpClient) {
+  public B withHttpClient(SolrInternalHttpClient httpClient) {
     this.httpClient = httpClient;
     return getThis();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
index fcefc2f..ab01e03 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
@@ -21,26 +21,20 @@ package org.apache.solr.client.solrj.impl;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.config.RequestConfig;
-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.HttpClientContext;
 import org.apache.http.entity.StringEntity;
-import org.apache.http.util.EntityUtils;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.cloud.DistributedQueueFactory;
 import org.apache.solr.client.solrj.cloud.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.DistributedQueueFactory;
 import org.apache.solr.client.solrj.cloud.NodeStateProvider;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
 import org.apache.solr.common.cloud.SolrZkClient;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.IOUtils;
@@ -122,55 +116,70 @@ public class SolrClientCloudManager implements SolrCloudManager {
 
   @Override
   public byte[] httpRequest(String url, SolrRequest.METHOD method, Map<String, String> headers, String payload, int timeout, boolean followRedirects) throws IOException {
-    HttpClient client = solrClient.getHttpClient();
-    final HttpRequestBase req;
-    HttpEntity entity = null;
-    if (payload != null) {
-      entity = new StringEntity(payload, "UTF-8");
-    }
-    switch (method) {
-      case GET:
-        req = new HttpGet(url);
-        break;
-      case POST:
-        req = new HttpPost(url);
-        if (entity != null) {
-          ((HttpPost)req).setEntity(entity);
-        }
-        break;
-      case PUT:
-        req = new HttpPut(url);
-        if (entity != null) {
-          ((HttpPut)req).setEntity(entity);
-        }
-        break;
-      case DELETE:
-        req = new HttpDelete(url);
-        break;
-      default:
-        throw new IOException("Unsupported method " + method);
-    }
-    if (headers != null) {
-      headers.forEach((k, v) -> req.addHeader(k, v));
-    }
-    RequestConfig.Builder requestConfigBuilder = HttpClientUtil.createDefaultRequestConfigBuilder();
-    if (timeout > 0) {
-      requestConfigBuilder.setSocketTimeout(timeout);
-      requestConfigBuilder.setConnectTimeout(timeout);
-    }
-    requestConfigBuilder.setRedirectsEnabled(followRedirects);
-    req.setConfig(requestConfigBuilder.build());
-    HttpClientContext httpClientRequestContext = HttpClientUtil.createNewHttpClientRequestContext();
-    HttpResponse rsp = client.execute(req, httpClientRequestContext);
-    int statusCode = rsp.getStatusLine().getStatusCode();
-    if (statusCode != 200) {
-      throw new IOException("Error sending request to " + url + ", HTTP response: " + rsp.toString());
+    // nocommit
+    SolrInternalHttpClient httpClient = solrClient.getHttpClient();
+    try (Http2SolrClient client = new Http2SolrClient.Builder(url).withHttpClient(httpClient).build()) {
+      final HttpRequestBase req;
+      HttpEntity entity = null;
+      if (payload != null) {
+        entity = new StringEntity(payload, "UTF-8");
+      }
+      switch (method) {
+        case GET:
+          return client.httpGet(url).asString.getBytes("UTF-8");
+        case POST:
+          return client.httpPost(url, payload.getBytes("UTF-8"), "text/html; charset=UTF-8").asString.getBytes("UTF-8");
+          //req = new HttpPost(url);
+//          if (entity != null) {
+//            ((HttpPost) req).setEntity(entity);
+//          }
+//          break;
+        case PUT:
+          return client.httpPut(url, payload.getBytes("UTF-8"), "text/html; charset=UTF-8").getBytes("UTF-8");
+//          req = new HttpPut(url);
+//          if (entity != null) {
+//            ((HttpPut) req).setEntity(entity);
+//          }
+//          break;
+        case DELETE:
+          return client.httpDelete(url).getBytes();
+          //req = new HttpDelete(url);
+          //break;
+        default:
+          throw new IOException("Unsupported method " + method);
+      }
+//      if (headers != null) {
+//        headers.forEach((k, v) -> req.addHeader(k, v));
+//      }
+//      RequestConfig.Builder requestConfigBuilder = HttpClientUtil.createDefaultRequestConfigBuilder();
+//      if (timeout > 0) {
+//        requestConfigBuilder.setSocketTimeout(timeout);
+//        requestConfigBuilder.setConnectTimeout(timeout);
+//      }
+//      requestConfigBuilder.setRedirectsEnabled(followRedirects);
+//      req.setConfig(requestConfigBuilder.build());
+//      HttpClientContext httpClientRequestContext = HttpClientUtil.createNewHttpClientRequestContext();
+//     // HttpResponse rsp = client.execute(req, httpClientRequestContext);
+//      int statusCode = rsp.getStatusLine().getStatusCode();
+//      if (statusCode != 200) {
+//        throw new IOException("Error sending request to " + url + ", HTTP response: " + rsp.toString());
+//      }
+//      HttpEntity responseEntity = rsp.getEntity();
+//      if (responseEntity != null && responseEntity.getContent() != null) {
+//        return EntityUtils.toByteArray(responseEntity);
+//      } else {
+//        return EMPTY;
+//      }
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    } catch (ExecutionException e) {
+      throw new RuntimeException(e);
+    } catch (TimeoutException e) {
+      throw new RuntimeException(e);
     }
-    HttpEntity responseEntity = rsp.getEntity();
-    if (responseEntity != null && responseEntity.getContent() != null) {
-      return EntityUtils.toByteArray(responseEntity);
-    } else {
-      return EMPTY;
+    // nocommit exceptions
+    catch (SolrServerException e) {
+      throw new RuntimeException(e);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
index b65d42a..23a5f5e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
@@ -17,6 +17,9 @@
 
 package org.apache.solr.client.solrj.impl;
 
+import static java.util.Collections.emptyMap;
+import static org.apache.solr.client.solrj.cloud.autoscaling.Clause.METRICS_PREFIX;
+
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
@@ -57,9 +60,6 @@ import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static java.util.Collections.emptyMap;
-import static org.apache.solr.client.solrj.cloud.autoscaling.Clause.METRICS_PREFIX;
-
 /**
  *
  */
@@ -297,10 +297,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
       String url = zkClientClusterStateProvider.getZkStateReader().getBaseUrlForNodeName(solrNode);
 
       GenericSolrRequest request = new GenericSolrRequest(SolrRequest.METHOD.GET, path, params);
-      try (HttpSolrClient client = new HttpSolrClient.Builder()
-          .withHttpClient(solrClient.getHttpClient())
-          .withBaseSolrUrl(url)
-          .withResponseParser(new BinaryResponseParser())
+      try (Http2SolrClient client = new Http2SolrClient.Builder(url).withHttpClient(solrClient.getHttpClient())
           .build()) {
         NamedList<Object> rsp = client.request(request);
         request.response.nl = rsp;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpClientScheduler.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpClientScheduler.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpClientScheduler.java
new file mode 100644
index 0000000..d3d607c
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrHttpClientScheduler.java
@@ -0,0 +1,105 @@
+package org.apache.solr.client.solrj.impl;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class SolrHttpClientScheduler extends AbstractLifeCycle implements Scheduler, Dumpable {
+  private final String name;
+  private final boolean daemon;
+  private final ClassLoader classloader;
+  private final ThreadGroup threadGroup;
+  private volatile ScheduledThreadPoolExecutor scheduler;
+  private volatile Thread thread;
+  private int coreThreads;
+
+  public SolrHttpClientScheduler() {
+    this(null, false);
+  }
+
+  public SolrHttpClientScheduler(String name, boolean daemon) {
+    this(name, daemon, Thread.currentThread().getContextClassLoader());
+  }
+
+  public SolrHttpClientScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader) {
+    this(name, daemon, threadFactoryClassLoader, null, 1);
+  }
+
+  public SolrHttpClientScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader,
+      ThreadGroup threadGroup, int coreThreads) {
+    this.name = name == null ? "Scheduler-" + hashCode() : name;
+    this.coreThreads = coreThreads;
+    this.daemon = daemon;
+    this.classloader = threadFactoryClassLoader == null ? Thread.currentThread().getContextClassLoader()
+        : threadFactoryClassLoader;
+    this.threadGroup = threadGroup;
+  }
+
+  @Override
+  protected void doStart() throws Exception {
+    scheduler = new ScheduledThreadPoolExecutor(coreThreads, new ThreadFactory() {
+      @Override
+      public Thread newThread(Runnable r) {
+        Thread thread = SolrHttpClientScheduler.this.thread = new Thread(threadGroup, r, name);
+        thread.setDaemon(daemon);
+        thread.setContextClassLoader(classloader);
+        return thread;
+      }
+    });
+    scheduler.setRemoveOnCancelPolicy(true);
+    super.doStart();
+  }
+
+  @Override
+  protected void doStop() throws Exception {
+    scheduler.shutdownNow();
+    super.doStop();
+    scheduler = null;
+  }
+
+  @Override
+  public Task schedule(Runnable task, long delay, TimeUnit unit) {
+    ScheduledThreadPoolExecutor s = scheduler;
+    if (s == null)
+      return () -> false;
+    ScheduledFuture<?> result = s.schedule(task, delay, unit);
+    return new ScheduledFutureTask(result);
+  }
+
+  @Override
+  public String dump() {
+    return ContainerLifeCycle.dump(this);
+  }
+
+  @Override
+  public void dump(Appendable out, String indent) throws IOException {
+    ContainerLifeCycle.dumpObject(out, this);
+    Thread thread = this.thread;
+    if (thread != null) {
+      List<StackTraceElement> frames = Arrays.asList(thread.getStackTrace());
+      ContainerLifeCycle.dump(out, indent, frames);
+    }
+  }
+
+  private static class ScheduledFutureTask implements Task {
+    private final ScheduledFuture<?> scheduledFuture;
+
+    ScheduledFutureTask(ScheduledFuture<?> scheduledFuture) {
+      this.scheduledFuture = scheduledFuture;
+    }
+
+    @Override
+    public boolean cancel() {
+      return scheduledFuture.cancel(false);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/io/Lang.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/Lang.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/Lang.java
index a01a841..7225e9a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/Lang.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/Lang.java
@@ -23,7 +23,53 @@ import org.apache.solr.client.solrj.io.ops.ConcatOperation;
 import org.apache.solr.client.solrj.io.ops.DistinctOperation;
 import org.apache.solr.client.solrj.io.ops.GroupOperation;
 import org.apache.solr.client.solrj.io.ops.ReplaceOperation;
-import org.apache.solr.client.solrj.io.stream.*;
+import org.apache.solr.client.solrj.io.stream.CalculatorStream;
+import org.apache.solr.client.solrj.io.stream.CartesianProductStream;
+import org.apache.solr.client.solrj.io.stream.CellStream;
+import org.apache.solr.client.solrj.io.stream.CloudSolrStream;
+import org.apache.solr.client.solrj.io.stream.CommitStream;
+import org.apache.solr.client.solrj.io.stream.ComplementStream;
+import org.apache.solr.client.solrj.io.stream.DaemonStream;
+import org.apache.solr.client.solrj.io.stream.EchoStream;
+import org.apache.solr.client.solrj.io.stream.EvalStream;
+import org.apache.solr.client.solrj.io.stream.ExecutorStream;
+import org.apache.solr.client.solrj.io.stream.FacetStream;
+import org.apache.solr.client.solrj.io.stream.FeaturesSelectionStream;
+import org.apache.solr.client.solrj.io.stream.FetchStream;
+import org.apache.solr.client.solrj.io.stream.GetStream;
+import org.apache.solr.client.solrj.io.stream.HashJoinStream;
+import org.apache.solr.client.solrj.io.stream.HavingStream;
+import org.apache.solr.client.solrj.io.stream.InnerJoinStream;
+import org.apache.solr.client.solrj.io.stream.IntersectStream;
+import org.apache.solr.client.solrj.io.stream.JDBCStream;
+import org.apache.solr.client.solrj.io.stream.KnnStream;
+import org.apache.solr.client.solrj.io.stream.LeftOuterJoinStream;
+import org.apache.solr.client.solrj.io.stream.LetStream;
+import org.apache.solr.client.solrj.io.stream.ListStream;
+import org.apache.solr.client.solrj.io.stream.MergeStream;
+import org.apache.solr.client.solrj.io.stream.ModelStream;
+import org.apache.solr.client.solrj.io.stream.NullStream;
+import org.apache.solr.client.solrj.io.stream.OuterHashJoinStream;
+import org.apache.solr.client.solrj.io.stream.ParallelStream;
+import org.apache.solr.client.solrj.io.stream.PlotStream;
+import org.apache.solr.client.solrj.io.stream.PriorityStream;
+import org.apache.solr.client.solrj.io.stream.RandomStream;
+import org.apache.solr.client.solrj.io.stream.RankStream;
+import org.apache.solr.client.solrj.io.stream.ReducerStream;
+import org.apache.solr.client.solrj.io.stream.RollupStream;
+import org.apache.solr.client.solrj.io.stream.ScoreNodesStream;
+import org.apache.solr.client.solrj.io.stream.SelectStream;
+import org.apache.solr.client.solrj.io.stream.ShuffleStream;
+import org.apache.solr.client.solrj.io.stream.SignificantTermsStream;
+import org.apache.solr.client.solrj.io.stream.SortStream;
+import org.apache.solr.client.solrj.io.stream.SqlStream;
+import org.apache.solr.client.solrj.io.stream.StatsStream;
+import org.apache.solr.client.solrj.io.stream.TextLogitStream;
+import org.apache.solr.client.solrj.io.stream.TimeSeriesStream;
+import org.apache.solr.client.solrj.io.stream.TopicStream;
+import org.apache.solr.client.solrj.io.stream.TupStream;
+import org.apache.solr.client.solrj.io.stream.UniqueStream;
+import org.apache.solr.client.solrj.io.stream.UpdateStream;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 import org.apache.solr.client.solrj.io.stream.metrics.CountMetric;
 import org.apache.solr.client.solrj.io.stream.metrics.MaxMetric;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
index a45c5de..bef700d 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
@@ -19,16 +19,16 @@ package org.apache.solr.client.solrj.io;
 import java.io.IOException;
 import java.io.Serializable;
 import java.lang.invoke.MethodHandles;
-import java.util.Map;
-import java.util.Optional;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 
-import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.util.SolrInternalHttpClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -42,13 +42,13 @@ public class SolrClientCache implements Serializable {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private final Map<String, SolrClient> solrClients = new HashMap<>();
-  private final HttpClient httpClient;
+  private final SolrInternalHttpClient httpClient;
 
   public SolrClientCache() {
     httpClient = null;
   }
 
-  public SolrClientCache(HttpClient httpClient) {
+  public SolrClientCache(SolrInternalHttpClient httpClient) {
     this.httpClient = httpClient;
   }
 
@@ -70,15 +70,15 @@ public class SolrClientCache implements Serializable {
 
     return client;
   }
-
-  public synchronized HttpSolrClient getHttpSolrClient(String host) {
-    HttpSolrClient client;
+  
+  public synchronized Http2SolrClient getHttpSolrClient(String host) {
+    Http2SolrClient client;
     if (solrClients.containsKey(host)) {
-      client = (HttpSolrClient) solrClients.get(host);
+      client = (Http2SolrClient) solrClients.get(host);
     } else {
-      HttpSolrClient.Builder builder = new HttpSolrClient.Builder(host);
+      Http2SolrClient.Builder builder = new Http2SolrClient.Builder(host);
       if (httpClient != null) {
-        builder = builder.withHttpClient(httpClient);
+       builder = builder.withHttpClient(httpClient);
       }
       client = builder.build();
       solrClients.put(host, client);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a9b54c/solr/solrj/src/java/org/apache/solr/client/solrj/io/graph/GatherNodesStream.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/graph/GatherNodesStream.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/graph/GatherNodesStream.java
index c0fd054..b5ec4f1 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/graph/GatherNodesStream.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/graph/GatherNodesStream.java
@@ -17,6 +17,8 @@
 
 package org.apache.solr.client.solrj.io.graph;
 
+import static org.apache.solr.common.params.CommonParams.SORT;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -52,8 +54,6 @@ import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.SolrjNamedThreadFactory;
 
-import static org.apache.solr.common.params.CommonParams.SORT;
-
 /**
  * @since 6.1.0
  */


Mime
View raw message