This is an automated email from the ASF dual-hosted git repository.
dsmiley pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_9x by this push:
new c5d2c4629f9 Revert "SOLR-17286: Remote Proxy via Jetty HttpClient
(#3447)"
c5d2c4629f9 is described below
commit c5d2c4629f928fa8c8b4310435b87bd075c2b645
Author: David Smiley <[email protected]>
AuthorDate: Mon Aug 25 09:24:06 2025 -0400
Revert "SOLR-17286: Remote Proxy via Jetty HttpClient (#3447)"
This reverts commit 31570101be82d52ce93daebcd2fb2af1aa794d28.
---
solr/CHANGES.txt | 3 +-
.../src/java/org/apache/solr/bench/Docs.java | 8 +-
.../solr/bench/MiniClusterBenchStateTest.java | 23 ++--
solr/core/build.gradle | 1 -
.../src/java/org/apache/solr/api/V2HttpCall.java | 4 +-
.../apache/solr/servlet/CoreContainerProvider.java | 12 ++
.../java/org/apache/solr/servlet/HttpSolrCall.java | 142 +++++++++++++++++--
.../org/apache/solr/servlet/HttpSolrProxy.java | 153 ---------------------
.../apache/solr/servlet/SolrDispatchFilter.java | 17 ++-
.../client/solrj/impl/Krb5HttpClientUtils.java | 1 -
.../solr/client/solrj/impl/Http2SolrClient.java | 10 +-
.../client/solrj/impl/Krb5HttpClientBuilder.java | 1 -
.../PreemptiveBasicAuthClientBuilderFactory.java | 7 +-
13 files changed, 181 insertions(+), 201 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 9f6aed27a9a..3744a6b5597 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -36,8 +36,7 @@ Dependency Upgrades
Other Changes
---------------------
-* SOLR-17286: When proxying requests to another node, use Jetty HttpClient not
Apache HttpClient.
- (David Smiley)
+(No changes)
================== 9.9.1 ==================
Bug Fixes
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
b/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
index b3c28bc1d4b..d256739acae 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/Docs.java
@@ -26,13 +26,13 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import org.apache.lucene.util.RamUsageEstimator;
import org.apache.solr.bench.generators.MultiString;
import org.apache.solr.bench.generators.SolrGen;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.util.CollectionUtil;
-import org.apache.solr.common.util.ExecutorUtil;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.common.util.SuppressForbidden;
import org.quicktheories.core.Gen;
@@ -110,7 +110,11 @@ public class Docs {
executorService.execute(() -> docs.add(Docs.this.inputDocument()));
}
- ExecutorUtil.shutdownAndAwaitTermination(executorService);
+ executorService.shutdown();
+ boolean result = executorService.awaitTermination(10, TimeUnit.MINUTES);
+ if (!result) {
+ throw new RuntimeException("Timeout waiting for doc adds to finish");
+ }
log(
"done preGenerateDocs docs="
+ docs.size()
diff --git
a/solr/benchmark/src/test/org/apache/solr/bench/MiniClusterBenchStateTest.java
b/solr/benchmark/src/test/org/apache/solr/bench/MiniClusterBenchStateTest.java
index 82708f6a34e..af568e3bb98 100644
---
a/solr/benchmark/src/test/org/apache/solr/bench/MiniClusterBenchStateTest.java
+++
b/solr/benchmark/src/test/org/apache/solr/bench/MiniClusterBenchStateTest.java
@@ -26,13 +26,15 @@ import static
org.apache.solr.bench.generators.SourceDSL.longs;
import static org.apache.solr.bench.generators.SourceDSL.strings;
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
+import java.lang.invoke.MethodHandles;
import java.util.Collections;
+import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
-import org.junit.After;
import org.junit.Test;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.infra.BenchmarkParams;
@@ -40,12 +42,12 @@ import org.openjdk.jmh.infra.IterationParams;
import org.openjdk.jmh.runner.IterationType;
import org.openjdk.jmh.runner.WorkloadParams;
import org.openjdk.jmh.runner.options.TimeValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@ThreadLeakLingering(linger = 10)
public class MiniClusterBenchStateTest extends SolrTestCaseJ4 {
- private MiniClusterState.MiniClusterBenchState miniBenchState;
- private BaseBenchState baseBenchState;
- private BenchmarkParams benchParams;
+ private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Test
public void testMiniClusterState() throws Exception {
@@ -53,8 +55,9 @@ public class MiniClusterBenchStateTest extends SolrTestCaseJ4
{
System.setProperty("workBaseDir", createTempDir("work").toString());
System.setProperty("random.counts", "true");
- miniBenchState = new MiniClusterState.MiniClusterBenchState();
- benchParams =
+ MiniClusterState.MiniClusterBenchState miniBenchState =
+ new MiniClusterState.MiniClusterBenchState();
+ BenchmarkParams benchParams =
new BenchmarkParams(
"benchmark",
"generatedTarget",
@@ -77,7 +80,7 @@ public class MiniClusterBenchStateTest extends SolrTestCaseJ4
{
"vmVersion",
"jmhVersion",
TimeValue.seconds(10));
- baseBenchState = new BaseBenchState();
+ BaseBenchState baseBenchState = new BaseBenchState();
baseBenchState.doSetup(benchParams);
miniBenchState.doSetup(benchParams, baseBenchState);
@@ -108,7 +111,7 @@ public class MiniClusterBenchStateTest extends
SolrTestCaseJ4 {
.field(doubles().all());
int numDocs = 50;
- docs.preGenerate(numDocs);
+ Iterator<SolrInputDocument> docIt = docs.preGenerate(numDocs);
miniBenchState.index(collection, docs, numDocs);
@@ -121,12 +124,8 @@ public class MiniClusterBenchStateTest extends
SolrTestCaseJ4 {
BaseBenchState.log("match all query result=" + result);
assertEquals(numDocs, result.getResults().getNumFound());
- }
- @After
- public void after() throws Exception {
BaseBenchState.doTearDown(benchParams);
-
miniBenchState.tearDown(benchParams);
miniBenchState.shutdownMiniCluster(benchParams, baseBenchState);
}
diff --git a/solr/core/build.gradle b/solr/core/build.gradle
index 51db9824312..72ac2dbe293 100644
--- a/solr/core/build.gradle
+++ b/solr/core/build.gradle
@@ -122,7 +122,6 @@ dependencies {
implementation 'org.eclipse.jetty:jetty-client'
implementation 'org.eclipse.jetty:jetty-http'
implementation 'org.eclipse.jetty:jetty-io'
- implementation 'org.eclipse.jetty:jetty-util'
implementation 'org.eclipse.jetty.toolchain:jetty-servlet-api'
// ZooKeeper
diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index 3480d8f7f5b..24cb696aeee 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -349,7 +349,7 @@ public class V2HttpCall extends HttpSolrCall {
}
/**
- * Differentiate between "admin" and "remoteproxy"-type requests; executing
each as appropriate.
+ * Differentiate between "admin" and "remotequery"-type requests; executing
each as appropriate.
*
* <p>The JAX-RS framework used by {@link V2HttpCall} doesn't provide any
easy way to check in
* advance whether a Jersey application can handle an incoming request.
This, in turn, makes it
@@ -360,7 +360,7 @@ public class V2HttpCall extends HttpSolrCall {
* <p>This method uses this strategy to differentiate between admin requests
that don't require a
* {@link SolrCore}, but whose path happen to contain a core/collection name
(e.g.
* ADDREPLICAPROP's path of
- * /collections/collName/shards/shardName/replicas/replicaName/properties),
and "REMOTEPROXY"
+ * /collections/collName/shards/shardName/replicas/replicaName/properties),
and "REMOTEQUERY"
* requests which do require a local SolrCore to process.
*/
@Override
diff --git
a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
index 10ae2bff6f2..8b8bc3c927d 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
@@ -49,6 +49,7 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.UnavailableException;
+import org.apache.http.client.HttpClient;
import org.apache.lucene.store.MMapDirectory;
import org.apache.lucene.util.VectorUtil;
import org.apache.solr.client.api.util.SolrVersion;
@@ -83,6 +84,7 @@ public class CoreContainerProvider implements
ServletContextListener {
private final String metricTag = SolrMetricProducer.getUniqueMetricTag(this,
null);
private CoreContainer cores;
private Properties extraProperties;
+ private HttpClient httpClient;
private SolrMetricManager metricManager;
private RateLimitManager rateLimitManager;
private String registryName;
@@ -121,6 +123,14 @@ public class CoreContainerProvider implements
ServletContextListener {
return cores;
}
+ /**
+ * @see SolrDispatchFilter#getHttpClient()
+ */
+ HttpClient getHttpClient() throws UnavailableException {
+ checkReady();
+ return httpClient;
+ }
+
private void checkReady() throws UnavailableException {
// TODO throw AlreadyClosedException instead?
if (cores == null) {
@@ -166,6 +176,7 @@ public class CoreContainerProvider implements
ServletContextListener {
}
} finally {
if (cc != null) {
+ httpClient = null;
cc.shutdown();
}
}
@@ -218,6 +229,7 @@ public class CoreContainerProvider implements
ServletContextListener {
});
coresInit = createCoreContainer(computeSolrHome(servletContext),
extraProperties);
+ this.httpClient =
coresInit.getUpdateShardHandler().getDefaultHttpClient();
setupJvmMetrics(coresInit, coresInit.getNodeConfig().getMetricsConfig());
SolrZkClient zkClient = null;
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index 895c00603dd..1fd374536b5 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -36,13 +36,16 @@ import io.opentracing.Span;
import io.opentracing.tag.Tags;
import java.io.EOFException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;
+import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -59,10 +62,26 @@ import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.jcip.annotations.ThreadSafe;
+import org.apache.http.Header;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+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.InputStreamEntity;
import org.apache.solr.api.ApiBag;
import org.apache.solr.api.V2HttpCall;
import org.apache.solr.client.api.util.SolrVersion;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.Aliases;
@@ -114,7 +133,6 @@ import
org.apache.solr.update.processor.DistributingUpdateProcessorFactory;
import org.apache.solr.util.RTimerTree;
import org.apache.solr.util.tracing.TraceUtils;
import org.apache.zookeeper.KeeperException;
-import org.eclipse.jetty.client.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MarkerFactory;
@@ -124,6 +142,8 @@ import org.slf4j.MarkerFactory;
public class HttpSolrCall {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ public static final String ORIGINAL_USER_PRINCIPAL_HEADER =
"originalUserPrincipal";
+
public static final String INTERNAL_REQUEST_COUNT = "_forwardedCount";
protected final SolrDispatchFilter solrDispatchFilter;
@@ -480,6 +500,12 @@ public class HttpSolrCall {
}
}
+ protected void sendRemoteQuery() throws IOException {
+ SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, new
SolrQueryResponse(), action));
+ mustClearSolrRequestInfo = true;
+ remoteQuery(coreUrl + path, response);
+ }
+
/** This method processes the request. */
public Action call() throws IOException {
@@ -604,7 +630,7 @@ public class HttpSolrCall {
/**
* Handle a request whose "type" could not be discerned in advance and may
be either "admin" or
- * "remoteproxy".
+ * "remotequery".
*
* <p>Some implementations (such as {@link V2HttpCall}) may find it
difficult to differentiate all
* request types in advance. This method serves as a hook; allowing those
implementations to
@@ -702,20 +728,107 @@ public class HttpSolrCall {
}
}
- protected void sendRemoteQuery() throws IOException {
+ // TODO using Http2Client
+ private void remoteQuery(String coreUrl, HttpServletResponse resp) throws
IOException {
+ HttpRequestBase method;
+ HttpEntity httpEntity = null;
+
ModifiableSolrParams updatedQueryParams = new
ModifiableSolrParams(queryParams);
int forwardCount = queryParams.getInt(INTERNAL_REQUEST_COUNT, 0) + 1;
updatedQueryParams.set(INTERNAL_REQUEST_COUNT, forwardCount);
String queryStr = updatedQueryParams.toQueryString();
- String coreUrlAndPath = coreUrl + path;
- log.info("Proxying request to: {}", coreUrlAndPath);
try {
- response.reset(); // clear all headers and status
- HttpClient httpClient = cores.getDefaultHttpSolrClient().getHttpClient();
- HttpSolrProxy.doHttpProxy(httpClient, req, response, coreUrlAndPath +
queryStr);
- } catch (Exception e) {
- // note: don't handle interruption differently; we are stopping
+ String urlstr = coreUrl + queryStr;
+
+ boolean isPostOrPutRequest = "POST".equals(req.getMethod()) ||
"PUT".equals(req.getMethod());
+ if ("GET".equals(req.getMethod())) {
+ method = new HttpGet(urlstr);
+ } else if ("HEAD".equals(req.getMethod())) {
+ method = new HttpHead(urlstr);
+ } else if (isPostOrPutRequest) {
+ HttpEntityEnclosingRequestBase entityRequest =
+ "POST".equals(req.getMethod()) ? new HttpPost(urlstr) : new
HttpPut(urlstr);
+ InputStream in = req.getInputStream();
+ HttpEntity entity = new InputStreamEntity(in, req.getContentLength());
+ entityRequest.setEntity(entity);
+ method = entityRequest;
+ } else if ("DELETE".equals(req.getMethod())) {
+ method = new HttpDelete(urlstr);
+ } else if ("OPTIONS".equals(req.getMethod())) {
+ method = new HttpOptions(urlstr);
+ } else {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, "Unexpected method type: " +
req.getMethod());
+ }
+
+ for (Enumeration<String> e = req.getHeaderNames(); e.hasMoreElements();
) {
+ String headerName = e.nextElement();
+ if (!"host".equalsIgnoreCase(headerName)
+ && !"authorization".equalsIgnoreCase(headerName)
+ && !"accept".equalsIgnoreCase(headerName)) {
+ method.addHeader(headerName, req.getHeader(headerName));
+ }
+ }
+ // These headers not supported for HttpEntityEnclosingRequests
+ if (method instanceof HttpEntityEnclosingRequest) {
+ method.removeHeaders(TRANSFER_ENCODING_HEADER);
+ method.removeHeaders(CONTENT_LENGTH_HEADER);
+ }
+
+ // Make sure the user principal is forwarded when its exist
+ HttpClientContext httpClientRequestContext =
+ HttpClientUtil.createNewHttpClientRequestContext();
+ Principal userPrincipal = req.getUserPrincipal();
+ if (userPrincipal != null) {
+ // Normally the context contains a static userToken to enable reuse
resources. However, if a
+ // personal Principal object exists, we use that instead, also as a
means to transfer
+ // authentication information to Auth plugins that wish to intercept
the request later
+ if (log.isDebugEnabled()) {
+ log.debug("Forwarding principal {}", userPrincipal);
+ }
+ httpClientRequestContext.setUserToken(userPrincipal);
+ }
+
+ // Execute the method.
+ final HttpResponse response =
+ solrDispatchFilter.getHttpClient().execute(method,
httpClientRequestContext);
+ int httpStatus = response.getStatusLine().getStatusCode();
+ httpEntity = response.getEntity();
+
+ resp.setStatus(httpStatus);
+ for (HeaderIterator responseHeaders = response.headerIterator();
+ responseHeaders.hasNext(); ) {
+ Header header = responseHeaders.nextHeader();
+
+ // We pull out these two headers below because they can cause chunked
+ // encoding issues with Tomcat
+ if (header != null
+ && !header.getName().equalsIgnoreCase(TRANSFER_ENCODING_HEADER)
+ && !header.getName().equalsIgnoreCase(CONNECTION_HEADER)) {
+
+ // NOTE: explicitly using 'setHeader' instead of 'addHeader' so that
+ // the remote nodes values for any response headers will overide any
that
+ // may have already been set locally (ex: by the local jetty's
RewriteHandler config)
+ resp.setHeader(header.getName(), header.getValue());
+ }
+ }
+
+ if (httpEntity != null) {
+ if (httpEntity.getContentEncoding() != null)
+ resp.setHeader(
+ httpEntity.getContentEncoding().getName(),
+ httpEntity.getContentEncoding().getValue());
+ if (httpEntity.getContentType() != null)
+ resp.setContentType(httpEntity.getContentType().getValue());
+
+ InputStream is = httpEntity.getContent();
+ OutputStream os = resp.getOutputStream();
+
+ is.transferTo(os);
+ }
+
+ } catch (IOException e) {
sendError(
new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
@@ -724,10 +837,8 @@ public class HttpSolrCall {
+ " with _forwardCount: "
+ forwardCount,
e));
- } catch (Error e) {
- throw e;
- } catch (Throwable e) {
- throw new RuntimeException(e);
+ } finally {
+ Utils.consumeFully(httpEntity);
}
}
@@ -1206,6 +1317,9 @@ public class HttpSolrCall {
};
}
+ static final String CONNECTION_HEADER = "Connection";
+ static final String TRANSFER_ENCODING_HEADER = "Transfer-Encoding";
+ static final String CONTENT_LENGTH_HEADER = "Content-Length";
List<CommandOperation> parsedCommands;
public List<CommandOperation> getCommands(boolean validateInput) {
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrProxy.java
b/solr/core/src/java/org/apache/solr/servlet/HttpSolrProxy.java
deleted file mode 100644
index 0282f360f05..00000000000
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrProxy.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.servlet;
-
-import io.opentracing.util.GlobalTracer;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.EnumSet;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.solr.util.tracing.TraceUtils;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.util.InputStreamRequestContent;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpHeader;
-
-/** Helper class for proxying the request to another Solr node. */
-// Tried to use Jetty's ProxyServlet instead but ran into inexplicable
difficulties: EOF/reset.
-// Perhaps was related to its use of ServletRequest.startAsync()/AsyncContext
-class HttpSolrProxy {
- // TODO add X-Forwarded-For and with comma delimited
-
- private static final Set<HttpHeader> HOP_BY_HOP_HEADERS =
- EnumSet.of(
- HttpHeader.CONNECTION,
- HttpHeader.KEEP_ALIVE,
- HttpHeader.PROXY_AUTHENTICATE,
- HttpHeader.PROXY_AUTHORIZATION,
- HttpHeader.TE,
- HttpHeader.TRANSFER_ENCODING,
- HttpHeader.UPGRADE);
-
- // Methods that shouldn't have a body according to HTTP spec
- private static final Set<String> NO_BODY_METHODS = Set.of("GET", "HEAD",
"DELETE");
-
- static void doHttpProxy(
- HttpClient httpClient,
- HttpServletRequest servletReq,
- HttpServletResponse servletRsp,
- String url)
- throws Throwable {
- Request proxyReq =
httpClient.newRequest(url).method(servletReq.getMethod());
-
- // clearing them first to ensure there's no stock entries (e.g. user-agent)
- proxyReq.headers(proxyFields -> copyRequestHeaders(servletReq,
proxyFields.clear()));
-
- // FYI see InstrumentedHttpListenerFactory
- TraceUtils.injectTraceContext(proxyReq, GlobalTracer.get().activeSpan());
- // TODO client spans. See OTEL agent's approach:
- //
https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/jetty-httpclient/jetty-httpclient-12.0
-
- if (!NO_BODY_METHODS.contains(servletReq.getMethod())) {
- proxyReq.body(
- new InputStreamRequestContent(servletReq.getContentType(),
servletReq.getInputStream()));
- }
-
- CompletableFuture<Result> resultFuture = new CompletableFuture<>();
-
- proxyReq.send(
- new Response.Listener() {
- private final byte[] buffer = new byte[8192];
-
- @Override
- public void onBegin(Response response) {
- servletRsp.setStatus(response.getStatus());
- }
-
- @Override
- public void onHeaders(Response response) {
- copyResponseHeaders(response, servletRsp);
- }
-
- @Override
- public void onContent(Response response, ByteBuffer content) {
- try {
- final OutputStream clientOutputStream =
servletRsp.getOutputStream();
-
- // Copy content to the client's output stream in chunks using
the existing buffer
- int remaining = content.remaining();
- while (remaining > 0) {
- int chunkSize = Math.min(remaining, buffer.length);
- content.get(buffer, 0, chunkSize);
- clientOutputStream.write(buffer, 0, chunkSize);
- remaining -= chunkSize;
- }
- clientOutputStream.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void onComplete(Result result) {
- resultFuture.complete(result);
- }
- });
-
- Result result = resultFuture.get(); // waits
- var failure = result.getFailure();
- if (failure != null) {
- throw failure;
- }
- }
-
- private static void copyRequestHeaders(
- HttpServletRequest servletReq, HttpFields.Mutable proxyFields) {
- servletReq
- .getHeaderNames()
- .asIterator()
- .forEachRemaining(
- headerName -> {
- HttpHeader knownHeader = HttpHeader.CACHE.get(headerName); //
maybe null
- if (!HOP_BY_HOP_HEADERS.contains(knownHeader)) {
- servletReq
- .getHeaders(headerName)
- .asIterator()
- .forEachRemaining(headerVal -> proxyFields.add(headerName,
headerVal));
- }
- });
- }
-
- private static void copyResponseHeaders(Response proxyRsp,
HttpServletResponse servletRsp) {
- for (HttpField headerField : proxyRsp.getHeaders()) {
- HttpHeader knownHeader = headerField.getHeader();
- if (!HOP_BY_HOP_HEADERS.contains(knownHeader)) {
- // HttpField: even if multiple values, it's encoded as one comma
delimited value
- servletRsp.addHeader(headerField.getName(), headerField.getValue());
- }
- }
- }
-}
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index 7e8f5aa447e..cfdac952858 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -40,6 +40,7 @@ import javax.servlet.UnavailableException;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.http.client.HttpClient;
import org.apache.solr.api.V2HttpCall;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
@@ -90,6 +91,15 @@ public class SolrDispatchFilter extends HttpFilter
implements PathExcluder {
public final boolean isV2Enabled = V2ApiUtils.isEnabled();
+ public HttpClient getHttpClient() {
+ try {
+ return containerProvider.getHttpClient();
+ } catch (UnavailableException e) {
+ throw new SolrException(
+ ErrorCode.SERVER_ERROR, "Internal Http Client Unavailable, startup
may have failed");
+ }
+ }
+
/**
* Enum to define action that needs to be processed. PASSTHROUGH: Pass
through to another filter
* via webapp. FORWARD: Forward rewritten URI (without path prefix and
core/collection name) to
@@ -124,7 +134,6 @@ public class SolrDispatchFilter extends HttpFilter
implements PathExcluder {
@Override
public void init(FilterConfig config) throws ServletException {
- super.init(config);
try {
containerProvider =
CoreContainerProvider.serviceForContext(config.getServletContext());
boolean isCoordinator =
@@ -192,11 +201,9 @@ public class SolrDispatchFilter extends HttpFilter
implements PathExcluder {
}
});
} finally {
+ ServletUtils.consumeInputFully(request, response);
SolrRequestInfo.reset();
- if (!request.isAsyncStarted()) { // jetty's proxy uses this
- ServletUtils.consumeInputFully(request, response);
- SolrRequestParsers.cleanupMultipartFiles(request);
- }
+ SolrRequestParsers.cleanupMultipartFiles(request);
}
}
diff --git
a/solr/modules/hadoop-auth/src/test/org/apache/solr/client/solrj/impl/Krb5HttpClientUtils.java
b/solr/modules/hadoop-auth/src/test/org/apache/solr/client/solrj/impl/Krb5HttpClientUtils.java
index 967c32c82ad..8f78c5035fb 100644
---
a/solr/modules/hadoop-auth/src/test/org/apache/solr/client/solrj/impl/Krb5HttpClientUtils.java
+++
b/solr/modules/hadoop-auth/src/test/org/apache/solr/client/solrj/impl/Krb5HttpClientUtils.java
@@ -38,7 +38,6 @@ public class Krb5HttpClientUtils {
HttpAuthenticationStore authenticationStore = new
HttpAuthenticationStore();
authenticationStore.addAuthentication(createSPNEGOAuthentication(principalName));
http2Client
- .getHttpClient()
.getProtocolHandlers()
.put(new
WWWAuthenticationProtocolHandler(http2Client.getHttpClient()));
http2Client.setAuthenticationStore(authenticationStore);
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
index 4e5761c2095..7462798a06a 100644
--- 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
@@ -214,14 +214,14 @@ public class Http2SolrClient extends HttpSolrClientBase {
this.listenerFactory.add(factory);
}
- /** internal use only */
- public HttpClient getHttpClient() {
+ // internal usage only
+ HttpClient getHttpClient() {
return httpClient;
}
- @Deprecated(since = "9.10")
- public ProtocolHandlers getProtocolHandlers() {
- return getHttpClient().getProtocolHandlers();
+ // internal usage only
+ ProtocolHandlers getProtocolHandlers() {
+ return httpClient.getProtocolHandlers();
}
private HttpClient createHttpClient(Builder builder) {
diff --git
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientBuilder.java
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientBuilder.java
index 3e555d533e8..8b47d45d2f0 100644
---
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientBuilder.java
+++
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Krb5HttpClientBuilder.java
@@ -131,7 +131,6 @@ public class Krb5HttpClientBuilder implements
HttpClientBuilderFactory {
authenticationStore.addAuthentication(createSPNEGOAuthentication());
http2Client.setAuthenticationStore(authenticationStore);
http2Client
- .getHttpClient()
.getProtocolHandlers()
.put(new
WWWAuthenticationProtocolHandler(http2Client.getHttpClient()));
}
diff --git
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
index 2b8c183065b..0d1231baa74 100644
---
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
+++
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
@@ -95,9 +95,10 @@ public class PreemptiveBasicAuthClientBuilderFactory
implements HttpClientBuilde
authenticationStore.addAuthentication(
new SolrBasicAuthentication(basicAuthUser, basicAuthPass));
client.setAuthenticationStore(authenticationStore);
- var httpClient = client.getHttpClient();
- httpClient.getProtocolHandlers().put(new
WWWAuthenticationProtocolHandler(httpClient));
- httpClient.getProtocolHandlers().put(new
ProxyAuthenticationProtocolHandler(httpClient));
+ client.getProtocolHandlers().put(new
WWWAuthenticationProtocolHandler(client.getHttpClient()));
+ client
+ .getProtocolHandlers()
+ .put(new ProxyAuthenticationProtocolHandler(client.getHttpClient()));
}
@Override