You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ds...@apache.org on 2025/12/01 01:24:32 UTC

(solr) branch branch_10x updated (ffe39411969 -> a6be194632f)

This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a change to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git


    from ffe39411969 Chasing typos in source code comments (#3886)
     new 221879461d0 Tests: ServletFixtures (#3877)
     new 83aceb9aaa5 Tests: move ServletFixtures to test-framework
     new 0ead8d3e0df minor: CloudSolrClient.close: don't throw (#3878)
     new fbf197fead8 SOLR-17996: SolrRequest.requestWithBaseUrl and on HttpSolrClientBase (#3881)
     new f957a72affa build: print broken links
     new ce593cb5ee3 Tests: move ServletFixtures to a util package (#3896)
     new 49d61c96563 Tests: SolrJettyTestRule: harden cleanup
     new ab330d40d60 SOLR-17962: Move tests relating to Apache HttpClient (#3888)
     new 1f74749a3e9 SOLR-17161: LBHttp2SolrClient is now LBAsyncSolrClient (#3882)
     new ed08c58ae85 SOLR-18005: ConcurrentUpdateJettySolrClient (#3885)
     new 6b120f2cfb2 SOLR-18005 SolrJ: consolidate Jetty-using classes to a jetty package (#3901)
     new a6be194632f SOLR-17996: improve documentation, etc.

The 12 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/lucene/missingdoclet/MissingDoclet.java |   1 +
 ...dhttp2solrclient-builder- now also can work.yml |   2 +-
 .../unreleased/SOLR-17996-requestWithBaseUrl.yml   |   8 +
 .../SOLR-18005-ConcurrentUpdateJettySolrClient.yml |   8 +
 .../unreleased/SOLR-18005-LBAsyncSolrClient.yml    |   8 +
 .../SOLR-18005-MoveToJettyPackageAndRename.yml     |   8 +
 gradle/documentation/render-javadoc.gradle         |   1 +
 gradle/validation/check-broken-links.gradle        |   4 +
 .../org/apache/solr/bench/MiniClusterState.java    |  21 +-
 .../org/apache/solr/bench/index/CloudIndexing.java |   2 +-
 .../org/apache/solr/bench/search/FilterCache.java  |   8 +-
 .../org/apache/solr/bench/search/JsonFaceting.java |   5 +-
 .../apache/solr/bench/search/NumericSearch.java    |   4 +-
 .../apache/solr/bench/search/StreamingSearch.java  |  10 +-
 solr/bin/solr                                      |   2 +-
 solr/bin/solr.cmd                                  |   4 +-
 solr/bin/solr.in.cmd                               |   4 +-
 solr/bin/solr.in.sh                                |   4 +-
 .../src/java/org/apache/solr/cli/CLIUtils.java     |  19 +-
 .../src/java/org/apache/solr/cli/CreateTool.java   |   8 +-
 .../src/java/org/apache/solr/cli/DeleteTool.java   |   8 +-
 .../src/java/org/apache/solr/cli/ExportTool.java   |   8 +-
 .../java/org/apache/solr/cli/HealthcheckTool.java  |   3 +-
 .../src/java/org/apache/solr/cli/PostLogsTool.java |   6 +-
 .../java/org/apache/solr/cli/RunExampleTool.java   |   4 +-
 .../org/apache/solr/cloud/RecoveryStrategy.java    |   7 +-
 .../java/org/apache/solr/cloud/SyncStrategy.java   |   6 +-
 .../java/org/apache/solr/cloud/ZkController.java   |  10 +-
 .../api/collections/CollectionHandlingUtils.java   |   8 +-
 .../api/collections/ReindexCollectionCmd.java      |   5 +-
 .../java/org/apache/solr/core/CoreContainer.java   |  11 +-
 .../apache/solr/core/HttpSolrClientProvider.java   |  10 +-
 .../apache/solr/filestore/DistribFileStore.java    |   2 +-
 .../java/org/apache/solr/handler/IndexFetcher.java |  17 +-
 .../org/apache/solr/handler/SolrConfigHandler.java |  14 +-
 .../org/apache/solr/handler/admin/ColStatus.java   |   8 +-
 .../apache/solr/handler/admin/api/SyncShard.java   |   4 +-
 .../solr/handler/component/HttpShardHandler.java   |   7 +-
 .../handler/component/HttpShardHandlerFactory.java |  13 +-
 .../handler/component/IterativeMergeStrategy.java  |   6 +-
 .../org/apache/solr/schema/ManagedIndexSchema.java |   3 +-
 .../org/apache/solr/security/BasicAuthPlugin.java  |   4 +-
 .../solr/security/HttpClientBuilderPlugin.java     |   4 +-
 .../solr/security/PKIAuthenticationPlugin.java     |   8 +-
 .../org/apache/solr/update/PeerSyncWithLeader.java |   6 +-
 .../org/apache/solr/update/SolrCmdDistributor.java |   6 +-
 .../apache/solr/update/StreamingSolrClients.java   |  23 +-
 .../org/apache/solr/update/UpdateShardHandler.java |  14 +-
 .../processor/DistributedUpdateProcessor.java      |   6 +-
 .../stats/InstrumentedHttpListenerFactory.java     |   2 +-
 .../test/org/apache/solr/cli/TestExportTool.java   |   4 +-
 .../test/org/apache/solr/cloud/OverseerTest.java   |   8 +-
 .../apache/solr/cloud/RecoveryZkTestWithAuth.java  |   3 +-
 .../solr/cloud/TestMiniSolrCloudClusterSSL.java    |  24 +-
 .../solr/cloud/TestQueryingOnDownCollection.java   |   4 +-
 .../org/apache/solr/cloud/ZkControllerTest.java    |   8 +-
 .../solr/core/TestHttpSolrClientProvider.java      |   4 +-
 .../solr/core/snapshots/TestSolrCoreSnapshots.java |   6 +-
 .../apache/solr/handler/ReplicationTestHelper.java |   6 +-
 .../org/apache/solr/handler/TestHttpRequestId.java |  10 +-
 .../TestUserManagedReplicationWithAuth.java        |  10 +-
 .../solr/packagemanager/TestPackageManager.java    |   4 +-
 .../apache/solr/search/TestCoordinatorRole.java    |   4 +-
 .../AuthWithShardHandlerFactoryOverrideTest.java   |   6 +-
 .../solr/security/BasicAuthOnSingleNodeTest.java   |   6 +-
 .../TestSimplePropagatorDistributedTracing.java    |   9 +-
 .../manager/consumer/KafkaCrossDcConsumer.java     |   3 +-
 .../apache/solr/security/jwt/JWTAuthPlugin.java    |   4 +-
 .../examples/UsingSolrJRefGuideExamplesTest.java   |  12 +-
 .../pages/basic-authentication-plugin.adoc         |  14 +-
 .../deployment-guide/pages/enabling-ssl.adoc       |   6 +-
 .../modules/deployment-guide/pages/solrj.adoc      |  14 +-
 .../pages/major-changes-in-solr-10.adoc            |   6 +
 .../pages/major-changes-in-solr-8.adoc             |   3 +-
 .../solr/client/solrj/io/SolrClientCache.java      |  46 +--
 .../client/solrj/io/sql/DatabaseMetaDataImpl.java  |   4 +-
 .../solr/client/solrj/impl/NodeValueFetcher.java   |   4 +-
 .../client/solrj/impl/SolrClientCloudManager.java  |   8 +-
 .../solrj/impl/SolrClientNodeStateProvider.java    |  25 +-
 .../solr/common/cloud/NodesSysPropsCacher.java     |  10 +-
 .../client/solrj/impl/NodeValueFetcherTest.java    |   6 +-
 .../org/apache/solr/client/solrj/SolrRequest.java  |  27 ++
 .../apache/solr/client/solrj/embedded/package.html |  22 --
 .../client/solrj/impl/CloudHttp2SolrClient.java    | 403 ++-------------------
 .../solr/client/solrj/impl/CloudSolrClient.java    | 372 ++++++++++++++++++-
 ...nt.java => ConcurrentUpdateBaseSolrClient.java} | 122 +++----
 .../solrj/impl/HttpClientBuilderFactory.java       |   5 +-
 ...Provider.java => HttpClusterStateProvider.java} |   9 +-
 .../solr/client/solrj/impl/HttpJdkSolrClient.java  |  23 +-
 .../solr/client/solrj/impl/HttpSolrClientBase.java |  27 +-
 .../solrj/impl/HttpSolrClientBuilderBase.java      |  30 +-
 ...Http2SolrClient.java => LBAsyncSolrClient.java} | 143 +-------
 .../solr/client/solrj/impl/LBSolrClient.java       | 125 ++++++-
 .../solr/client/solrj/impl/StallDetection.java     |   6 +-
 .../{impl => jetty}/AuthenticationStoreHolder.java |   2 +-
 .../client/solrj/jetty/CloudJettySolrClient.java   |  66 ++++
 .../jetty/ConcurrentUpdateJettySolrClient.java     | 202 +++++++++++
 .../HttpJettySolrClient.java}                      | 277 +++++---------
 .../solrj/{impl => jetty}/HttpListenerFactory.java |   6 +-
 .../solr/client/solrj/jetty/LBJettySolrClient.java |  53 +++
 .../PreemptiveBasicAuthClientBuilderFactory.java   |   9 +-
 .../solrj/{embedded => jetty}/SSLConfig.java       |   2 +-
 .../{util => jetty}/SolrBasicAuthentication.java   |   2 +-
 .../solr/client/solrj/jetty}/package-info.java     |   4 +-
 .../client/solrj/SolrExampleBinaryHttp2Test.java   |   4 +-
 .../solrj/embedded/SolrExampleJettyTest.java       |   2 +-
 .../SolrExampleStreamingBinaryHttp2Test.java       |   9 +-
 .../embedded/SolrExampleStreamingHttp2Test.java    |  32 +-
 .../solrj/embedded/SolrExampleXMLHttp2Test.java    |   6 +-
 .../impl/CloudHttp2SolrClientBuilderTest.java      |  94 ++---
 .../CloudHttp2SolrClientMultiConstructorTest.java  |   4 +-
 .../solrj/impl/CloudHttp2SolrClientRetryTest.java  |   7 +-
 .../solrj/impl/CloudHttp2SolrClientTest.java       | 126 +++++--
 .../solrj/impl/ClusterStateProviderTest.java       |  17 +-
 .../solr/client/solrj/impl/DebugServlet.java       | 180 ---------
 .../client/solrj/impl/HttpClusterStateSSLTest.java |  10 +-
 .../client/solrj/impl/HttpJdkSolrClientTest.java   |   1 +
 .../client/solrj/impl/HttpSolrClientTestBase.java  |  15 +-
 ...IntegrationTest.java => LB2SolrClientTest.java} |  22 +-
 ...rClientTest.java => LBAsyncSolrClientTest.java} |  78 ++--
 .../impl/SendUpdatesToLeadersOverrideTest.java     |   6 +-
 ...ncurrentUpdateJettySolrClientBadInputTest.java} |  17 +-
 ...tUpdateJettySolrClientMultiCollectionTest.java} |  16 +-
 .../ConcurrentUpdateJettySolrClientTest.java}      | 225 ++++++++++--
 .../HttpJettySolrClientCompatibilityTest.java}     |  20 +-
 .../HttpJettySolrClientProxyTest.java}             |  10 +-
 .../HttpJettySolrClientTest.java}                  | 219 +++++------
 ...reemptiveBasicAuthClientBuilderFactoryTest.java |   2 +-
 .../solr/client/solrj/request/TestCoreAdmin.java   |   2 +-
 .../src/java/org/apache/solr/SolrTestCaseJ4.java   |  18 +-
 .../client/solrj/apache/CloudLegacySolrClient.java |   6 +-
 .../solrj/apache/ConcurrentUpdateSolrClient.java   |   4 +-
 .../solrj/apache/HttpClusterStateProvider.java     |   3 +-
 .../solr/client/solrj/apache/HttpSolrClient.java   |   6 +-
 .../solr/client/solrj/apache/LBHttpSolrClient.java |   3 +-
 .../solr/cloud/AbstractFullDistribZkTestBase.java  |   2 +-
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |   2 +-
 .../java/org/apache/solr/embedded/JettyConfig.java |   2 +-
 .../org/apache/solr/embedded/JettySolrRunner.java  |   2 +-
 .../java/org/apache/solr/util/SSLTestConfig.java   |   2 +-
 .../java/org/apache/solr/util/ServletFixtures.java | 228 ++++++++++++
 .../apache/solr/util}/SolrJMetricTestUtils.java    |  11 +-
 .../org/apache/solr/util/SolrJettyTestRule.java    |   5 +-
 .../solrj/apache}/BasicHttpSolrClientTest.java     | 234 +++---------
 .../solrj/apache}/CloudSolrClientBadInputTest.java |   2 +-
 .../solrj/apache}/CloudSolrClientBuilderTest.java  |  20 +-
 .../CloudSolrClientMultiConstructorTest.java       |  11 +-
 .../solrj/apache}/CloudSolrClientRetryTest.java    |  14 +-
 .../client/solrj/apache}/CloudSolrClientTest.java  |  20 +-
 .../ConcurrentUpdateSolrClientBadInputTest.java    |   3 +-
 .../ConcurrentUpdateSolrClientBuilderTest.java     |   3 +-
 ...currentUpdateSolrClientMultiCollectionTest.java |   3 +-
 .../apache}/ConcurrentUpdateSolrClientTest.java    |   3 +-
 153 files changed, 2367 insertions(+), 1946 deletions(-)
 create mode 100644 changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
 create mode 100644 changelog/unreleased/SOLR-18005-ConcurrentUpdateJettySolrClient.yml
 create mode 100644 changelog/unreleased/SOLR-18005-LBAsyncSolrClient.yml
 create mode 100644 changelog/unreleased/SOLR-18005-MoveToJettyPackageAndRename.yml
 delete mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/embedded/package.html
 rename solr/solrj/src/java/org/apache/solr/client/solrj/impl/{ConcurrentUpdateHttp2SolrClient.java => ConcurrentUpdateBaseSolrClient.java} (86%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/impl/{Http2ClusterStateProvider.java => HttpClusterStateProvider.java} (85%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/impl/{LBHttp2SolrClient.java => LBAsyncSolrClient.java} (58%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{impl => jetty}/AuthenticationStoreHolder.java (98%)
 create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/jetty/CloudJettySolrClient.java
 create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClient.java
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{impl/Http2SolrClient.java => jetty/HttpJettySolrClient.java} (81%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{impl => jetty}/HttpListenerFactory.java (92%)
 create mode 100644 solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{impl => jetty}/PreemptiveBasicAuthClientBuilderFactory.java (94%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{embedded => jetty}/SSLConfig.java (99%)
 rename solr/solrj/src/java/org/apache/solr/client/solrj/{util => jetty}/SolrBasicAuthentication.java (98%)
 copy solr/{benchmark/src/java/org/apache/solr/bench/index => solrj/src/java/org/apache/solr/client/solrj/jetty}/package-info.java (88%)
 delete mode 100644 solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java
 rename solr/solrj/src/test/org/apache/solr/client/solrj/impl/{LBHttp2SolrClientIntegrationTest.java => LB2SolrClientTest.java} (94%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/impl/{LBHttp2SolrClientTest.java => LBAsyncSolrClientTest.java} (83%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/ConcurrentUpdateHttp2SolrClientBadInputTest.java => jetty/ConcurrentUpdateJettySolrClientBadInputTest.java} (88%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/ConcurrentUpdateHttp2SolrClientMultiCollectionTest.java => jetty/ConcurrentUpdateJettySolrClientMultiCollectionTest.java} (85%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/ConcurrentUpdateHttp2SolrClientTest.java => jetty/ConcurrentUpdateJettySolrClientTest.java} (52%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/Http2SolrClientCompatibilityTest.java => jetty/HttpJettySolrClientCompatibilityTest.java} (86%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/Http2SolrClientProxyTest.java => jetty/HttpJettySolrClientProxyTest.java} (92%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl/Http2SolrClientTest.java => jetty/HttpJettySolrClientTest.java} (78%)
 rename solr/solrj/src/test/org/apache/solr/client/solrj/{impl => jetty}/PreemptiveBasicAuthClientBuilderFactoryTest.java (99%)
 create mode 100644 solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java
 rename solr/{solrj/src/test/org/apache/solr/client/solrj => test-framework/src/java/org/apache/solr/util}/SolrJMetricTestUtils.java (91%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/BasicHttpSolrClientTest.java (81%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/CloudSolrClientBadInputTest.java (98%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/CloudSolrClientBuilderTest.java (85%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/CloudSolrClientMultiConstructorTest.java (89%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/CloudSolrClientRetryTest.java (84%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/CloudSolrClientTest.java (99%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/ConcurrentUpdateSolrClientBadInputTest.java (97%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/ConcurrentUpdateSolrClientBuilderTest.java (96%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/ConcurrentUpdateSolrClientMultiCollectionTest.java (96%)
 rename solr/{solrj/src/test/org/apache/solr/client/solrj/impl => test-framework/src/test/org/apache/solr/client/solrj/apache}/ConcurrentUpdateSolrClientTest.java (99%)


(solr) 11/12: SOLR-18005 SolrJ: consolidate Jetty-using classes to a jetty package (#3901)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 6b120f2cfb2de04d95f2d5efdd2a6507795623e1
Author: David Smiley <ds...@apache.org>
AuthorDate: Sun Nov 30 12:19:17 2025 -0500

    SOLR-18005 SolrJ: consolidate Jetty-using classes to a jetty package (#3901)
    
    Classes using Jetty HttpClient have been moved to a new package `org.apache.solr.solrj.jetty`.
    The following classes were renamed with some refactorings: `Http2SolrClient` to `HttpJettySolrClient`, `ConcurrentUpdateHttp2SolrClient` to `ConcurrentUpdateJettySolrClient`, `LBHttp2SolrClient` to `LBJettySolrClient`.
    `CloudHttp2SolrClient.Builder` has moved to `CloudSolrClient`; users should generally have no need to refer to `CloudHttp2SolrClient` or any class/member with "http2" in it.
    CloudSolrClient.Builder will check if Jetty `HttpClient` is available and use that, otherwise fallback on a JDK based `HttpClient`.
    `CloudJettySolrClient` is new, proving an explicit option.
    
    * streaming: SolrClientCache: reduce Jetty dependency.  Make more extensible.
    * PreemptiveBasicAuthClientBuilderFactory: reference in new package
    * the SolrJ "embedded" package is now gone; only had a Jetty-specific SSLConfig that moved.
    
    * restore accidental partial revert of props:
    ** solr.http.cookies.enabled,
    ** solr.keystore.reload.enabled,
    ** solr.jetty.ssl.context.reload.scan.interval.secs
---
 ...dhttp2solrclient-builder- now also can work.yml |   2 +-
 .../SOLR-18005-MoveToJettyPackageAndRename.yml     |   8 +
 .../org/apache/solr/bench/MiniClusterState.java    |   6 +-
 .../apache/solr/bench/search/StreamingSearch.java  |  10 +-
 solr/bin/solr                                      |   2 +-
 solr/bin/solr.cmd                                  |   4 +-
 solr/bin/solr.in.cmd                               |   4 +-
 solr/bin/solr.in.sh                                |   4 +-
 .../src/java/org/apache/solr/cli/CLIUtils.java     |  19 +-
 .../src/java/org/apache/solr/cli/CreateTool.java   |   8 +-
 .../src/java/org/apache/solr/cli/DeleteTool.java   |   8 +-
 .../src/java/org/apache/solr/cli/ExportTool.java   |   8 +-
 .../java/org/apache/solr/cli/HealthcheckTool.java  |   3 +-
 .../src/java/org/apache/solr/cli/PostLogsTool.java |   6 +-
 .../java/org/apache/solr/cli/RunExampleTool.java   |   4 +-
 .../org/apache/solr/cloud/RecoveryStrategy.java    |   7 +-
 .../java/org/apache/solr/cloud/SyncStrategy.java   |   6 +-
 .../java/org/apache/solr/cloud/ZkController.java   |  10 +-
 .../api/collections/CollectionHandlingUtils.java   |   6 +-
 .../java/org/apache/solr/core/CoreContainer.java   |   6 +-
 .../apache/solr/core/HttpSolrClientProvider.java   |  10 +-
 .../java/org/apache/solr/handler/IndexFetcher.java |   8 +-
 .../org/apache/solr/handler/SolrConfigHandler.java |  10 +-
 .../org/apache/solr/handler/admin/ColStatus.java   |   6 +-
 .../apache/solr/handler/admin/api/SyncShard.java   |   4 +-
 .../handler/component/HttpShardHandlerFactory.java |   6 +-
 .../handler/component/IterativeMergeStrategy.java  |   4 +-
 .../org/apache/solr/security/BasicAuthPlugin.java  |   4 +-
 .../solr/security/HttpClientBuilderPlugin.java     |   4 +-
 .../solr/security/PKIAuthenticationPlugin.java     |   6 +-
 .../org/apache/solr/update/PeerSyncWithLeader.java |   4 +-
 .../apache/solr/update/StreamingSolrClients.java   |  10 +-
 .../org/apache/solr/update/UpdateShardHandler.java |  14 +-
 .../stats/InstrumentedHttpListenerFactory.java     |   2 +-
 .../test/org/apache/solr/cli/TestExportTool.java   |   4 +-
 .../test/org/apache/solr/cloud/OverseerTest.java   |   8 +-
 .../apache/solr/cloud/RecoveryZkTestWithAuth.java  |   3 +-
 .../solr/cloud/TestMiniSolrCloudClusterSSL.java    |  24 +-
 .../solr/cloud/TestQueryingOnDownCollection.java   |   4 +-
 .../org/apache/solr/cloud/ZkControllerTest.java    |   8 +-
 .../solr/core/snapshots/TestSolrCoreSnapshots.java |   6 +-
 .../apache/solr/handler/ReplicationTestHelper.java |   6 +-
 .../org/apache/solr/handler/TestHttpRequestId.java |  10 +-
 .../TestUserManagedReplicationWithAuth.java        |   6 +-
 .../solr/packagemanager/TestPackageManager.java    |   4 +-
 .../apache/solr/search/TestCoordinatorRole.java    |   4 +-
 .../AuthWithShardHandlerFactoryOverrideTest.java   |   6 +-
 .../solr/security/BasicAuthOnSingleNodeTest.java   |   6 +-
 .../TestSimplePropagatorDistributedTracing.java    |   9 +-
 .../manager/consumer/KafkaCrossDcConsumer.java     |   3 +-
 .../apache/solr/security/jwt/JWTAuthPlugin.java    |   4 +-
 .../examples/UsingSolrJRefGuideExamplesTest.java   |  12 +-
 .../pages/basic-authentication-plugin.adoc         |  14 +-
 .../deployment-guide/pages/enabling-ssl.adoc       |   6 +-
 .../modules/deployment-guide/pages/solrj.adoc      |   8 +-
 .../pages/major-changes-in-solr-10.adoc            |   6 +
 .../pages/major-changes-in-solr-8.adoc             |   3 +-
 .../solr/client/solrj/io/SolrClientCache.java      |  46 ++-
 .../client/solrj/io/sql/DatabaseMetaDataImpl.java  |   4 +-
 .../solr/client/solrj/impl/NodeValueFetcher.java   |   4 +-
 .../client/solrj/impl/SolrClientCloudManager.java  |   8 +-
 .../solrj/impl/SolrClientNodeStateProvider.java    |  19 +-
 .../solr/common/cloud/NodesSysPropsCacher.java     |   6 +-
 .../client/solrj/impl/NodeValueFetcherTest.java    |   6 +-
 .../apache/solr/client/solrj/embedded/package.html |  22 --
 .../client/solrj/impl/CloudHttp2SolrClient.java    | 380 +--------------------
 .../solr/client/solrj/impl/CloudSolrClient.java    | 370 ++++++++++++++++++++
 .../solrj/impl/ConcurrentUpdateBaseSolrClient.java |   8 +-
 .../solrj/impl/HttpClientBuilderFactory.java       |   5 +-
 ...Provider.java => HttpClusterStateProvider.java} |   9 +-
 .../solr/client/solrj/impl/HttpJdkSolrClient.java  |  15 +-
 .../solr/client/solrj/impl/HttpSolrClientBase.java |   7 +-
 .../solrj/impl/HttpSolrClientBuilderBase.java      |  30 +-
 .../solr/client/solrj/impl/LBSolrClient.java       |   7 +-
 .../{impl => jetty}/AuthenticationStoreHolder.java |   2 +-
 .../client/solrj/jetty/CloudJettySolrClient.java   |  66 ++++
 .../ConcurrentUpdateJettySolrClient.java           |  28 +-
 .../HttpJettySolrClient.java}                      | 151 ++++----
 .../solrj/{impl => jetty}/HttpListenerFactory.java |   6 +-
 .../solr/client/solrj/jetty/LBJettySolrClient.java |   7 +-
 .../PreemptiveBasicAuthClientBuilderFactory.java   |   9 +-
 .../solrj/{embedded => jetty}/SSLConfig.java       |   2 +-
 .../{util => jetty}/SolrBasicAuthentication.java   |   2 +-
 .../client/solrj/SolrExampleBinaryHttp2Test.java   |   4 +-
 .../solrj/embedded/SolrExampleJettyTest.java       |   2 +-
 .../SolrExampleStreamingBinaryHttp2Test.java       |   6 +-
 .../embedded/SolrExampleStreamingHttp2Test.java    |  14 +-
 .../solrj/embedded/SolrExampleXMLHttp2Test.java    |   6 +-
 .../impl/CloudHttp2SolrClientBuilderTest.java      |  83 ++---
 .../CloudHttp2SolrClientMultiConstructorTest.java  |   4 +-
 .../solrj/impl/CloudHttp2SolrClientRetryTest.java  |   5 +-
 .../solrj/impl/CloudHttp2SolrClientTest.java       |  25 +-
 .../solrj/impl/ClusterStateProviderTest.java       |  17 +-
 .../client/solrj/impl/HttpClusterStateSSLTest.java |  10 +-
 .../solr/client/solrj/impl/LB2SolrClientTest.java  |   3 +-
 .../client/solrj/impl/LBAsyncSolrClientTest.java   |  25 +-
 .../impl/SendUpdatesToLeadersOverrideTest.java     |   6 +-
 ...oncurrentUpdateJettySolrClientBadInputTest.java |   6 +-
 ...ntUpdateJettySolrClientMultiCollectionTest.java |   4 +-
 .../ConcurrentUpdateJettySolrClientTest.java       |  16 +-
 .../HttpJettySolrClientCompatibilityTest.java}     |  19 +-
 .../HttpJettySolrClientProxyTest.java}             |  10 +-
 .../HttpJettySolrClientTest.java}                  | 203 +++++------
 ...reemptiveBasicAuthClientBuilderFactoryTest.java |   2 +-
 .../src/java/org/apache/solr/SolrTestCaseJ4.java   |  18 +-
 .../solrj/apache/HttpClusterStateProvider.java     |   3 +-
 .../solr/client/solrj/apache/HttpSolrClient.java   |   6 +-
 .../solr/cloud/AbstractFullDistribZkTestBase.java  |   2 +-
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |   2 +-
 .../java/org/apache/solr/embedded/JettyConfig.java |   2 +-
 .../org/apache/solr/embedded/JettySolrRunner.java  |   2 +-
 .../java/org/apache/solr/util/SSLTestConfig.java   |   2 +-
 .../org/apache/solr/util/SolrJMetricTestUtils.java |   6 +-
 .../solrj/apache/CloudSolrClientBuilderTest.java   |  14 +-
 .../CloudSolrClientMultiConstructorTest.java       |   5 +-
 115 files changed, 1140 insertions(+), 998 deletions(-)

diff --git a/changelog/unreleased/SOLR-17771-cloudhttp2solrclient-builder- now also can work.yml b/changelog/unreleased/SOLR-17771-cloudhttp2solrclient-builder- now also can work.yml
index f121838cb96..d1d6618ce39 100644
--- a/changelog/unreleased/SOLR-17771-cloudhttp2solrclient-builder- now also can work.yml	
+++ b/changelog/unreleased/SOLR-17771-cloudhttp2solrclient-builder- now also can work.yml	
@@ -1,4 +1,4 @@
-title: '`CloudHttp2SolrClient.Builder` now also can work with `HttpJdkSolrClient`.'
+title: '`CloudSolrClient.Builder` /  now also can work with `HttpJdkSolrClient`.'
 type: changed
 authors:
 - name: James Dyer
diff --git a/changelog/unreleased/SOLR-18005-MoveToJettyPackageAndRename.yml b/changelog/unreleased/SOLR-18005-MoveToJettyPackageAndRename.yml
new file mode 100644
index 00000000000..47ffa749c83
--- /dev/null
+++ b/changelog/unreleased/SOLR-18005-MoveToJettyPackageAndRename.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Add CloudJettySolrClient. Moved CloudHttp2SolrClient.Builder to CloudSolrClient. Rename Http2SolrClient to HttpJettySolrClient
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+  - name: David Smiley
+links:
+  - name: SOLR-18005
+    url: https://issues.apache.org/jira/browse/SOLR-18005
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
index baf4ccedbfb..87dba1cb477 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
@@ -38,7 +38,7 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -82,7 +82,7 @@ public class MiniClusterState {
     MiniSolrCloudCluster cluster;
 
     /** The Client. */
-    public Http2SolrClient client;
+    public HttpJettySolrClient client;
 
     /** The Run cnt. */
     int runCnt = 0;
@@ -291,7 +291,7 @@ public class MiniClusterState {
       }
       zkHost = cluster.getZkServer().getZkAddress();
 
-      client = new Http2SolrClient.Builder(nodes.get(0)).useHttp1_1(useHttp1).build();
+      client = new HttpJettySolrClient.Builder(nodes.get(0)).useHttp1_1(useHttp1).build();
 
       log("done starting mini cluster");
       log("");
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java b/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
index 09928af733c..a9860763dbe 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java
@@ -27,12 +27,12 @@ import org.apache.solr.bench.Docs;
 import org.apache.solr.bench.MiniClusterState;
 import org.apache.solr.bench.MiniClusterState.MiniClusterBenchState;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.io.SolrClientCache;
 import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.CloudSolrStream;
 import org.apache.solr.client.solrj.io.stream.StreamContext;
 import org.apache.solr.client.solrj.io.stream.TupleStream;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.openjdk.jmh.annotations.Benchmark;
@@ -68,7 +68,7 @@ public class StreamingSearch {
     private String zkHost;
     private ModifiableSolrParams params;
     private StreamContext streamContext;
-    private Http2SolrClient http2SolrClient;
+    private HttpJettySolrClient httpJettySolrClient;
 
     @Setup(Level.Trial)
     public void setup(MiniClusterBenchState miniClusterState) throws Exception {
@@ -98,7 +98,7 @@ public class StreamingSearch {
         throws SolrServerException, IOException {
       SolrClientCache solrClientCache;
       // TODO tune params?
-      var client = new Http2SolrClient.Builder().useHttp1_1(useHttp1).build();
+      var client = new HttpJettySolrClient.Builder().useHttp1_1(useHttp1).build();
       solrClientCache = new SolrClientCache(client);
 
       streamContext = new StreamContext();
@@ -108,8 +108,8 @@ public class StreamingSearch {
     @TearDown(Level.Iteration)
     public void teardownIt() {
       streamContext.getSolrClientCache().close();
-      if (http2SolrClient != null) {
-        http2SolrClient.close();
+      if (httpJettySolrClient != null) {
+        httpJettySolrClient.close();
       }
     }
   }
diff --git a/solr/bin/solr b/solr/bin/solr
index d17f00a4013..d9a95a41efe 100755
--- a/solr/bin/solr
+++ b/solr/bin/solr
@@ -342,7 +342,7 @@ fi
 if [ -n "${SOLR_AUTH_TYPE:-}" ]; then
   case "$(echo "$SOLR_AUTH_TYPE" | awk '{print tolower($0)}')" in
     basic)
-      SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory"
+      SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory"
       ;;
     *)
       echo "ERROR: Value specified for SOLR_AUTH_TYPE environment variable is invalid."
diff --git a/solr/bin/solr.cmd b/solr/bin/solr.cmd
index 44262f4f974..7396804262b 100755
--- a/solr/bin/solr.cmd
+++ b/solr/bin/solr.cmd
@@ -227,7 +227,7 @@ IF DEFINED SOLR_AUTH_TYPE (
 
 IF DEFINED SOLR_AUTH_TYPE (
   IF /I "%SOLR_AUTH_TYPE%" == "basic" (
-    set SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory"
+    set SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory"
   ) ELSE (
     echo ERROR: Value specified for SOLR_AUTH_TYPE configuration variable is invalid.
     goto err
@@ -1172,7 +1172,7 @@ for %%a in (%*) do (
    ) else (
       set "option!option!=%%a"
       if "!option!" equ "--solr-home" set "SOLR_HOME=%%a"
-      if "!option!" equ "--server-dir" set "SOLR_SERVER_DIR=%%a"    
+      if "!option!" equ "--server-dir" set "SOLR_SERVER_DIR=%%a"
       if not "!option!" equ "--solr-home" if not "!option!" equ "--server-dir" (
         set "AUTH_PARAMS=!AUTH_PARAMS! !option! %%a"
       )
diff --git a/solr/bin/solr.in.cmd b/solr/bin/solr.in.cmd
index c90a9fa1c32..d605e04b712 100755
--- a/solr/bin/solr.in.cmd
+++ b/solr/bin/solr.in.cmd
@@ -173,7 +173,7 @@ REM set SOLR_SSL_CLIENT_TRUST_STORE_TYPE=
 
 REM Settings for authentication
 REM Please configure only one of SOLR_AUTHENTICATION_CLIENT_BUILDER or SOLR_AUTH_TYPE parameters
-REM set SOLR_AUTHENTICATION_CLIENT_BUILDER=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory
+REM set SOLR_AUTHENTICATION_CLIENT_BUILDER=org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory
 REM set SOLR_AUTH_TYPE=basic
 REM set SOLR_AUTHENTICATION_OPTS=-Dsolr.security.auth.basicauth.credentials=solr:SolrRocks
 
@@ -251,6 +251,6 @@ REM Configure the default replica placement plugin to use if one is not configur
 REM See https://solr.apache.org/guide/solr/latest/configuration-guide/replica-placement-plugins.html for details
 REM set SOLR_PLACEMENTPLUGIN_DEFAULT=simple
 
-REM Solr internally doesn't use cookies. If you don't need any of those, and you don't 
+REM Solr internally doesn't use cookies. If you don't need any of those, and you don't
 REM need them for an external system (such as a load balancer), you can disable the use of a CookieStore with:
 REM set SOLR_OPTS=%SOLR_OPTS% -Dsolr.solrj.http.cookies.enabled=false
diff --git a/solr/bin/solr.in.sh b/solr/bin/solr.in.sh
index 4752790d44f..569066fede4 100644
--- a/solr/bin/solr.in.sh
+++ b/solr/bin/solr.in.sh
@@ -189,7 +189,7 @@
 
 # Settings for authentication
 # Please configure only one of SOLR_AUTHENTICATION_CLIENT_BUILDER or SOLR_AUTH_TYPE parameters
-#SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory"
+#SOLR_AUTHENTICATION_CLIENT_BUILDER="org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory"
 #SOLR_AUTH_TYPE="basic"
 #SOLR_AUTHENTICATION_OPTS="-Dsolr.security.auth.basicauth.credentials=solr:SolrRocks"
 
@@ -287,6 +287,6 @@
 # See https://solr.apache.org/guide/solr/latest/configuration-guide/replica-placement-plugins.html for details
 #SOLR_PLACEMENTPLUGIN_DEFAULT=simple
 
-# Solr internally doesn't use cookies. If you don't need any of those, and you don't 
+# Solr internally doesn't use cookies. If you don't need any of those, and you don't
 # need them for an external system (such as a load balancer), you can disable the use of a CookieStore with:
 #SOLR_OPTS="$SOLR_OPTS -Dsolr.solrj.http.cookies.enabled=false"
diff --git a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java
index 59937f79017..0520fab4e51 100644
--- a/solr/core/src/java/org/apache/solr/cli/CLIUtils.java
+++ b/solr/core/src/java/org/apache/solr/cli/CLIUtils.java
@@ -40,10 +40,9 @@ import org.apache.commons.exec.OS;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoresApi;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
@@ -109,8 +108,8 @@ public final class CLIUtils {
     if (!barePath && !solrUrl.endsWith("/solr") && !solrUrl.contains("/solr/")) {
       solrUrl = solrUrl + "/solr";
     }
-    Http2SolrClient.Builder builder =
-        new Http2SolrClient.Builder(solrUrl)
+    var builder =
+        new HttpJettySolrClient.Builder(solrUrl)
             .withMaxConnectionsPerHost(32)
             .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
             .withOptionalBasicAuthCredentials(credentials);
@@ -200,7 +199,7 @@ public final class CLIUtils {
                 + solrUrl
                 + ".");
       } else {
-        try (CloudSolrClient cloudSolrClient = getCloudHttp2SolrClient(zkHost)) {
+        try (CloudSolrClient cloudSolrClient = getCloudSolrClient(zkHost)) {
           cloudSolrClient.connect();
           Set<String> liveNodes = cloudSolrClient.getClusterState().getLiveNodes();
           if (liveNodes.isEmpty())
@@ -283,13 +282,13 @@ public final class CLIUtils {
         .build();
   }
 
-  public static CloudHttp2SolrClient getCloudHttp2SolrClient(String zkHost) {
-    return getCloudHttp2SolrClient(zkHost, null);
+  public static CloudSolrClient getCloudSolrClient(String zkHost) {
+    return getCloudSolrClient(zkHost, null);
   }
 
-  public static CloudHttp2SolrClient getCloudHttp2SolrClient(
-      String zkHost, Http2SolrClient.Builder builder) {
-    return new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost), Optional.empty())
+  public static CloudSolrClient getCloudSolrClient(
+      String zkHost, HttpJettySolrClient.Builder builder) {
+    return new CloudSolrClient.Builder(Collections.singletonList(zkHost), Optional.empty())
         .withHttpClientBuilder(builder)
         .build();
   }
diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
index 5ce1fdbfb8c..78eb6eb2901 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
@@ -32,8 +32,8 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.JsonMapResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
@@ -208,8 +208,8 @@ public class CreateTool extends ToolBase {
   }
 
   protected void createCollection(CommandLine cli) throws Exception {
-    Http2SolrClient.Builder builder =
-        new Http2SolrClient.Builder()
+    var builder =
+        new HttpJettySolrClient.Builder()
             .withIdleTimeout(30, TimeUnit.SECONDS)
             .withConnectionTimeout(15, TimeUnit.SECONDS)
             .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
@@ -217,7 +217,7 @@ public class CreateTool extends ToolBase {
                 cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION));
     String zkHost = CLIUtils.getZkHost(cli);
     echoIfVerbose("Connecting to ZooKeeper at " + zkHost);
-    try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudHttp2SolrClient(zkHost, builder)) {
+    try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) {
       cloudSolrClient.connect();
       createCollection(cloudSolrClient, cli);
     }
diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
index ce330576a71..25be18d95f5 100644
--- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
@@ -28,8 +28,8 @@ import org.apache.commons.cli.Options;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.JsonMapResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.common.cloud.ZkStateReader;
@@ -105,8 +105,8 @@ public class DeleteTool extends ToolBase {
   }
 
   protected void deleteCollection(CommandLine cli) throws Exception {
-    Http2SolrClient.Builder builder =
-        new Http2SolrClient.Builder()
+    var builder =
+        new HttpJettySolrClient.Builder()
             .withIdleTimeout(30, TimeUnit.SECONDS)
             .withConnectionTimeout(15, TimeUnit.SECONDS)
             .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
@@ -114,7 +114,7 @@ public class DeleteTool extends ToolBase {
                 cli.getOptionValue(CommonCLIOptions.CREDENTIALS_OPTION));
 
     String zkHost = CLIUtils.getZkHost(cli);
-    try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudHttp2SolrClient(zkHost, builder)) {
+    try (CloudSolrClient cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost, builder)) {
       echoIfVerbose("Connecting to ZooKeeper at " + zkHost);
       cloudSolrClient.connect();
       deleteCollection(cloudSolrClient, cli);
diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
index af2cf6ba590..fc1930cd658 100644
--- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
@@ -60,11 +60,10 @@ 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.StreamingResponseCallback;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.ClusterStateProvider;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.StreamingJavaBinResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.common.SolrDocument;
@@ -252,11 +251,10 @@ public class ExportTool extends ToolBase {
     abstract void exportDocs() throws Exception;
 
     void fetchUniqueKey() throws SolrServerException, IOException {
-      Http2SolrClient.Builder builder =
-          new Http2SolrClient.Builder().withOptionalBasicAuthCredentials(credentials);
+      var builder = new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials(credentials);
 
       solrClient =
-          new CloudHttp2SolrClient.Builder(Collections.singletonList(baseurl))
+          new CloudSolrClient.Builder(Collections.singletonList(baseurl))
               .withHttpClientBuilder(builder)
               .build();
       NamedList<Object> response =
diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
index bf795a51596..c8b6b78e6ce 100644
--- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
@@ -32,7 +32,6 @@ import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -88,7 +87,7 @@ public class HealthcheckTool extends ToolBase {
       CLIO.err("Healthcheck tool only works in Solr Cloud mode.");
       runtime.exit(1);
     }
-    try (CloudHttp2SolrClient cloudSolrClient = CLIUtils.getCloudHttp2SolrClient(zkHost)) {
+    try (var cloudSolrClient = CLIUtils.getCloudSolrClient(zkHost)) {
       echoIfVerbose("\nConnecting to ZooKeeper at " + zkHost + " ...");
       cloudSolrClient.connect();
       runCloudTool(cloudSolrClient, cli);
diff --git a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
index fd993291ed8..0cf5e95a943 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
@@ -38,7 +38,7 @@ import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.SolrInputField;
@@ -107,8 +107,8 @@ public class PostLogsTool extends ToolBase {
               + "] must point to a particular collection but appears to be a Solr base URL");
     }
 
-    Http2SolrClient.Builder builder =
-        new Http2SolrClient.Builder(URLUtil.extractBaseUrl(baseUrl))
+    var builder =
+        new HttpJettySolrClient.Builder(URLUtil.extractBaseUrl(baseUrl))
             .withDefaultCollection(URLUtil.extractCoreFromCoreUrl(baseUrl))
             .withKeyStoreReloadInterval(-1, TimeUnit.SECONDS)
             .withOptionalBasicAuthCredentials(credentials);
diff --git a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
index aecc0676ebb..9fcd71c911d 100644
--- a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
@@ -44,7 +44,6 @@ import org.apache.commons.exec.Executor;
 import org.apache.commons.exec.environment.EnvironmentUtils;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.EnvUtils;
@@ -635,8 +634,7 @@ public class RunExampleTool extends ToolBase {
   /** wait until the number of live nodes == numNodes. */
   protected void waitToSeeLiveNodes(String zkHost, int numNodes) {
     try (CloudSolrClient cloudClient =
-        new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost), Optional.empty())
-            .build()) {
+        new CloudSolrClient.Builder(Collections.singletonList(zkHost), Optional.empty()).build()) {
       cloudClient.connect();
       Set<String> liveNodes = cloudClient.getClusterState().getLiveNodes();
       int numLiveNodes = (liveNodes != null) ? liveNodes.size() : 0;
diff --git a/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java b/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
index 4ab9713d017..0b8c2a744da 100644
--- a/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
+++ b/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
@@ -32,7 +32,7 @@ import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.store.Directory;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.WaitForState;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -177,9 +177,10 @@ public class RecoveryStrategy implements Runnable, Closeable {
     this.recoveringAfterStartup = recoveringAfterStartup;
   }
 
-  private Http2SolrClient.Builder recoverySolrClientBuilder(String baseUrl, String leaderCoreName) {
+  private HttpJettySolrClient.Builder recoverySolrClientBuilder(
+      String baseUrl, String leaderCoreName) {
     final UpdateShardHandlerConfig cfg = cc.getConfig().getUpdateShardHandlerConfig();
-    return new Http2SolrClient.Builder(baseUrl)
+    return new HttpJettySolrClient.Builder(baseUrl)
         .withDefaultCollection(leaderCoreName)
         .withHttpClient(cc.getUpdateShardHandler().getRecoveryOnlyHttpClient());
   }
diff --git a/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java b/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java
index a686743f6b5..1b2789cb3a0 100644
--- a/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java
+++ b/solr/core/src/java/org/apache/solr/cloud/SyncStrategy.java
@@ -26,7 +26,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.RequestRecovery;
 import org.apache.solr.common.cloud.ZkCoreNodeProps;
 import org.apache.solr.common.cloud.ZkNodeProps;
@@ -53,7 +53,7 @@ public class SyncStrategy {
 
   private volatile boolean isClosed;
 
-  private final Http2SolrClient solrClient;
+  private final HttpJettySolrClient solrClient;
 
   private final ExecutorService updateExecutor;
 
@@ -358,7 +358,7 @@ public class SyncStrategy {
           recoverRequestCmd.setAction(CoreAdminAction.REQUESTRECOVERY);
           recoverRequestCmd.setCoreName(coreName);
           try (SolrClient client =
-              new Http2SolrClient.Builder(baseUrl)
+              new HttpJettySolrClient.Builder(baseUrl)
                   .withHttpClient(solrClient)
                   .withIdleTimeout(120000, TimeUnit.MILLISECONDS)
                   .build()) {
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 84325711d30..f8551e6fb3c 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -61,12 +61,12 @@ import org.apache.curator.framework.api.ACLProvider;
 import org.apache.solr.client.api.util.SolrVersion;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
 import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
 import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
+import org.apache.solr.client.solrj.jetty.CloudJettySolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.WaitForState;
 import org.apache.solr.cloud.api.collections.DistributedCollectionConfigSetCommandRunner;
 import org.apache.solr.cloud.overseer.ClusterStateMutator;
@@ -207,7 +207,7 @@ public class ZkController implements Closeable {
   public final ZkStateReader zkStateReader;
   private SolrCloudManager cloudManager;
 
-  private CloudHttp2SolrClient cloudSolrClient;
+  private CloudSolrClient cloudSolrClient;
 
   private final ExecutorService zkConnectionListenerCallbackExecutor =
       ExecutorUtil.newMDCAwareSingleThreadExecutor(
@@ -959,7 +959,7 @@ public class ZkController implements Closeable {
         return cloudManager;
       }
       cloudSolrClient =
-          new CloudHttp2SolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
+          new CloudJettySolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
               .withHttpClient(cc.getDefaultHttpSolrClient())
               .build();
       cloudManager = new SolrClientCloudManager(cloudSolrClient, cc.getObjectCache());
@@ -2316,7 +2316,7 @@ public class ZkController implements Closeable {
         // TODO ideally want 8sec connection timeout but can't easily also share the client
         // listeners
         try (SolrClient client =
-            new Http2SolrClient.Builder(leaderBaseUrl)
+            new HttpJettySolrClient.Builder(leaderBaseUrl)
                 .withHttpClient(getCoreContainer().getDefaultHttpSolrClient())
                 .withIdleTimeout(30000, TimeUnit.MILLISECONDS)
                 .build()) {
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
index 22f3618d111..85440f4b650 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrResponse;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.UpdateResponse;
@@ -219,7 +219,7 @@ public class CollectionHandlingUtils {
   }
 
   static void commit(
-      Http2SolrClient solrClient,
+      HttpJettySolrClient solrClient,
       NamedList<Object> results,
       String slice,
       Replica parentShardLeader) {
@@ -244,7 +244,7 @@ public class CollectionHandlingUtils {
   }
 
   private static UpdateResponse softCommit(
-      Http2SolrClient solrClient, String baseUrl, String coreName)
+      HttpJettySolrClient solrClient, String baseUrl, String coreName)
       throws SolrServerException, IOException {
     UpdateRequest ureq = new UpdateRequest();
     ureq.setAction(AbstractUpdateRequest.ACTION.COMMIT, false, true, true);
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index be631383c6b..42f8bd2ec7e 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -69,9 +69,9 @@ import org.apache.solr.api.ClusterPluginsSource;
 import org.apache.solr.api.ContainerPluginsRegistry;
 import org.apache.solr.api.JerseyResource;
 import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.client.solrj.io.SolrClientCache;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.cloud.ClusterSingleton;
@@ -2389,10 +2389,10 @@ public class CoreContainer {
    *
    * <p>The caller does not need to close the client.
    *
-   * @return the existing {@link Http2SolrClient}
+   * @return the existing {@link HttpJettySolrClient}
    * @see HttpSolrClientBase#requestWithBaseUrl(String, SolrRequest, String)
    */
-  public Http2SolrClient getDefaultHttpSolrClient() {
+  public HttpJettySolrClient getDefaultHttpSolrClient() {
     return solrClientProvider.getSolrClient();
   }
 
diff --git a/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
index a8c27f27ffa..8168ac420bc 100644
--- a/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
+++ b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
@@ -18,7 +18,7 @@ package org.apache.solr.core;
 
 import io.opentelemetry.api.common.Attributes;
 import java.util.concurrent.TimeUnit;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.metrics.SolrMetricsContext;
 import org.apache.solr.security.HttpClientBuilderPlugin;
@@ -34,7 +34,7 @@ final class HttpSolrClientProvider implements AutoCloseable {
 
   static final String METRIC_SCOPE_NAME = "defaultHttpSolrClientProvider";
 
-  private final Http2SolrClient httpSolrClient;
+  private final HttpJettySolrClient httpSolrClient;
 
   private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
 
@@ -42,8 +42,8 @@ final class HttpSolrClientProvider implements AutoCloseable {
     trackHttpSolrMetrics = new InstrumentedHttpListenerFactory(getNameStrategy(cfg));
     initializeMetrics(parentContext);
 
-    Http2SolrClient.Builder httpClientBuilder =
-        new Http2SolrClient.Builder().addListenerFactory(trackHttpSolrMetrics);
+    var httpClientBuilder =
+        new HttpJettySolrClient.Builder().addListenerFactory(trackHttpSolrMetrics);
 
     if (cfg != null) {
       httpClientBuilder
@@ -68,7 +68,7 @@ final class HttpSolrClientProvider implements AutoCloseable {
     trackHttpSolrMetrics.initializeMetrics(solrMetricsContext, Attributes.empty());
   }
 
-  Http2SolrClient getSolrClient() {
+  HttpJettySolrClient getSolrClient() {
     return httpSolrClient;
   }
 
diff --git a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
index 430fad38971..dc496f3084b 100644
--- a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
+++ b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
@@ -91,9 +91,9 @@ import org.apache.lucene.store.IndexOutput;
 import org.apache.solr.client.api.model.FileMetaData;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.cloud.ZkController;
@@ -179,7 +179,7 @@ public class IndexFetcher {
 
   boolean fetchFromLeader = false;
 
-  private final Http2SolrClient solrClient;
+  private final HttpJettySolrClient solrClient;
 
   private Integer soTimeout;
 
@@ -251,10 +251,10 @@ public class IndexFetcher {
   // It's crucial not to remove the authentication credentials as they are essential for User
   // managed replication.
   // GitHub PR #2276
-  private Http2SolrClient createSolrClient(
+  private HttpJettySolrClient createSolrClient(
       SolrCore core, String httpBasicAuthUser, String httpBasicAuthPassword, String leaderBaseUrl) {
     final UpdateShardHandler updateShardHandler = core.getCoreContainer().getUpdateShardHandler();
-    return new Http2SolrClient.Builder(leaderBaseUrl)
+    return new HttpJettySolrClient.Builder(leaderBaseUrl)
         .withHttpClient(updateShardHandler.getRecoveryOnlyHttpClient())
         .withBasicAuthCredentials(httpBasicAuthUser, httpBasicAuthPassword)
         .withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS)
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index b22626f3339..bbaf39b1931 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -55,8 +55,8 @@ import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
 import org.apache.solr.api.ApiBag;
 import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.io.stream.expr.Expressible;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionRequiringSolrRequest;
 import org.apache.solr.client.solrj.response.SimpleSolrResponse;
 import org.apache.solr.cloud.ZkController;
@@ -844,10 +844,10 @@ public class SolrConfigHandler extends RequestHandlerBase
     // course)
     List<PerReplicaCallable> concurrentTasks = new ArrayList<>();
 
-    var http2SolrClient = zkController.getCoreContainer().getDefaultHttpSolrClient();
+    var httpSolrClient = zkController.getCoreContainer().getDefaultHttpSolrClient();
     for (Replica replica : getActiveReplicas(zkController, collection)) {
       PerReplicaCallable e =
-          new PerReplicaCallable(http2SolrClient, replica, prop, expectedVersion, maxWaitSecs);
+          new PerReplicaCallable(httpSolrClient, replica, prop, expectedVersion, maxWaitSecs);
       concurrentTasks.add(e);
     }
     if (concurrentTasks.isEmpty()) return; // nothing to wait for ...
@@ -963,7 +963,7 @@ public class SolrConfigHandler extends RequestHandlerBase
 
   private static class PerReplicaCallable extends CollectionRequiringSolrRequest<SolrResponse>
       implements Callable<Boolean> {
-    private final Http2SolrClient solrClient;
+    private final HttpJettySolrClient solrClient;
     Replica replica;
     String prop;
     int expectedZkVersion;
@@ -971,7 +971,7 @@ public class SolrConfigHandler extends RequestHandlerBase
     int maxWait;
 
     PerReplicaCallable(
-        Http2SolrClient solrClient,
+        HttpJettySolrClient solrClient,
         Replica replica,
         String prop,
         int expectedZkVersion,
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
index efca9d39fa5..ece36d05ad7 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
@@ -28,7 +28,7 @@ import java.util.TreeSet;
 import org.apache.solr.client.api.model.GetSegmentDataResponse;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
@@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory;
 public class ColStatus {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private final Http2SolrClient solrClient;
+  private final HttpJettySolrClient solrClient;
   private final ClusterState clusterState;
   private final ZkNodeProps props;
 
@@ -65,7 +65,7 @@ public class ColStatus {
       SegmentsInfoRequestHandler.RAW_SIZE_SAMPLING_PERCENT_PARAM;
   public static final String SEGMENTS_PROP = "segments";
 
-  public ColStatus(Http2SolrClient solrClient, ClusterState clusterState, ZkNodeProps props) {
+  public ColStatus(HttpJettySolrClient solrClient, ClusterState clusterState, ZkNodeProps props) {
     this.solrClient = solrClient;
     this.clusterState = clusterState;
     this.props = props;
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShard.java b/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShard.java
index 1bf3dc56f4e..6b23e01a05c 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShard.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/SyncShard.java
@@ -28,7 +28,7 @@ import org.apache.solr.client.api.endpoint.SyncShardApi;
 import org.apache.solr.client.api.model.SolrJerseyResponse;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
@@ -80,7 +80,7 @@ public class SyncShard extends AdminAPIBase implements SyncShardApi {
     Replica leader = docCollection.getLeader(shardName);
 
     try (SolrClient client =
-        new Http2SolrClient.Builder(leader.getBaseUrl())
+        new HttpJettySolrClient.Builder(leader.getBaseUrl())
             .withHttpClient(coreContainer.getDefaultHttpSolrClient())
             .withIdleTimeout(60000, TimeUnit.MILLISECONDS)
             .build()) {
diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
index 0daec7a4825..862283da609 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
@@ -31,10 +31,10 @@ import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.LBAsyncSolrClient;
 import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.routing.AffinityReplicaListTransformerFactory;
@@ -81,7 +81,7 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory
   // This executor is initialized in the init method
   protected ExecutorService commExecutor;
 
-  protected volatile Http2SolrClient defaultClient;
+  protected volatile HttpJettySolrClient defaultClient;
   protected InstrumentedHttpListenerFactory httpListenerFactory;
   protected LBAsyncSolrClient loadbalancer;
 
@@ -301,7 +301,7 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory
             args, SolrHttpConstants.PROP_SO_TIMEOUT, SolrHttpConstants.DEFAULT_SO_TIMEOUT, sb);
 
     this.defaultClient =
-        new Http2SolrClient.Builder()
+        new HttpJettySolrClient.Builder()
             .withConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS)
             .withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS)
             .withExecutor(commExecutor)
diff --git a/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java b/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
index 31f1d95ed0f..670986eedb4 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
@@ -24,7 +24,7 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocumentList;
@@ -38,7 +38,7 @@ public abstract class IterativeMergeStrategy implements MergeStrategy {
 
   protected volatile ExecutorService executorService;
 
-  protected volatile Http2SolrClient httpSolrClient;
+  protected volatile HttpJettySolrClient httpSolrClient;
 
   @Override
   public void merge(ResponseBuilder rb, ShardRequest sreq) {
diff --git a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
index be9b4e8deba..834d5f61610 100644
--- a/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/BasicAuthPlugin.java
@@ -33,7 +33,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.StringTokenizer;
 import javax.security.auth.Subject;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.SpecProvider;
@@ -217,7 +217,7 @@ public class BasicAuthPlugin extends AuthenticationPlugin
   @Override
   protected boolean interceptInternodeRequest(Request request) {
     if (forwardCredentials) {
-      Object userToken = request.getAttributes().get(Http2SolrClient.REQ_PRINCIPAL_KEY);
+      Object userToken = request.getAttributes().get(HttpJettySolrClient.REQ_PRINCIPAL_KEY);
       if (userToken instanceof BasicAuthUserPrincipal principal) {
         String userPassBase64 =
             Base64.getEncoder()
diff --git a/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java b/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
index bc8495e9416..eb1ade431a8 100644
--- a/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/HttpClientBuilderPlugin.java
@@ -16,7 +16,7 @@
  */
 package org.apache.solr.security;
 
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 
 /**
  * Plugin interface for configuring internal HttpClients. This relies on the internal HttpClient
@@ -26,5 +26,5 @@ import org.apache.solr.client.solrj.impl.Http2SolrClient;
  */
 public interface HttpClientBuilderPlugin {
 
-  public default void setup(Http2SolrClient client) {}
+  public default void setup(HttpJettySolrClient client) {}
 }
diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
index baec6f23e3b..514e1317290 100644
--- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
@@ -41,8 +41,8 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
-import org.apache.solr.client.solrj.impl.HttpListenerFactory;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
+import org.apache.solr.client.solrj.jetty.HttpListenerFactory;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
@@ -403,7 +403,7 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin
   }
 
   @Override
-  public void setup(Http2SolrClient client) {
+  public void setup(HttpJettySolrClient client) {
     final HttpListenerFactory.RequestResponseListener listener =
         new HttpListenerFactory.RequestResponseListener() {
           private static final String CACHED_REQUEST_USER_KEY = "cachedRequestUser";
diff --git a/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java b/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
index b6dc8e14fdf..00097cb981d 100644
--- a/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
+++ b/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
@@ -29,7 +29,7 @@ import java.util.List;
 import java.util.Set;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrException;
@@ -56,7 +56,7 @@ public class PeerSyncWithLeader implements SolrMetricProducer {
 
   private UpdateHandler uhandler;
   private UpdateLog ulog;
-  private final Http2SolrClient clientToLeader;
+  private final HttpJettySolrClient clientToLeader;
   private final String coreName;
   private final String leaderBaseUrl;
 
diff --git a/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java b/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
index 35bfc79dc63..1b281abf802 100644
--- a/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
+++ b/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
@@ -28,8 +28,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateJettySolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.ConcurrentUpdateJettySolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.update.SolrCmdDistributor.SolrError;
@@ -45,7 +45,7 @@ public class StreamingSolrClients {
   private final int pollQueueTimeMillis =
       Integer.getInteger("solr.cloud.client.pollQueueTime", 10000);
 
-  private Http2SolrClient httpClient;
+  private HttpJettySolrClient httpClient;
 
   private Map<String, ConcurrentUpdateBaseSolrClient> solrClients = new HashMap<>();
   private List<SolrError> errors = Collections.synchronizedList(new ArrayList<>());
@@ -114,7 +114,7 @@ public class StreamingSolrClients {
     return fullUrl;
   }
 
-  public Http2SolrClient getHttpClient() {
+  public HttpJettySolrClient getHttpClient() {
     return httpClient;
   }
 
@@ -168,7 +168,7 @@ class ErrorReportingConcurrentUpdateSolrClient extends ConcurrentUpdateJettySolr
      */
     public Builder(
         String baseSolrUrl,
-        Http2SolrClient client,
+        HttpJettySolrClient client,
         SolrCmdDistributor.Req req,
         List<SolrError> errors) {
       super(baseSolrUrl, client);
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
index 8410de18ffa..8abb54c066d 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
@@ -24,8 +24,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
@@ -64,9 +64,9 @@ public class UpdateShardHandler implements SolrInfoBean {
 
   private ExecutorService recoveryExecutor;
 
-  private final Http2SolrClient updateOnlyClient;
+  private final HttpJettySolrClient updateOnlyClient;
 
-  private final Http2SolrClient recoveryOnlyClient;
+  private final HttpJettySolrClient recoveryOnlyClient;
 
   private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
 
@@ -99,8 +99,8 @@ public class UpdateShardHandler implements SolrInfoBean {
         Set.of(
             DistributedUpdateProcessor.DISTRIB_FROM,
             DistributingUpdateProcessorFactory.DISTRIB_UPDATE_PARAM);
-    Http2SolrClient.Builder updateOnlyClientBuilder = new Http2SolrClient.Builder();
-    Http2SolrClient.Builder recoveryOnlyClientBuilder = new Http2SolrClient.Builder();
+    var updateOnlyClientBuilder = new HttpJettySolrClient.Builder();
+    var recoveryOnlyClientBuilder = new HttpJettySolrClient.Builder();
     if (cfg != null) {
       updateOnlyClientBuilder
           .addListenerFactory(trackHttpSolrMetrics)
@@ -189,12 +189,12 @@ public class UpdateShardHandler implements SolrInfoBean {
   }
 
   // don't introduce a bug, this client is for sending updates only!
-  public Http2SolrClient getUpdateOnlyHttpClient() {
+  public HttpJettySolrClient getUpdateOnlyHttpClient() {
     return updateOnlyClient;
   }
 
   // don't introduce a bug, this client is for recovery ops only!
-  public Http2SolrClient getRecoveryOnlyHttpClient() {
+  public HttpJettySolrClient getRecoveryOnlyHttpClient() {
     return recoveryOnlyClient;
   }
 
diff --git a/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java b/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
index e31e02b85d2..9f38d8845da 100644
--- a/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
+++ b/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
@@ -23,7 +23,7 @@ import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.trace.Span;
 import java.util.Locale;
 import java.util.Map;
-import org.apache.solr.client.solrj.impl.HttpListenerFactory;
+import org.apache.solr.client.solrj.jetty.HttpListenerFactory;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.CollectionUtil;
 import org.apache.solr.metrics.SolrMetricProducer;
diff --git a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
index d356c439849..13ad5859e3d 100644
--- a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
+++ b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
@@ -34,7 +34,7 @@ import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec;
@@ -216,7 +216,7 @@ public class TestExportTool extends SolrCloudTestCase {
       long totalDocsFromCores = 0;
       for (Slice slice : coll.getSlices()) {
         Replica replica = slice.getLeader();
-        try (SolrClient client = new Http2SolrClient.Builder(replica.getBaseUrl()).build()) {
+        try (SolrClient client = new HttpJettySolrClient.Builder(replica.getBaseUrl()).build()) {
           long count = ExportTool.getDocCount(replica.getCoreName(), client, "*:*");
           docCounts.put(replica.getCoreName(), count);
           totalDocsFromCores += count;
diff --git a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
index d7680eaff7d..b1aaf9e457b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
@@ -50,10 +50,10 @@ import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
 import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.cloud.overseer.NodeMutator;
 import org.apache.solr.cloud.overseer.OverseerAction;
 import org.apache.solr.cloud.overseer.ZkWriteCommand;
@@ -1826,12 +1826,12 @@ public class OverseerTest extends SolrTestCaseJ4 {
 
   private SolrCloudManager getCloudDataProvider(ZkStateReader zkStateReader) {
     var httpSolrClient =
-        new Http2SolrClient.Builder()
+        new HttpJettySolrClient.Builder()
             .withIdleTimeout(30000, TimeUnit.MILLISECONDS)
             .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
             .build();
     var cloudSolrClient =
-        new CloudHttp2SolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
+        new CloudSolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
             .withHttpClient(httpSolrClient)
             .build();
     solrClients.add(cloudSolrClient);
diff --git a/solr/core/src/test/org/apache/solr/cloud/RecoveryZkTestWithAuth.java b/solr/core/src/test/org/apache/solr/cloud/RecoveryZkTestWithAuth.java
index 77c910a8da2..688543c8487 100644
--- a/solr/core/src/test/org/apache/solr/cloud/RecoveryZkTestWithAuth.java
+++ b/solr/core/src/test/org/apache/solr/cloud/RecoveryZkTestWithAuth.java
@@ -29,6 +29,7 @@ import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -49,7 +50,7 @@ public class RecoveryZkTestWithAuth extends SolrCloudTestCase {
     if (rarely()) {
       System.setProperty(
           SolrHttpConstants.SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY,
-          "org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory");
+          PreemptiveBasicAuthClientBuilderFactory.class.getName());
       System.setProperty("solr.security.auth.basicauth.credentials", SecurityJson.USER_PASS);
     }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterSSL.java b/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterSSL.java
index c82864b7f6f..457429a21ac 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterSSL.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestMiniSolrCloudClusterSSL.java
@@ -38,8 +38,8 @@ import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.HttpClientUtil;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.common.cloud.ZkStateReader;
@@ -90,14 +90,14 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     log.info(
         "NOTE: This Test ignores the randomized SSL & clientAuth settings selected by base class");
     HttpClientUtil.resetHttpClientBuilder(); // also resets SocketFactoryRegistryProvider
-    Http2SolrClient.resetSslContextFactory();
+    HttpJettySolrClient.resetSslContextFactory();
     System.clearProperty(ZkStateReader.URL_SCHEME);
   }
 
   @After
   public void after() {
     HttpClientUtil.resetHttpClientBuilder(); // also resets SocketFactoryRegistryProvider
-    Http2SolrClient.resetSslContextFactory();
+    HttpJettySolrClient.resetSslContextFactory();
     System.clearProperty(ZkStateReader.URL_SCHEME);
     SSLContext.setDefault(DEFAULT_SSL_CONTEXT);
   }
@@ -106,7 +106,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     final SSLTestConfig sslConfig = new SSLTestConfig(false, false);
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider()); // must be reset
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig()); // must be reset
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig()); // must be reset
     System.setProperty(ZkStateReader.URL_SCHEME, "http");
     checkClusterWithNodeReplacement(sslConfig);
   }
@@ -118,7 +118,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     final SSLTestConfig sslConfig = new SSLTestConfig(false, true);
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     System.setProperty(ZkStateReader.URL_SCHEME, "http");
     checkClusterWithNodeReplacement(sslConfig);
   }
@@ -127,7 +127,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     final SSLTestConfig sslConfig = new SSLTestConfig(true, false);
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     System.setProperty(ZkStateReader.URL_SCHEME, "https");
     checkClusterWithNodeReplacement(sslConfig);
   }
@@ -139,7 +139,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
 
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     System.setProperty(ZkStateReader.URL_SCHEME, "https");
     checkClusterWithNodeReplacement(sslConfig);
   }
@@ -148,7 +148,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     final SSLTestConfig sslConfig = new SSLTestConfig(true, false, true);
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     System.setProperty(ZkStateReader.URL_SCHEME, "https");
     checkClusterWithNodeReplacement(sslConfig);
   }
@@ -181,7 +181,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
           SolrHttpConstants.SYS_PROP_CHECK_PEER_NAME,
           Boolean.toString(sslConfig.getCheckPeerName()));
       HttpClientUtil.resetHttpClientBuilder();
-      Http2SolrClient.resetSslContextFactory();
+      HttpJettySolrClient.resetSslContextFactory();
 
       // recheck that we can communicate with all the jetty instances in our cluster
       checkClusterJettys(cluster, sslConfig);
@@ -197,7 +197,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     final SSLTestConfig sslConfig = new SSLTestConfig(true, false, false);
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     System.setProperty(ZkStateReader.URL_SCHEME, "https");
     final JettyConfig config =
         JettyConfig.builder().withSSLConfig(sslConfig.buildServerSSLConfig()).build();
@@ -210,7 +210,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
       // our existing certificate, but *does* care about validating the peer name
       System.setProperty(SolrHttpConstants.SYS_PROP_CHECK_PEER_NAME, "true");
       HttpClientUtil.resetHttpClientBuilder();
-      Http2SolrClient.resetSslContextFactory();
+      HttpJettySolrClient.resetSslContextFactory();
 
       // and validate we get failures when trying to talk to our cluster...
       final List<JettySolrRunner> jettys = cluster.getJettySolrRunners();
@@ -432,7 +432,7 @@ public class TestMiniSolrCloudClusterSSL extends SolrTestCaseJ4 {
     // ... so we are hopefully future proofing against possible changes to
     // SolrTestCaseJ4.getHttpSolrClient that "optimize" the test client construction in a way that
     // would prevent us from finding bugs with regular HttpSolrClient instantiation.
-    // This test fails if you return a Http2SolrClient
+    // This test fails if you return a HttpJettySolrClient
     if (random().nextBoolean()) {
       return (new HttpSolrClient.Builder(url)).build();
     } // else...
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java b/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
index 6254d3d2917..cc353b3ff60 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestQueryingOnDownCollection.java
@@ -21,7 +21,7 @@ import java.util.Map;
 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.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -103,7 +103,7 @@ public class TestQueryingOnDownCollection extends SolrCloudTestCase {
 
     // run same set of tests on v2 client which uses V2HttpCall
     try (SolrClient v2Client =
-        new Http2SolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
+        new HttpJettySolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
             .build()) {
 
       SolrException error =
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
index 9eab3a3cafe..7b5840efc64 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkControllerTest.java
@@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.api.util.SolrVersion;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.MapWriter;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ClusterProperties;
@@ -784,7 +784,7 @@ public class ZkControllerTest extends SolrCloudTestCase {
   private static class MockCoreContainer extends CoreContainer {
     UpdateShardHandler updateShardHandler =
         new UpdateShardHandler(UpdateShardHandlerConfig.DEFAULT);
-    Http2SolrClient solrClient;
+    HttpJettySolrClient solrClient;
 
     public MockCoreContainer() {
       super(SolrXmlConfig.fromString(TEST_PATH(), "<solr/>"));
@@ -793,7 +793,7 @@ public class ZkControllerTest extends SolrCloudTestCase {
       this.shardHandlerFactory = httpShardHandlerFactory;
       this.coreAdminHandler = new CoreAdminHandler();
       this.metricManager = mock(SolrMetricManager.class);
-      this.solrClient = new Http2SolrClient.Builder().build();
+      this.solrClient = new HttpJettySolrClient.Builder().build();
     }
 
     @Override
@@ -812,7 +812,7 @@ public class ZkControllerTest extends SolrCloudTestCase {
     }
 
     @Override
-    public Http2SolrClient getDefaultHttpSolrClient() {
+    public HttpJettySolrClient getDefaultHttpSolrClient() {
       return solrClient;
     }
 
diff --git a/solr/core/src/test/org/apache/solr/core/snapshots/TestSolrCoreSnapshots.java b/solr/core/src/test/org/apache/solr/core/snapshots/TestSolrCoreSnapshots.java
index 478a67aa479..3ee972c063b 100644
--- a/solr/core/src/test/org/apache/solr/core/snapshots/TestSolrCoreSnapshots.java
+++ b/solr/core/src/test/org/apache/solr/core/snapshots/TestSolrCoreSnapshots.java
@@ -34,7 +34,7 @@ import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.CreateSnapshot;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.DeleteSnapshot;
@@ -110,7 +110,7 @@ public class TestSolrCoreSnapshots extends SolrCloudTestCase {
     try (SolrClient adminClient =
             getHttpSolrClient(cluster.getJettySolrRunners().get(0).getBaseUrl().toString());
         SolrClient leaderClient =
-            new Http2SolrClient.Builder(replica.getBaseUrl())
+            new HttpJettySolrClient.Builder(replica.getBaseUrl())
                 .withDefaultCollection(replica.getCoreName())
                 .build()) {
 
@@ -204,7 +204,7 @@ public class TestSolrCoreSnapshots extends SolrCloudTestCase {
     try (SolrClient adminClient =
             getHttpSolrClient(cluster.getJettySolrRunners().get(0).getBaseUrl().toString());
         SolrClient leaderClient =
-            new Http2SolrClient.Builder(replica.getBaseUrl())
+            new HttpJettySolrClient.Builder(replica.getBaseUrl())
                 .withDefaultCollection(replica.getCoreName())
                 .build()) {
 
diff --git a/solr/core/src/test/org/apache/solr/handler/ReplicationTestHelper.java b/solr/core/src/test/org/apache/solr/handler/ReplicationTestHelper.java
index 1127bbb48be..0a25972f7ac 100644
--- a/solr/core/src/test/org/apache/solr/handler/ReplicationTestHelper.java
+++ b/solr/core/src/test/org/apache/solr/handler/ReplicationTestHelper.java
@@ -37,7 +37,7 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrRequest.SolrRequestType;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocumentList;
@@ -85,8 +85,8 @@ public final class ReplicationTestHelper {
    * @param baseUrl the root URL for a Solr node
    * @param collectionOrCore an optional default collection/core for the created client
    */
-  public static Http2SolrClient createNewSolrClient(String baseUrl, String collectionOrCore) {
-    return new Http2SolrClient.Builder(baseUrl)
+  public static HttpJettySolrClient createNewSolrClient(String baseUrl, String collectionOrCore) {
+    return new HttpJettySolrClient.Builder(baseUrl)
         .withDefaultCollection(collectionOrCore)
         .withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
         .withIdleTimeout(90000, TimeUnit.MILLISECONDS)
diff --git a/solr/core/src/test/org/apache/solr/handler/TestHttpRequestId.java b/solr/core/src/test/org/apache/solr/handler/TestHttpRequestId.java
index eedd23870bb..7865c74d954 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestHttpRequestId.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestHttpRequestId.java
@@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.solr.SolrJettyTestBase;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.SolrPing;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.NamedList;
@@ -39,7 +39,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.slf4j.MDC;
 
-@LogLevel("org.apache.solr.client.solrj.impl.Http2SolrClient=DEBUG")
+@LogLevel("org.apache.solr.client.solrj.jetty.HttpJettySolrClient=DEBUG")
 public class TestHttpRequestId extends SolrJettyTestBase {
 
   @BeforeClass
@@ -82,7 +82,7 @@ public class TestHttpRequestId extends SolrJettyTestBase {
     final String value = "TestHttpRequestId" + System.nanoTime();
 
     try (LogListener reqLog =
-        LogListener.debug(Http2SolrClient.class).substring("response processing")) {
+        LogListener.debug(HttpJettySolrClient.class).substring("response processing")) {
       // client setup needs to be same as HttpShardHandlerFactory
       ThreadPoolExecutor commExecutor =
           new ExecutorUtil.MDCAwareThreadPoolExecutor(
@@ -94,8 +94,8 @@ public class TestHttpRequestId extends SolrJettyTestBase {
               new SolrNamedThreadFactory("httpShardExecutor"),
               false);
       CompletableFuture<NamedList<Object>> cf;
-      try (Http2SolrClient client =
-          new Http2SolrClient.Builder(getBaseUrl())
+      try (var client =
+          new HttpJettySolrClient.Builder(getBaseUrl())
               .withDefaultCollection(collection)
               .withExecutor(commExecutor)
               .build()) {
diff --git a/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java b/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
index af7d03fec4c..a004ce614a2 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
@@ -33,7 +33,7 @@ import org.apache.solr.client.solrj.SolrQuery;
 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.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.HealthCheckRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -49,7 +49,7 @@ import org.junit.Test;
 @SuppressSSL
 public class TestUserManagedReplicationWithAuth extends SolrTestCaseJ4 {
   JettySolrRunner leaderJetty, followerJetty, followerJettyWithAuth;
-  Http2SolrClient leaderClient, followerClient, followerClientWithAuth;
+  HttpJettySolrClient leaderClient, followerClient, followerClientWithAuth;
   ReplicationTestHelper.SolrInstance leader = null, follower = null, followerWithAuth = null;
 
   private static String user = "solr";
@@ -226,7 +226,7 @@ public class TestUserManagedReplicationWithAuth extends SolrTestCaseJ4 {
     return withBasicAuth(new QueryRequest(q)).process(client);
   }
 
-  private void disablePoll(JettySolrRunner Jetty, Http2SolrClient solrClient)
+  private void disablePoll(JettySolrRunner Jetty, HttpJettySolrClient solrClient)
       throws SolrServerException, IOException {
     ModifiableSolrParams disablePollParams = new ModifiableSolrParams();
     disablePollParams.set(COMMAND, CMD_DISABLE_POLL);
diff --git a/solr/core/src/test/org/apache/solr/packagemanager/TestPackageManager.java b/solr/core/src/test/org/apache/solr/packagemanager/TestPackageManager.java
index 2d4892239f0..58089790229 100644
--- a/solr/core/src/test/org/apache/solr/packagemanager/TestPackageManager.java
+++ b/solr/core/src/test/org/apache/solr/packagemanager/TestPackageManager.java
@@ -24,7 +24,7 @@ import java.util.Map;
 import org.apache.solr.cli.CLITestHelper;
 import org.apache.solr.cli.ToolRuntime;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -49,7 +49,7 @@ public class TestPackageManager extends SolrCloudTestCase {
   public void testWrongVerificationJPathIsThrown() throws IOException {
     SolrZkClient zkClient = cluster.getZkClient();
     URL baseURLV2 = cluster.getJettySolrRunner(0).getBaseURLV2();
-    try (Http2SolrClient solrClient = new Http2SolrClient.Builder(baseURLV2.toString()).build()) {
+    try (var solrClient = new HttpJettySolrClient.Builder(baseURLV2.toString()).build()) {
       ToolRuntime runtime = new CLITestHelper.TestingRuntime(false);
       try (PackageManager manager = new StubPackageManager(runtime, solrClient, zkClient)) {
         SolrPackage.Plugin plugin = new SolrPackage.Plugin();
diff --git a/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java b/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
index 65ff8220c6d..0d48114e045 100644
--- a/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
+++ b/solr/core/src/test/org/apache/solr/search/TestCoordinatorRole.java
@@ -39,7 +39,7 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -466,7 +466,7 @@ public class TestCoordinatorRole extends SolrCloudTestCase {
             CommonParams.JAVABIN);
     p.accept(q);
     SolrDocumentList docs = null;
-    try (SolrClient solrClient = new Http2SolrClient.Builder(qaNode).build()) {
+    try (SolrClient solrClient = new HttpJettySolrClient.Builder(qaNode).build()) {
       for (int i = 0; i < 100; i++) {
         try {
           QueryResponse queryResponse = solrClient.query(COLL, q);
diff --git a/solr/core/src/test/org/apache/solr/security/AuthWithShardHandlerFactoryOverrideTest.java b/solr/core/src/test/org/apache/solr/security/AuthWithShardHandlerFactoryOverrideTest.java
index eff43b40d22..b1dfedc3f78 100644
--- a/solr/core/src/test/org/apache/solr/security/AuthWithShardHandlerFactoryOverrideTest.java
+++ b/solr/core/src/test/org/apache/solr/security/AuthWithShardHandlerFactoryOverrideTest.java
@@ -18,7 +18,7 @@ package org.apache.solr.security;
 
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.cloud.SolrCloudAuthTestCase;
@@ -83,7 +83,7 @@ public class AuthWithShardHandlerFactoryOverrideTest extends SolrCloudAuthTestCa
   @Test
   public void collectionTest() throws Exception {
     try (SolrClient client =
-        new Http2SolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
+        new HttpJettySolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
             .build()) {
 
       for (int i = 0; i < 30; i++) {
@@ -103,7 +103,7 @@ public class AuthWithShardHandlerFactoryOverrideTest extends SolrCloudAuthTestCa
   @Test
   public void aliasTest() throws Exception {
     try (SolrClient client =
-        new Http2SolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
+        new HttpJettySolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
             .build()) {
 
       for (int i = 0; i < 30; i++) {
diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthOnSingleNodeTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthOnSingleNodeTest.java
index 8b6708fe825..183e6ba3235 100644
--- a/solr/core/src/test/org/apache/solr/security/BasicAuthOnSingleNodeTest.java
+++ b/solr/core/src/test/org/apache/solr/security/BasicAuthOnSingleNodeTest.java
@@ -18,7 +18,7 @@
 package org.apache.solr.security;
 
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.cloud.SolrCloudAuthTestCase;
@@ -52,7 +52,7 @@ public class BasicAuthOnSingleNodeTest extends SolrCloudAuthTestCase {
   @Test
   public void basicTest() throws Exception {
     try (SolrClient client =
-        new Http2SolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
+        new HttpJettySolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
             .build()) {
 
       // SOLR-13510, this will be failed if the listener (handling inject credential in header) is
@@ -69,7 +69,7 @@ public class BasicAuthOnSingleNodeTest extends SolrCloudAuthTestCase {
   @Test
   public void testDeleteSecurityJsonZnode() throws Exception {
     try (SolrClient client =
-        new Http2SolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
+        new HttpJettySolrClient.Builder(cluster.getJettySolrRunner(0).getBaseUrl().toString())
             .build()) {
       try {
         new QueryRequest(params("q", "*:*")).process(client, COLLECTION);
diff --git a/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java b/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java
index e822352bc39..806040c755c 100644
--- a/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java
+++ b/solr/core/src/test/org/apache/solr/util/tracing/TestSimplePropagatorDistributedTracing.java
@@ -27,7 +27,6 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
@@ -88,7 +87,7 @@ public class TestSimplePropagatorDistributedTracing extends SolrCloudTestCase {
         assertSameTraceId(reqLog, traceId);
       }
 
-      try (SolrClient testClient = newCloudHttp2SolrClient()) {
+      try (SolrClient testClient = newCloudSolrClient()) {
         // verify all query events have the same auto-generated trace id
         var r1 = testClient.query(COLLECTION, new SolrQuery("*:*"));
         assertEquals(0, r1.getStatus());
@@ -132,7 +131,7 @@ public class TestSimplePropagatorDistributedTracing extends SolrCloudTestCase {
         assertSameTraceId(reqLog, traceId);
       }
 
-      try (SolrClient testClient = newCloudHttp2SolrClient()) {
+      try (SolrClient testClient = newCloudSolrClient()) {
         // verify all indexing events have trace id present
         testClient.add(COLLECTION, sdoc("id", "2"));
         testClient.add(COLLECTION, sdoc("id", "4"));
@@ -177,9 +176,9 @@ public class TestSimplePropagatorDistributedTracing extends SolrCloudTestCase {
         .build();
   }
 
-  private CloudHttp2SolrClient newCloudHttp2SolrClient() {
+  private CloudSolrClient newCloudSolrClient() {
     var builder =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
             List.of(cluster.getZkServer().getZkAddress()), Optional.empty());
     var client = builder.build();
     client.connect();
diff --git a/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java b/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
index 7e6e9f1bd65..bf1ba691f26 100644
--- a/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
+++ b/solr/cross-dc-manager/src/java/org/apache/solr/crossdc/manager/consumer/KafkaCrossDcConsumer.java
@@ -43,7 +43,6 @@ import org.apache.kafka.common.errors.WakeupException;
 import org.apache.kafka.common.serialization.StringDeserializer;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
@@ -122,7 +121,7 @@ public class KafkaCrossDcConsumer extends Consumer.CrossDcConsumer {
 
     protected CloudSolrClient createSolrClient() {
       log.debug("Creating new SolrClient...");
-      return new CloudHttp2SolrClient.Builder(
+      return new CloudSolrClient.Builder(
               Collections.singletonList(zkConnectString), Optional.empty())
           .build();
     }
diff --git a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
index 634245c4b56..83d0e99303b 100644
--- a/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
+++ b/solr/modules/jwt-auth/src/java/org/apache/solr/security/jwt/JWTAuthPlugin.java
@@ -46,7 +46,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SpecProvider;
 import org.apache.solr.common.util.CommandOperation;
@@ -941,7 +941,7 @@ public class JWTAuthPlugin extends AuthenticationPlugin
 
   @Override
   protected boolean interceptInternodeRequest(Request request) {
-    Object userToken = request.getAttributes().get(Http2SolrClient.REQ_PRINCIPAL_KEY);
+    Object userToken = request.getAttributes().get(HttpJettySolrClient.REQ_PRINCIPAL_KEY);
     if (userToken instanceof JWTPrincipal jwtPrincipal) {
       request.headers(
           h -> h.put(HttpHeader.AUTHORIZATION.asString(), "Bearer " + jwtPrincipal.getToken()));
diff --git a/solr/solr-ref-guide/modules/deployment-guide/examples/UsingSolrJRefGuideExamplesTest.java b/solr/solr-ref-guide/modules/deployment-guide/examples/UsingSolrJRefGuideExamplesTest.java
index dc8e860a363..d1d08f1b6bc 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/examples/UsingSolrJRefGuideExamplesTest.java
+++ b/solr/solr-ref-guide/modules/deployment-guide/examples/UsingSolrJRefGuideExamplesTest.java
@@ -31,8 +31,8 @@ import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrQuery.ORDER;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.beans.Field;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.response.CollectionAdminResponse;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -242,7 +242,7 @@ public class UsingSolrJRefGuideExamplesTest extends SolrCloudTestCase {
   private SolrClient getTechProductSolrClient() {
     // tag::solrj-solrclient-timeouts[]
     final String solrUrl = "http://localhost:8983/solr";
-    return new Http2SolrClient.Builder(solrUrl)
+    return new HttpJettySolrClient.Builder(solrUrl)
         .withConnectionTimeout(10000, TimeUnit.MILLISECONDS)
         .withIdleTimeout(60000, TimeUnit.MILLISECONDS)
         .build();
@@ -254,7 +254,7 @@ public class UsingSolrJRefGuideExamplesTest extends SolrCloudTestCase {
     final List<String> solrUrls = new ArrayList<>();
     solrUrls.add("http://solr1:8983/solr");
     solrUrls.add("http://solr2:8983/solr");
-    return new CloudHttp2SolrClient.Builder(solrUrls).build();
+    return new CloudSolrClient.Builder(solrUrls).build();
     // end::solrj-cloudsolrclient-baseurl[]
   }
 
@@ -264,7 +264,7 @@ public class UsingSolrJRefGuideExamplesTest extends SolrCloudTestCase {
     zkServers.add("zookeeper1:2181");
     zkServers.add("zookeeper2:2181");
     zkServers.add("zookeeper3:2181");
-    return new CloudHttp2SolrClient.Builder(zkServers, Optional.empty()).build();
+    return new CloudSolrClient.Builder(zkServers, Optional.empty()).build();
     // end::solrj-cloudsolrclient-zookeepernoroot[]
   }
 
@@ -274,7 +274,7 @@ public class UsingSolrJRefGuideExamplesTest extends SolrCloudTestCase {
     zkServers.add("zookeeper1:2181");
     zkServers.add("zookeeper2:2181");
     zkServers.add("zookeeper3:2181");
-    return new CloudHttp2SolrClient.Builder(zkServers, Optional.of("/solr")).build();
+    return new CloudSolrClient.Builder(zkServers, Optional.of("/solr")).build();
     // end::solrj-cloudsolrclient-zookeeperroot[]
   }
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
index 490f932582e..204731a89e4 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/basic-authentication-plugin.adoc
@@ -336,30 +336,30 @@ While this is method is simple, it can often be inconvenient to ensure the crede
 It also doesn't work with the many `SolrClient` methods which don't consume `SolrRequest` objects.
 
 === Per-Client Credentials
-Http2SolrClient supports setting the credentials at the client level when building it.
+HttpJettySolrClient & HttpJdkSolrClient support setting the credentials at the client level when building it.
 This will ensure all requests issued with this particular client get the Basic Authentication headers set.
 
 [source,java]
 ----
-Http2SolrClient client = new Http2SolrClient.Builder(solrUrl)
+var client = new HttpJettySolrClient.Builder(solrUrl)
         .withBasicAuthCredentials(userName, password).build();
 QueryResponse rsp = req.process(client);
 ----
 
-CloudHttp2SolrClient supports receiving an `Http2SolrClient.Builder` instance for creating its internal client, so to set the credentials at the client level you could use a code like:
+CloudSolrClient's Builder supports receiving an `Http2SolrClient.Builder` instance for creating its internal client, so to set the credentials at the client level you could use a code like:
 
 [source,java]
 ----
-Http2SolrClient.Builder http2ClientBuilder = Http2SolrClient.Builder().withBasicAuthCredentials(userName, password);
-CloudHttp2SolrClient client = new CloudHttp2SolrClient.Builder(zkHostList, chroot)
-            .withInternalClientBuilder(http2ClientBuilder).build();
+var httpClientBuilder = HttpJettySolrClient.Builder().withBasicAuthCredentials(userName, password);
+var client = new CloudSolrClient.Builder(zkHostList, chroot)
+            .withInternalClientBuilder(httpClientBuilder).build();
 QueryResponse rsp = req.process(client);
 ----
 
 === Global (JVM) Basic Auth Credentials
 
 Alternatively, users can use SolrJ's `PreemptiveBasicAuthClientBuilderFactory` to add basic authentication credentials to _all_ requests automatically.
-To enable this feature, users should set the following system property `-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory`.
+To enable this feature, users should set the following system property `-Dsolr.httpclient.builder.factory=org.apache.solr.client.solrj.jetty.PreemptiveBasicAuthClientBuilderFactory`.
 `PreemptiveBasicAuthClientBuilderFactory` allows applications to provide credentials in two different ways:
 
 . The `solr.security.auth.basicauth.credentials` system property can be passed, containing the credentials directly (e.g., `-Dsolr.security.auth.basicauth.credentials=username:password`).
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc
index dc7c2341ab2..dd8998b8e19 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/enabling-ssl.adoc
@@ -254,9 +254,9 @@ Note that the truststore file is not actively monitored, so if you need to apply
 to update it and after that touch the keystore to trigger a reload.
 
 === SolrJ client
-Http2SolrClient builder has a method `withKeyStoreReloadInterval(long interval, TimeUnit unit)` to initialize a scanner
-that will watch and update the keystore and truststore for changes. If you are using CloudHttp2SolrClient, you can use
-the `withInternalClientBuilder(Http2SolrClient.Builder internalClientBuilder)` to configure the internal http client
+The HTTP SolrClient builder has a method `withKeyStoreReloadInterval(long interval, TimeUnit unit)` to initialize a scanner
+that will watch and update the keystore and truststore for changes. If you are using CloudSolrClient, you can use
+the `withHttpClientBuilder(internalClientBuilder)` to configure the internal http client
 with a keystore reload interval. The minimum reload interval is 1 second. If not set (or set to 0 or a negative value),
 the keystore/truststore won't be updated in the client.
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
index 6df63275c39..3913d16e3a9 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
@@ -95,11 +95,11 @@ Requests are sent in the form of {solr-javadocs}/solrj/org/apache/solr/client/so
 
 `SolrClient` has a few concrete implementations, each geared towards a different usage-pattern or resiliency model:
 
-- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/Http2SolrClient.html[`Http2SolrClient`] - a general purpose client based on Jetty HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Most used & tested.
+- {solr-javadocs}/solrj/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.html[`HttpJettySolrClient`] - a general purpose client based on Jetty HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Most used & tested.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.html[`HttpJdkSolrClient`] - a general purpose client based on JDK HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Has no dependencies.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBSolrClient.html[`LBSolrClient`] - an internal client that delegates to other clients pointed at different URLs for fail-over/availability.  Adjusts the list of "in-service" nodes based on node health.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/CloudSolrClient.html[`CloudSolrClient`] - the ideal client for SolrCloud.  Using the "cluster state", it routes requests to the optimal nodes, including splitting out the documents in an UpdateRequest to different nodes.
-- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.html[`ConcurrentUpdateJettySolrClient`] - geared towards indexing-centric workloads.
+- {solr-javadocs}/solrj/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClient.html[`ConcurrentUpdateJettySolrClient`] - geared towards indexing-centric workloads.
 Buffers documents internally before sending larger batches to Solr.
 
 === Common Configuration Options
@@ -114,8 +114,8 @@ Unless otherwise specified, SolrJ expects these URLs to point to the root Solr p
 
 A few notable exceptions to this are described below:
 
-- *Http2SolrClient* - Users of `Http2SolrClient` may choose to skip providing a root URL to their client, in favor of specifying the URL as an argument for the `Http2SolrClient.requestWithBaseUrl` method.
-Calling any other `request` methods on a URL-less `Http2SolrClient` will result in an `IllegalArgumentException`.
+- *HttpJettySolrClient* & *HttpJdkSolrClient* - Users of these may choose to skip providing a root URL to their client, in favor of specifying the URL as an argument for the `requestWithBaseUrl` method.
+Calling any other `request` methods on a URL-less `HttpJettySolrClient` will result in an `IllegalArgumentException`.
 - *LBSolrClient* - Solr's "load balancing" client is frequently used to round-robin requests across a set of replicas or cores.
 URLs are still expected to point to the Solr root (i.e. "/solr"), but to support this use-case the URLs are often supplemented by an additional parameter to specify the targeted core.
 Alternatively, some "load balancing" methods make use of an `Endpoint` abstraction to provide this URL and core information in a more structured way.
diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
index e8707afb94b..8647a3d6400 100644
--- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
+++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
@@ -73,6 +73,12 @@ That no longer happens in Solr 10.  It is only used to load a configuration as a
 
 * Starting in 10, the Maven POM for SolrJ does not refer to SolrJ modules like ZooKeeper.  If you require such functionality, you need to add additional dependencies.
 
+* Classes using Jetty HttpClient have been moved to a new package `org.apache.solr.solrj.jetty`.
+The following classes were renamed with some refactorings: `Http2SolrClient` to `HttpJettySolrClient`, `ConcurrentUpdateHttp2SolrClient` to `ConcurrentUpdateJettySolrClient`, `LBHttp2SolrClient` to `LBJettySolrClient`.
+`CloudHttp2SolrClient.Builder` has moved to `CloudSolrClient`; users should generally have no need to refer to `CloudHttp2SolrClient` or any class/member with "http2" in it.
+The builder will check if Jetty `HttpClient` is available and use that, otherwise fallback on a JDK based `HttpClient`.
+`CloudJettySolrClient` is new, proving an explicit option.
+
 * `SolrClient` implementations that rely on "base URL" strings now only accept "root" URL paths (i.e. URLs that end in "/solr").
 Users who previously relied on collection-specific URLs to avoid including the collection name with each request can instead achieve this by specifying a "default collection" using the `withDefaultCollection` method available on most `SolrClient` Builders.
 
diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-8.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-8.adoc
index 5e21087a965..105c61dee70 100644
--- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-8.adoc
+++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-8.adoc
@@ -80,8 +80,7 @@ In order to support SSL over HTTP/2 connections, Solr uses ALPN.
 Java 8 does not include an implementation of ALPN, therefore Solr will start with HTTP/1 only when SSL is enabled and Java 8 is in use.
 
 ==== Client Changes for HTTP/2
-{solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/Http2SolrClient.html[`Http2SolrClient`]
-with HTTP/2 and async capabilities based on Jetty Client is introduced. This client replaced
+`Http2SolrClient` with HTTP/2 and async capabilities based on Jetty Client is introduced. This client replaced
 `HttpSolrClient` and `ConcurrentUpdateSolrClient` for sending most internal requests (sent by
 `UpdateShardHandler` and `HttpShardHandler`).
 
diff --git a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
index ec94c7d5aa9..3f5013fe664 100644
--- a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
+++ b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/SolrClientCache.java
@@ -26,10 +26,11 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBuilderBase;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.common.AlreadyClosedException;
 import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.URLUtil;
@@ -46,19 +47,19 @@ public class SolrClientCache implements Closeable {
   private static final int minSocketTimeout =
       Math.max(Integer.getInteger(SolrHttpConstants.PROP_SO_TIMEOUT, MIN_TIMEOUT), MIN_TIMEOUT);
 
-  private String basicAuthCredentials = null; // Only support with the http2SolrClient
+  protected String basicAuthCredentials = null; // Only support with the httpJettySolrClient
 
   private final Map<String, SolrClient> solrClients = new HashMap<>();
-  private final Http2SolrClient http2SolrClient;
+  private final HttpSolrClientBase httpSolrClient;
   private final AtomicBoolean isClosed = new AtomicBoolean(false);
   private final AtomicReference<String> defaultZkHost = new AtomicReference<>();
 
   public SolrClientCache() {
-    this.http2SolrClient = null;
+    this.httpSolrClient = null;
   }
 
-  public SolrClientCache(Http2SolrClient http2SolrClient) {
-    this.http2SolrClient = http2SolrClient;
+  public SolrClientCache(HttpSolrClientBase httpSolrClient) {
+    this.httpSolrClient = httpSolrClient;
   }
 
   public void setBasicAuthCredentials(String basicAuthCredentials) {
@@ -90,24 +91,18 @@ public class SolrClientCache implements Closeable {
     boolean canUseACLs =
         Optional.ofNullable(defaultZkHost.get()).map(zkHostNoChroot::equals).orElse(false);
 
-    final var client =
-        newCloudHttp2SolrClient(zkHost, http2SolrClient, canUseACLs, basicAuthCredentials);
+    final var client = newCloudSolrClient(zkHost, httpSolrClient, canUseACLs);
     solrClients.put(zkHost, client);
     return client;
   }
 
-  private static CloudHttp2SolrClient newCloudHttp2SolrClient(
-      String zkHost,
-      Http2SolrClient http2SolrClient,
-      boolean canUseACLs,
-      String basicAuthCredentials) {
+  protected CloudSolrClient newCloudSolrClient(
+      String zkHost, HttpSolrClientBase httpSolrClient, boolean canUseACLs) {
     final List<String> hosts = List.of(zkHost);
-    var builder = new CloudHttp2SolrClient.Builder(hosts, Optional.empty());
+    var builder = new CloudSolrClient.Builder(hosts, Optional.empty());
     builder.canUseZkACLs(canUseACLs);
     // using internal builder to ensure the internal client gets closed
-    builder =
-        builder.withHttpClientBuilder(
-            newHttp2SolrClientBuilder(null, http2SolrClient, basicAuthCredentials));
+    builder = builder.withHttpClientBuilder(newHttpSolrClientBuilder(null, httpSolrClient));
     var client = builder.build();
     try {
       client.connect();
@@ -132,21 +127,20 @@ public class SolrClientCache implements Closeable {
     if (solrClients.containsKey(baseUrl)) {
       return solrClients.get(baseUrl);
     }
-    final var client =
-        newHttp2SolrClientBuilder(baseUrl, http2SolrClient, basicAuthCredentials).build();
+    final var client = newHttpSolrClientBuilder(baseUrl, httpSolrClient).build();
     solrClients.put(baseUrl, client);
     return client;
   }
 
-  private static Http2SolrClient.Builder newHttp2SolrClientBuilder(
-      String url, Http2SolrClient http2SolrClient, String basicAuthCredentials) {
+  protected HttpSolrClientBuilderBase<?, ?> newHttpSolrClientBuilder(
+      String url, HttpSolrClientBase httpSolrClient) {
     final var builder =
         (url == null || URLUtil.isBaseUrl(url)) // URL may be null here and set by caller
-            ? new Http2SolrClient.Builder(url)
-            : new Http2SolrClient.Builder(URLUtil.extractBaseUrl(url))
+            ? new HttpJettySolrClient.Builder(url)
+            : new HttpJettySolrClient.Builder(URLUtil.extractBaseUrl(url))
                 .withDefaultCollection(URLUtil.extractCoreFromCoreUrl(url));
-    if (http2SolrClient != null) {
-      builder.withHttpClient(http2SolrClient);
+    if (httpSolrClient != null) {
+      builder.withHttpClient((HttpJettySolrClient) httpSolrClient); // TODO support JDK
       // cannot set connection timeout
     } else {
       builder.withConnectionTimeout(minConnTimeout, TimeUnit.MILLISECONDS);
diff --git a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java
index 5228f98dae3..74f3fee7d4b 100644
--- a/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java
+++ b/solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/sql/DatabaseMetaDataImpl.java
@@ -28,7 +28,7 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.util.SimpleOrderedMap;
@@ -124,7 +124,7 @@ class DatabaseMetaDataImpl implements DatabaseMetaData {
     for (String node : liveNodes) {
       try {
         String nodeURL = Utils.getBaseUrlForNodeName(node, urlScheme);
-        solrClient = new Http2SolrClient.Builder(nodeURL).build();
+        solrClient = new HttpJettySolrClient.Builder(nodeURL).build();
 
         QueryResponse rsp = solrClient.query(sysQuery);
         return String.valueOf(
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
index bd9b237cecd..7972f8ab3e2 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java
@@ -180,10 +180,10 @@ public class NodeValueFetcher {
 
       String baseUrl =
           ctx.zkClientClusterStateProvider.getZkStateReader().getBaseUrlForNodeName(ctx.getNode());
-      SimpleSolrResponse rsp = ctx.http2SolrClient().requestWithBaseUrl(baseUrl, req::process);
+      NamedList<Object> response = ctx.httpSolrClient().requestWithBaseUrl(baseUrl, req, null);
 
       // TODO come up with a better solution to stream this response instead of loading in memory
-      try (InputStream prometheusStream = (InputStream) rsp.getResponse().get(STREAM_KEY)) {
+      try (InputStream prometheusStream = (InputStream) response.get(STREAM_KEY)) {
         List<String> prometheusLines = Metrics.prometheusMetricStream(prometheusStream).toList();
         for (Metrics t : requestedMetricNames) {
           Object value = t.extractFromPrometheus(prometheusLines);
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
index 30afd6bed57..b6aae92ecc1 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientCloudManager.java
@@ -17,7 +17,6 @@
 
 package org.apache.solr.client.solrj.impl;
 
-import java.lang.invoke.MethodHandles;
 import org.apache.solr.client.solrj.cloud.DistribStateManager;
 import org.apache.solr.client.solrj.cloud.NodeStateProvider;
 import org.apache.solr.client.solrj.cloud.SolrCloudManager;
@@ -26,14 +25,11 @@ import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.ObjectCache;
 import org.apache.solr.common.util.TimeSource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /** Class that implements {@link SolrCloudManager} using a SolrClient */
 public class SolrClientCloudManager implements SolrCloudManager {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private final CloudHttp2SolrClient cloudSolrClient;
+  private final CloudSolrClient cloudSolrClient;
   private final ZkDistribStateManager stateManager;
   private final ZkStateReader zkStateReader;
   private final SolrZkClient zkClient;
@@ -41,7 +37,7 @@ public class SolrClientCloudManager implements SolrCloudManager {
   private final boolean closeObjectCache;
   private volatile boolean isClosed;
 
-  public SolrClientCloudManager(CloudHttp2SolrClient client, ObjectCache objectCache) {
+  public SolrClientCloudManager(CloudSolrClient client, ObjectCache objectCache) {
     this.cloudSolrClient = client;
     this.zkStateReader = ZkStateReader.from(client);
     this.zkClient = zkStateReader.getZkClient();
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
index da494738281..bebb9456917 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
@@ -58,12 +58,12 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
   @SuppressWarnings({"rawtypes"})
   private Map<String, Map> nodeVsTags = new HashMap<>();
 
-  public SolrClientNodeStateProvider(CloudHttp2SolrClient solrClient) {
-    if (!(solrClient.getHttpClient() instanceof Http2SolrClient)) {
+  public SolrClientNodeStateProvider(CloudSolrClient solrClient) {
+    if (!(solrClient instanceof CloudHttp2SolrClient)) {
       throw new IllegalArgumentException(
-          "The passed-in Cloud Solr Client must delegate to " + Http2SolrClient.class);
+          "The passed-in CloudSolrClient must be a " + CloudHttp2SolrClient.class);
     }
-    this.solrClient = solrClient;
+    this.solrClient = (CloudHttp2SolrClient) solrClient;
     try {
       readReplicaDetails();
     } catch (IOException e) {
@@ -219,8 +219,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
         ctx.zkClientClusterStateProvider.getZkStateReader().getBaseUrlForNodeName(solrNode);
 
     try (InputStream in =
-        (InputStream)
-            ctx.http2SolrClient().requestWithBaseUrl(baseUrl, req, null).get(STREAM_KEY)) {
+        (InputStream) ctx.httpSolrClient().requestWithBaseUrl(baseUrl, req, null).get(STREAM_KEY)) {
 
       NodeValueFetcher.Metrics.prometheusMetricStream(in).forEach(lineProcessor);
     } catch (Exception e) {
@@ -257,18 +256,14 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
     }
 
     public RemoteCallCtx(String node, CloudHttp2SolrClient cloudSolrClient) {
-      if (!(cloudSolrClient.getHttpClient() instanceof Http2SolrClient)) {
-        throw new IllegalArgumentException(
-            "The passed-in Cloud Solr Client must delegate to " + Http2SolrClient.class);
-      }
       this.node = node;
       this.cloudSolrClient = cloudSolrClient;
       this.zkClientClusterStateProvider =
           (ZkClientClusterStateProvider) cloudSolrClient.getClusterStateProvider();
     }
 
-    protected Http2SolrClient http2SolrClient() {
-      return (Http2SolrClient) cloudSolrClient.getHttpClient();
+    protected HttpSolrClientBase httpSolrClient() {
+      return cloudSolrClient.getHttpClient();
     }
 
     /**
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
index f8773b6e213..46974dce6d4 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
@@ -22,7 +22,7 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.NavigableObject;
 import org.apache.solr.common.SolrException;
@@ -34,9 +34,9 @@ public class NodesSysPropsCacher implements NodesSysProps, AutoCloseable {
   private volatile boolean isClosed = false;
   private final Map<String, Map<String, Object>> nodeVsTagsCache = new ConcurrentHashMap<>();
   private ZkStateReader zkStateReader;
-  private final Http2SolrClient solrClient;
+  private final HttpJettySolrClient solrClient;
 
-  public NodesSysPropsCacher(Http2SolrClient solrClient, ZkStateReader zkStateReader) {
+  public NodesSysPropsCacher(HttpJettySolrClient solrClient, ZkStateReader zkStateReader) {
     this.zkStateReader = zkStateReader;
     this.solrClient = solrClient;
     zkStateReader.registerLiveNodesListener(
diff --git a/solr/solrj-zookeeper/src/test/org/apache/solr/client/solrj/impl/NodeValueFetcherTest.java b/solr/solrj-zookeeper/src/test/org/apache/solr/client/solrj/impl/NodeValueFetcherTest.java
index 29edd771b43..59d38f64f2c 100644
--- a/solr/solrj-zookeeper/src/test/org/apache/solr/client/solrj/impl/NodeValueFetcherTest.java
+++ b/solr/solrj-zookeeper/src/test/org/apache/solr/client/solrj/impl/NodeValueFetcherTest.java
@@ -65,8 +65,8 @@ public class NodeValueFetcherTest extends SolrCloudTestCase {
 
   @Test
   public void testGetTags() throws Exception {
-    try (var cloudHttp2SolrClient =
-        new CloudHttp2SolrClient.Builder(
+    try (var cloudSolrClient =
+        new CloudSolrClient.Builder(
                 Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
             .build()) {
       int totalCores = 0;
@@ -75,7 +75,7 @@ public class NodeValueFetcherTest extends SolrCloudTestCase {
       // We should get same number than when we created the collection
       for (JettySolrRunner runner : cluster.getJettySolrRunners()) {
         String node = runner.getNodeName();
-        RemoteCallCtx ctx = new RemoteCallCtx(node, cloudHttp2SolrClient);
+        RemoteCallCtx ctx = new RemoteCallCtx(node, cloudSolrClient);
         NodeValueFetcher fetcher = new NodeValueFetcher();
 
         Set<String> requestedTags = Set.of("cores");
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/embedded/package.html b/solr/solrj/src/java/org/apache/solr/client/solrj/embedded/package.html
deleted file mode 100644
index 4fa9c318686..00000000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/embedded/package.html
+++ /dev/null
@@ -1,22 +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.
--->
-<!-- not a package-info.java, because we already defined this package in core/ -->
-<html>
-<body>
-SolrJ client implementations for embedded solr access.
-</body>
-</html>
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index 6886952c25c..1eb61d4c9c5 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -17,63 +17,43 @@
 
 package org.apache.solr.client.solrj.impl;
 
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
 import java.util.concurrent.TimeUnit;
-import org.apache.solr.client.solrj.ResponseParser;
-import org.apache.solr.client.solrj.impl.SolrZkClientTimeout.SolrZkClientTimeoutAware;
-import org.apache.solr.client.solrj.request.RequestWriter;
-import org.apache.solr.client.solrj.request.UpdateRequest;
-import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
- * SolrJ client class to communicate with SolrCloud using an Http/2-capable Solr Client. Instances
- * of this class communicate with Zookeeper to discover Solr endpoints for SolrCloud collections,
- * and then use the {@link LBSolrClient} to issue requests.
+ * This {@link CloudSolrClient} is a base implementation using a {@link HttpSolrClientBase}. The '2'
+ * in the name has no differentiating significance anymore.
  *
  * @since solr 8.0
+ * @lucene.internal
  */
 @SuppressWarnings("serial")
 public class CloudHttp2SolrClient extends CloudSolrClient {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private final ClusterStateProvider stateProvider;
   private final LBSolrClient lbClient;
   private final HttpSolrClientBase myClient;
   private final boolean clientIsInternal;
 
-  private static final boolean JETTY_CLIENT_AVAILABLE;
-
-  static {
-    boolean jettyClientAvailable = true;
-    try {
-      Class.forName("org.eclipse.jetty.client.HttpClient");
-    } catch (ClassNotFoundException e) {
-      jettyClientAvailable = false;
-    }
-    JETTY_CLIENT_AVAILABLE = jettyClientAvailable;
-  }
-
   /**
    * Create a new client object that connects to Zookeeper and is always aware of the SolrCloud
    * state. If there is a fully redundant Zookeeper quorum and SolrCloud has enough replicas for
    * every shard in a collection, there is no single point of failure. Updates will be sent to shard
    * leaders by default.
    *
-   * @param builder a {@link CloudHttp2SolrClient.Builder} with the options used to create the
-   *     client.
+   * @param builder a {@link CloudSolrClient.Builder} with the options used to create the client.
    */
   protected CloudHttp2SolrClient(Builder builder) {
     super(builder.shardLeadersOnly, builder.parallelUpdates, builder.directUpdatesToLeadersOnly);
     this.clientIsInternal = builder.httpClient == null;
-    this.myClient = createOrGetHttpClientFromBuilder(builder);
-    this.stateProvider = createClusterStateProvider(builder);
+    try {
+      this.myClient = builder.createOrGetHttpClient();
+      this.lbClient = builder.createOrGetLbClient(myClient);
+      this.stateProvider = createClusterStateProvider(builder);
+    } catch (RuntimeException e) {
+      close();
+      throw e;
+    }
     this.retryExpiryTimeNano = builder.retryExpiryTimeNano;
     this.defaultCollection = builder.defaultCollection;
     if (builder.requestWriter != null) {
@@ -89,62 +69,15 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     //  If caches are expired then they are refreshed after acquiring a lock. Set the number of
     // locks.
     this.locks = objectList(builder.parallelCacheRefreshesLocks);
-
-    this.lbClient = builder.createOrGetLbClient(myClient);
-  }
-
-  private HttpSolrClientBase createOrGetHttpClientFromBuilder(Builder builder) {
-    if (builder.httpClient != null) {
-      return builder.httpClient;
-    } else if (builder.internalClientBuilder != null) {
-      return builder.internalClientBuilder.build();
-    } else if (JETTY_CLIENT_AVAILABLE) {
-      log.debug("Using {} as the delegate http client", Http2SolrClient.class);
-      return new Http2SolrClient.Builder().build();
-    } else {
-      log.debug("Using {} as the delegate http client", HttpJdkSolrClient.class);
-      return new HttpJdkSolrClient.Builder().build();
-    }
   }
 
   private ClusterStateProvider createClusterStateProvider(Builder builder) {
     if (builder.stateProvider != null) {
       return builder.stateProvider;
     } else if (builder.zkHosts.isEmpty()) {
-      return createHttp2ClusterStateProvider(builder.solrUrls, this.myClient);
+      return builder.createHttpClusterStateProvider(this.myClient);
     } else {
-      return createZkClusterStateProvider(builder);
-    }
-  }
-
-  private ClusterStateProvider createZkClusterStateProvider(Builder builder) {
-    try {
-      ClusterStateProvider stateProvider =
-          ClusterStateProvider.newZkClusterStateProvider(
-              builder.zkHosts, builder.zkChroot, builder.canUseZkACLs);
-      if (stateProvider instanceof SolrZkClientTimeoutAware timeoutAware) {
-        timeoutAware.setZkClientTimeout(builder.zkClientTimeout);
-        timeoutAware.setZkConnectTimeout(builder.zkConnectTimeout);
-      }
-      return stateProvider;
-    } catch (Exception e) {
-      close();
-      throw e;
-    }
-  }
-
-  private ClusterStateProvider createHttp2ClusterStateProvider(
-      List<String> solrUrls, HttpSolrClientBase httpClient) {
-    try {
-      return new Http2ClusterStateProvider<>(solrUrls, httpClient);
-    } catch (Exception e) {
-      close(); // doesn't throw
-      throw new RuntimeException(
-          "Couldn't initialize a HttpClusterStateProvider (is/are the "
-              + "Solr server(s), "
-              + solrUrls
-              + ", down?)",
-          e);
+      return builder.createZkClusterStateProvider();
     }
   }
 
@@ -172,289 +105,4 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
   public HttpSolrClientBase getHttpClient() {
     return myClient;
   }
-
-  /** Constructs {@link CloudHttp2SolrClient} instances from provided configuration. */
-  public static class Builder {
-    protected Collection<String> zkHosts = new ArrayList<>();
-    protected List<String> solrUrls = new ArrayList<>();
-    protected String zkChroot;
-    protected HttpSolrClientBase httpClient;
-    protected boolean shardLeadersOnly = true;
-    protected boolean directUpdatesToLeadersOnly = false;
-    protected boolean parallelUpdates = true;
-    protected ClusterStateProvider stateProvider;
-    protected HttpSolrClientBuilderBase<?, ?> internalClientBuilder;
-    private RequestWriter requestWriter;
-    private ResponseParser responseParser;
-    private long retryExpiryTimeNano =
-        TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS); // 3 seconds or 3 million nanos
-
-    private String defaultCollection;
-    private long timeToLiveSeconds = 60;
-    private int parallelCacheRefreshesLocks = 3;
-    private int zkConnectTimeout = SolrZkClientTimeout.DEFAULT_ZK_CONNECT_TIMEOUT;
-    private int zkClientTimeout = SolrZkClientTimeout.DEFAULT_ZK_CLIENT_TIMEOUT;
-    private boolean canUseZkACLs = true;
-
-    /**
-     * Provide a series of Solr URLs to be used when configuring {@link CloudHttp2SolrClient}
-     * instances. The solr client will use these urls to understand the cluster topology, which solr
-     * nodes are active etc.
-     *
-     * <p>Provided Solr URLs are expected to point to the root Solr path
-     * ("http://hostname:8983/solr"); they should not include any collections, cores, or other path
-     * components.
-     *
-     * <p>Usage example:
-     *
-     * <pre>
-     *   final List&lt;String&gt; solrBaseUrls = new ArrayList&lt;String&gt;();
-     *   solrBaseUrls.add("http://solr1:8983/solr"); solrBaseUrls.add("http://solr2:8983/solr"); solrBaseUrls.add("http://solr3:8983/solr");
-     *   final SolrClient client = new CloudHttp2SolrClient.Builder(solrBaseUrls).build();
-     * </pre>
-     */
-    public Builder(List<String> solrUrls) {
-      this.solrUrls = solrUrls;
-    }
-
-    /**
-     * Provide a series of ZK hosts which will be used when configuring {@link CloudHttp2SolrClient}
-     * instances.
-     *
-     * <p>Usage example when Solr stores data at the ZooKeeper root ('/'):
-     *
-     * <pre>
-     *   final List&lt;String&gt; zkServers = new ArrayList&lt;String&gt;();
-     *   zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
-     *   final SolrClient client = new CloudHttp2SolrClient.Builder(zkServers, Optional.empty()).build();
-     * </pre>
-     *
-     * Usage example when Solr data is stored in a ZooKeeper chroot:
-     *
-     * <pre>
-     *    final List&lt;String&gt; zkServers = new ArrayList&lt;String&gt;();
-     *    zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
-     *    final SolrClient client = new CloudHttp2SolrClient.Builder(zkServers, Optional.of("/solr")).build();
-     *  </pre>
-     *
-     * @param zkHosts a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181")
-     * @param zkChroot the path to the root ZooKeeper node containing Solr data. Provide {@code
-     *     java.util.Optional.empty()} if no ZK chroot is used.
-     */
-    public Builder(List<String> zkHosts, Optional<String> zkChroot) {
-      this.zkHosts = zkHosts;
-      if (zkChroot.isPresent()) this.zkChroot = zkChroot.get();
-    }
-
-    /** for an expert use-case */
-    public Builder(ClusterStateProvider stateProvider) {
-      this.stateProvider = stateProvider;
-    }
-
-    /** Whether to use the default ZK ACLs when building a ZK Client. */
-    public Builder canUseZkACLs(boolean canUseZkACLs) {
-      this.canUseZkACLs = canUseZkACLs;
-      return this;
-    }
-
-    /**
-     * Tells {@link Builder} that created clients should be configured such that {@link
-     * CloudSolrClient#isUpdatesToLeaders} returns <code>true</code>.
-     *
-     * @see #sendUpdatesToAnyReplica
-     * @see CloudSolrClient#isUpdatesToLeaders
-     */
-    public Builder sendUpdatesOnlyToShardLeaders() {
-      shardLeadersOnly = true;
-      return this;
-    }
-
-    /**
-     * Tells {@link Builder} that created clients should be configured such that {@link
-     * CloudSolrClient#isUpdatesToLeaders} returns <code>false</code>.
-     *
-     * @see #sendUpdatesOnlyToShardLeaders
-     * @see CloudSolrClient#isUpdatesToLeaders
-     */
-    public Builder sendUpdatesToAnyReplica() {
-      shardLeadersOnly = false;
-      return this;
-    }
-
-    /**
-     * Tells {@link CloudHttp2SolrClient.Builder} that created clients should send direct updates to
-     * shard leaders only.
-     *
-     * <p>UpdateRequests whose leaders cannot be found will "fail fast" on the client side with a
-     * {@link SolrException}
-     *
-     * @see #sendDirectUpdatesToAnyShardReplica
-     * @see CloudSolrClient#isDirectUpdatesToLeadersOnly
-     */
-    public Builder sendDirectUpdatesToShardLeadersOnly() {
-      directUpdatesToLeadersOnly = true;
-      return this;
-    }
-
-    /**
-     * Tells {@link CloudHttp2SolrClient.Builder} that created clients can send updates to any shard
-     * replica (shard leaders and non-leaders).
-     *
-     * <p>Shard leaders are still preferred, but the created clients will fall back to using other
-     * replicas if a leader cannot be found.
-     *
-     * @see #sendDirectUpdatesToShardLeadersOnly
-     * @see CloudSolrClient#isDirectUpdatesToLeadersOnly
-     */
-    public Builder sendDirectUpdatesToAnyShardReplica() {
-      directUpdatesToLeadersOnly = false;
-      return this;
-    }
-
-    /** Provides a {@link RequestWriter} for created clients to use when handing requests. */
-    public Builder withRequestWriter(RequestWriter requestWriter) {
-      this.requestWriter = requestWriter;
-      return this;
-    }
-
-    /** Provides a {@link ResponseParser} for created clients to use when handling requests. */
-    public Builder withResponseParser(ResponseParser responseParser) {
-      this.responseParser = responseParser;
-      return this;
-    }
-
-    /**
-     * Tells {@link CloudHttp2SolrClient.Builder} whether created clients should send shard updates
-     * serially or in parallel
-     *
-     * <p>When an {@link UpdateRequest} affects multiple shards, {@link CloudHttp2SolrClient} splits
-     * it up and sends a request to each affected shard. This setting chooses whether those
-     * sub-requests are sent serially or in parallel.
-     *
-     * <p>If not set, this defaults to 'true' and sends sub-requests in parallel.
-     */
-    public Builder withParallelUpdates(boolean parallelUpdates) {
-      this.parallelUpdates = parallelUpdates;
-      return this;
-    }
-
-    /**
-     * When caches are expired then they are refreshed after acquiring a lock. Use this to set the
-     * number of locks.
-     *
-     * <p>Defaults to 3.
-     */
-    public Builder withParallelCacheRefreshes(int parallelCacheRefreshesLocks) {
-      this.parallelCacheRefreshesLocks = parallelCacheRefreshesLocks;
-      return this;
-    }
-
-    /**
-     * This is the time to wait to re-fetch the state after getting the same state version from ZK
-     */
-    public Builder withRetryExpiryTime(long expiryTime, TimeUnit unit) {
-      this.retryExpiryTimeNano = TimeUnit.NANOSECONDS.convert(expiryTime, unit);
-      return this;
-    }
-
-    /** Sets the default collection for request. */
-    public Builder withDefaultCollection(String defaultCollection) {
-      this.defaultCollection = defaultCollection;
-      return this;
-    }
-
-    /**
-     * Sets the cache ttl for DocCollection Objects cached.
-     *
-     * @param timeToLive ttl value
-     */
-    public Builder withCollectionCacheTtl(long timeToLive, TimeUnit unit) {
-      assert timeToLive > 0;
-      this.timeToLiveSeconds = TimeUnit.SECONDS.convert(timeToLive, unit);
-      return this;
-    }
-
-    /**
-     * Set the internal Solr HTTP client.
-     *
-     * <p>Note: closing the client instance is the responsibility of the caller.
-     *
-     * @return this
-     */
-    public Builder withHttpClient(HttpSolrClientBase httpSolrClient) {
-      if (this.internalClientBuilder != null) {
-        throw new IllegalStateException(
-            "The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
-      }
-      this.httpClient = httpSolrClient;
-      return this;
-    }
-
-    /**
-     * If provided, the CloudHttp2SolrClient will build it's internal client using this builder
-     * (instead of the empty default one). Providing this builder allows users to configure the
-     * internal clients (authentication, timeouts, etc.).
-     *
-     * @param internalClientBuilder the builder to use for creating the internal http client.
-     * @return this
-     */
-    public Builder withHttpClientBuilder(HttpSolrClientBuilderBase<?, ?> internalClientBuilder) {
-      if (this.httpClient != null) {
-        throw new IllegalStateException(
-            "The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
-      }
-      this.internalClientBuilder = internalClientBuilder;
-      return this;
-    }
-
-    @Deprecated(since = "9.10")
-    public Builder withInternalClientBuilder(
-        HttpSolrClientBuilderBase<?, ?> internalClientBuilder) {
-      return withHttpClientBuilder(internalClientBuilder);
-    }
-
-    /**
-     * Sets the Zk connection timeout
-     *
-     * @param zkConnectTimeout timeout value
-     * @param unit time unit
-     */
-    public Builder withZkConnectTimeout(int zkConnectTimeout, TimeUnit unit) {
-      this.zkConnectTimeout = Math.toIntExact(unit.toMillis(zkConnectTimeout));
-      return this;
-    }
-
-    /**
-     * Sets the Zk client session timeout
-     *
-     * @param zkClientTimeout timeout value
-     * @param unit time unit
-     */
-    public Builder withZkClientTimeout(int zkClientTimeout, TimeUnit unit) {
-      this.zkClientTimeout = Math.toIntExact(unit.toMillis(zkClientTimeout));
-      return this;
-    }
-
-    /** Create a {@link CloudHttp2SolrClient} based on the provided configuration. */
-    public CloudHttp2SolrClient build() {
-      int providedOptions = 0;
-      if (!zkHosts.isEmpty()) providedOptions++;
-      if (!solrUrls.isEmpty()) providedOptions++;
-      if (stateProvider != null) providedOptions++;
-
-      if (providedOptions > 1) {
-        throw new IllegalArgumentException(
-            "Only one of zkHost(s), solrUrl(s), or stateProvider should be specified.");
-      } else if (providedOptions == 0) {
-        throw new IllegalArgumentException(
-            "One of zkHosts, solrUrls, or stateProvider must be specified.");
-      }
-
-      return new CloudHttp2SolrClient(this);
-    }
-
-    protected LBSolrClient createOrGetLbClient(HttpSolrClientBase myClient) {
-      return myClient.createLBSolrClient();
-    }
-  }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index cecbc4fcd23..bd504d2535d 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -21,6 +21,7 @@ import static org.apache.solr.common.params.CommonParams.ID;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Constructor;
 import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -33,6 +34,7 @@ import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -82,6 +84,12 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
+/**
+ * A {@link SolrClient} that routes requests to ideal nodes, including splitting update batches to
+ * the correct shards. It uses {@link LBSolrClient} as well, thus offering fail-over abilities if a
+ * core or node becomes unavailable. It's able to know where to route requests due to its knowledge
+ * of the SolrCloud "cluster state".
+ */
 public abstract class CloudSolrClient extends SolrClient {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -119,6 +127,368 @@ public abstract class CloudSolrClient extends SolrClient {
 
   protected volatile Object[] locks = objectList(3);
 
+  /**
+   * Constructs {@link CloudSolrClient} instances from provided configuration. It will use a Jetty
+   * based {@code HttpClient} if available, or will otherwise use the JDK.
+   */
+  public static class Builder {
+    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    // If the Jetty-based HttpJettySolrClient builder is on the classpath, this will be its no-arg
+    // constructor; otherwise it will be null and we will fall back to the JDK HTTP client.
+    private static final Constructor<? extends HttpSolrClientBuilderBase<?, ?>>
+        HTTP_JETTY_SOLR_CLIENT_BUILDER_CTOR;
+
+    static {
+      Constructor<? extends HttpSolrClientBuilderBase<?, ?>> ctor = null;
+      try {
+        @SuppressWarnings("unchecked")
+        Class<? extends HttpSolrClientBuilderBase<?, ?>> builderClass =
+            (Class<? extends HttpSolrClientBuilderBase<?, ?>>)
+                Class.forName("org.apache.solr.client.solrj.jetty.HttpJettySolrClient$Builder");
+        ctor = builderClass.getDeclaredConstructor();
+        ctor.newInstance(); // perhaps fails because Jetty libs aren't on the classpath
+      } catch (Throwable t) {
+        // Class not present or incompatible; leave ctor as null to indicate unavailability
+        if (log.isTraceEnabled()) {
+          log.trace(
+              "HttpJettySolrClient$Builder not available on classpath; will use HttpJdkSolrClient",
+              t);
+        }
+      }
+      HTTP_JETTY_SOLR_CLIENT_BUILDER_CTOR = ctor;
+    }
+
+    protected Collection<String> zkHosts = new ArrayList<>();
+    protected List<String> solrUrls = new ArrayList<>();
+    protected String zkChroot;
+    protected HttpSolrClientBase httpClient;
+    protected boolean shardLeadersOnly = true;
+    protected boolean directUpdatesToLeadersOnly = false;
+    protected boolean parallelUpdates = true;
+    protected ClusterStateProvider stateProvider;
+    protected HttpSolrClientBuilderBase<?, ?> internalClientBuilder;
+    protected RequestWriter requestWriter;
+    protected ResponseParser responseParser;
+    protected long retryExpiryTimeNano =
+        TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS); // 3 seconds or 3 million nanos
+
+    protected String defaultCollection;
+    protected long timeToLiveSeconds = 60;
+    protected int parallelCacheRefreshesLocks = 3;
+    protected int zkConnectTimeout = SolrZkClientTimeout.DEFAULT_ZK_CONNECT_TIMEOUT;
+    protected int zkClientTimeout = SolrZkClientTimeout.DEFAULT_ZK_CLIENT_TIMEOUT;
+    protected boolean canUseZkACLs = true;
+
+    /**
+     * Provide a series of Solr URLs to be used when configuring {@link CloudSolrClient} instances.
+     * The solr client will use these urls to understand the cluster topology, which solr nodes are
+     * active etc.
+     *
+     * <p>Provided Solr URLs are expected to point to the root Solr path
+     * ("http://hostname:8983/solr"); they should not include any collections, cores, or other path
+     * components.
+     *
+     * <p>Usage example:
+     *
+     * <pre>
+     *   final List&lt;String&gt; solrBaseUrls = new ArrayList&lt;String&gt;();
+     *   solrBaseUrls.add("http://solr1:8983/solr"); solrBaseUrls.add("http://solr2:8983/solr"); solrBaseUrls.add("http://solr3:8983/solr");
+     *   final SolrClient client = new CloudSolrClient.Builder(solrBaseUrls).build();
+     * </pre>
+     */
+    public Builder(List<String> solrUrls) {
+      this.solrUrls = solrUrls;
+    }
+
+    /**
+     * Provide a series of ZK hosts which will be used when configuring {@link CloudSolrClient}
+     * instances.
+     *
+     * <p>Usage example when Solr stores data at the ZooKeeper root ('/'):
+     *
+     * <pre>
+     *   final List&lt;String&gt; zkServers = new ArrayList&lt;String&gt;();
+     *   zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
+     *   final SolrClient client = new CloudSolrClient.Builder(zkServers, Optional.empty()).build();
+     * </pre>
+     *
+     * Usage example when Solr data is stored in a ZooKeeper chroot:
+     *
+     * <pre>
+     *    final List&lt;String&gt; zkServers = new ArrayList&lt;String&gt;();
+     *    zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
+     *    final SolrClient client = new CloudSolrClient.Builder(zkServers, Optional.of("/solr")).build();
+     *  </pre>
+     *
+     * @param zkHosts a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181")
+     * @param zkChroot the path to the root ZooKeeper node containing Solr data. Provide {@code
+     *     java.util.Optional.empty()} if no ZK chroot is used.
+     */
+    public Builder(List<String> zkHosts, Optional<String> zkChroot) {
+      this.zkHosts = zkHosts;
+      if (zkChroot.isPresent()) this.zkChroot = zkChroot.get();
+    }
+
+    /** for an expert use-case */
+    public Builder(ClusterStateProvider stateProvider) {
+      this.stateProvider = stateProvider;
+    }
+
+    /** Whether to use the default ZK ACLs when building a ZK Client. */
+    public Builder canUseZkACLs(boolean canUseZkACLs) {
+      this.canUseZkACLs = canUseZkACLs;
+      return this;
+    }
+
+    /**
+     * Tells {@link Builder} that created clients should be configured such that {@link
+     * CloudSolrClient#isUpdatesToLeaders} returns <code>true</code>.
+     *
+     * @see #sendUpdatesToAnyReplica
+     * @see CloudSolrClient#isUpdatesToLeaders
+     */
+    public Builder sendUpdatesOnlyToShardLeaders() {
+      shardLeadersOnly = true;
+      return this;
+    }
+
+    /**
+     * Tells {@link Builder} that created clients should be configured such that {@link
+     * CloudSolrClient#isUpdatesToLeaders} returns <code>false</code>.
+     *
+     * @see #sendUpdatesOnlyToShardLeaders
+     * @see CloudSolrClient#isUpdatesToLeaders
+     */
+    public Builder sendUpdatesToAnyReplica() {
+      shardLeadersOnly = false;
+      return this;
+    }
+
+    /**
+     * Tells {@link CloudSolrClient.Builder} that created clients should send direct updates to
+     * shard leaders only.
+     *
+     * <p>UpdateRequests whose leaders cannot be found will "fail fast" on the client side with a
+     * {@link SolrException}
+     *
+     * @see #sendDirectUpdatesToAnyShardReplica
+     * @see CloudSolrClient#isDirectUpdatesToLeadersOnly
+     */
+    public Builder sendDirectUpdatesToShardLeadersOnly() {
+      directUpdatesToLeadersOnly = true;
+      return this;
+    }
+
+    /**
+     * Tells {@link CloudSolrClient.Builder} that created clients can send updates to any shard
+     * replica (shard leaders and non-leaders).
+     *
+     * <p>Shard leaders are still preferred, but the created clients will fall back to using other
+     * replicas if a leader cannot be found.
+     *
+     * @see #sendDirectUpdatesToShardLeadersOnly
+     * @see CloudSolrClient#isDirectUpdatesToLeadersOnly
+     */
+    public Builder sendDirectUpdatesToAnyShardReplica() {
+      directUpdatesToLeadersOnly = false;
+      return this;
+    }
+
+    /** Provides a {@link RequestWriter} for created clients to use when handing requests. */
+    public Builder withRequestWriter(RequestWriter requestWriter) {
+      this.requestWriter = requestWriter;
+      return this;
+    }
+
+    /** Provides a {@link ResponseParser} for created clients to use when handling requests. */
+    public Builder withResponseParser(ResponseParser responseParser) {
+      this.responseParser = responseParser;
+      return this;
+    }
+
+    /**
+     * Tells {@link CloudSolrClient.Builder} whether created clients should send shard updates
+     * serially or in parallel
+     *
+     * <p>When an {@link UpdateRequest} affects multiple shards, {@link CloudSolrClient} splits it
+     * up and sends a request to each affected shard. This setting chooses whether those
+     * sub-requests are sent serially or in parallel.
+     *
+     * <p>If not set, this defaults to 'true' and sends sub-requests in parallel.
+     */
+    public Builder withParallelUpdates(boolean parallelUpdates) {
+      this.parallelUpdates = parallelUpdates;
+      return this;
+    }
+
+    /**
+     * When caches are expired then they are refreshed after acquiring a lock. Use this to set the
+     * number of locks.
+     *
+     * <p>Defaults to 3.
+     */
+    public Builder withParallelCacheRefreshes(int parallelCacheRefreshesLocks) {
+      this.parallelCacheRefreshesLocks = parallelCacheRefreshesLocks;
+      return this;
+    }
+
+    /**
+     * This is the time to wait to re-fetch the state after getting the same state version from ZK
+     */
+    public Builder withRetryExpiryTime(long expiryTime, TimeUnit unit) {
+      this.retryExpiryTimeNano = TimeUnit.NANOSECONDS.convert(expiryTime, unit);
+      return this;
+    }
+
+    /** Sets the default collection for request. */
+    public Builder withDefaultCollection(String defaultCollection) {
+      this.defaultCollection = defaultCollection;
+      return this;
+    }
+
+    /**
+     * Sets the cache ttl for DocCollection Objects cached.
+     *
+     * @param timeToLive ttl value
+     */
+    public Builder withCollectionCacheTtl(long timeToLive, TimeUnit unit) {
+      assert timeToLive > 0;
+      this.timeToLiveSeconds = TimeUnit.SECONDS.convert(timeToLive, unit);
+      return this;
+    }
+
+    /**
+     * Set the internal Solr HTTP client.
+     *
+     * <p>Note: closing the client instance is the responsibility of the caller.
+     *
+     * @return this
+     */
+    public Builder withHttpClient(HttpSolrClientBase httpSolrClient) {
+      if (this.internalClientBuilder != null) {
+        throw new IllegalStateException(
+            "The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
+      }
+      this.httpClient = httpSolrClient;
+      return this;
+    }
+
+    /**
+     * If provided, the CloudSolrClient will build it's internal client using this builder (instead
+     * of the empty default one). Providing this builder allows users to configure the internal
+     * clients (authentication, timeouts, etc.).
+     *
+     * @param internalClientBuilder the builder to use for creating the internal http client.
+     * @return this
+     */
+    public Builder withHttpClientBuilder(HttpSolrClientBuilderBase<?, ?> internalClientBuilder) {
+      if (this.httpClient != null) {
+        throw new IllegalStateException(
+            "The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
+      }
+      this.internalClientBuilder = internalClientBuilder;
+      return this;
+    }
+
+    @Deprecated(since = "9.10")
+    public Builder withInternalClientBuilder(
+        HttpSolrClientBuilderBase<?, ?> internalClientBuilder) {
+      return withHttpClientBuilder(internalClientBuilder);
+    }
+
+    /**
+     * Sets the Zk connection timeout
+     *
+     * @param zkConnectTimeout timeout value
+     * @param unit time unit
+     */
+    public Builder withZkConnectTimeout(int zkConnectTimeout, TimeUnit unit) {
+      this.zkConnectTimeout = Math.toIntExact(unit.toMillis(zkConnectTimeout));
+      return this;
+    }
+
+    /**
+     * Sets the Zk client session timeout
+     *
+     * @param zkClientTimeout timeout value
+     * @param unit time unit
+     */
+    public Builder withZkClientTimeout(int zkClientTimeout, TimeUnit unit) {
+      this.zkClientTimeout = Math.toIntExact(unit.toMillis(zkClientTimeout));
+      return this;
+    }
+
+    /** Create a {@link CloudSolrClient} based on the provided configuration. */
+    public CloudHttp2SolrClient build() {
+      int providedOptions = 0;
+      if (!zkHosts.isEmpty()) providedOptions++;
+      if (!solrUrls.isEmpty()) providedOptions++;
+      if (stateProvider != null) providedOptions++;
+
+      if (providedOptions > 1) {
+        throw new IllegalArgumentException(
+            "Only one of zkHost(s), solrUrl(s), or stateProvider should be specified.");
+      } else if (providedOptions == 0) {
+        throw new IllegalArgumentException(
+            "One of zkHosts, solrUrls, or stateProvider must be specified.");
+      }
+
+      return new CloudHttp2SolrClient(this);
+    }
+
+    protected HttpSolrClientBase createOrGetHttpClient() {
+      if (httpClient != null) {
+        return httpClient;
+      } else if (internalClientBuilder != null) {
+        return internalClientBuilder.build();
+      }
+
+      HttpSolrClientBuilderBase<?, ?> builder;
+      if (HTTP_JETTY_SOLR_CLIENT_BUILDER_CTOR != null) {
+        try {
+          log.debug("Using HttpJettySolrClient as the delegate http client");
+          builder = HTTP_JETTY_SOLR_CLIENT_BUILDER_CTOR.newInstance();
+        } catch (RuntimeException e) {
+          throw e;
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      } else {
+        log.debug("Using HttpJdkSolrClient as the delegate http client");
+        builder = new HttpJdkSolrClient.Builder();
+      }
+      return builder.build();
+    }
+
+    protected LBSolrClient createOrGetLbClient(HttpSolrClientBase myClient) {
+      return myClient.createLBSolrClient();
+    }
+
+    protected ClusterStateProvider createZkClusterStateProvider() {
+      ClusterStateProvider stateProvider =
+          ClusterStateProvider.newZkClusterStateProvider(zkHosts, zkChroot, canUseZkACLs);
+      if (stateProvider instanceof SolrZkClientTimeout.SolrZkClientTimeoutAware timeoutAware) {
+        timeoutAware.setZkClientTimeout(zkClientTimeout);
+        timeoutAware.setZkConnectTimeout(zkConnectTimeout);
+      }
+      return stateProvider;
+    }
+
+    protected ClusterStateProvider createHttpClusterStateProvider(HttpSolrClientBase httpClient) {
+      try {
+        return new HttpClusterStateProvider<>(solrUrls, httpClient);
+      } catch (Exception e) {
+        throw new RuntimeException(
+            "Couldn't initialize a HttpClusterStateProvider (is/are the "
+                + "Solr server(s), "
+                + solrUrls
+                + ", down?)",
+            e);
+      }
+    }
+  }
+
   protected static class StateCache extends ConcurrentHashMap<String, ExpiringCachedDocCollection> {
     final AtomicLong puts = new AtomicLong();
     final AtomicLong hits = new AtomicLong();
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
index 745bc1ffd2d..38a82b1791e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
@@ -634,7 +634,7 @@ public abstract class ConcurrentUpdateBaseSolrClient extends SolrClient {
 
   /** Constructs {@link ConcurrentUpdateBaseSolrClient} instances from provided configuration. */
   public abstract static class Builder {
-    public long idleTimeoutMillis;
+    protected long idleTimeoutMillis;
     protected HttpSolrClientBase client;
     protected String baseSolrUrl;
     protected String defaultCollection;
@@ -643,7 +643,7 @@ public abstract class ConcurrentUpdateBaseSolrClient extends SolrClient {
     protected ExecutorService executorService;
     protected boolean streamDeletes;
     protected boolean closeHttpClient;
-    private long pollQueueTimeMillis;
+    protected long pollQueueTimeMillis;
 
     /**
      * Initialize a Builder object, based on the provided URL and client.
@@ -784,5 +784,9 @@ public abstract class ConcurrentUpdateBaseSolrClient extends SolrClient {
      * Create a {@link ConcurrentUpdateBaseSolrClient} based on the provided configuration options.
      */
     public abstract ConcurrentUpdateBaseSolrClient build();
+
+    public HttpSolrClientBase getClient() {
+      return client;
+    }
   }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientBuilderFactory.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientBuilderFactory.java
index 2295360c9cc..48558e9b076 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientBuilderFactory.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClientBuilderFactory.java
@@ -17,13 +17,14 @@
 package org.apache.solr.client.solrj.impl;
 
 import java.io.Closeable;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 
 /**
- * A config hook for post-configuration of a {@linkplain Http2SolrClient} by its builder.
+ * A config hook for post-configuration of a {@linkplain HttpJettySolrClient} by its builder.
  *
  * @see SolrHttpConstants#SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY
  * @lucene.experimental
  */
 public interface HttpClientBuilderFactory extends Closeable {
-  default void setup(Http2SolrClient client) {}
+  default void setup(HttpJettySolrClient client) {}
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
similarity index 85%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
index d6407514522..17b41e951a0 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2ClusterStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpClusterStateProvider.java
@@ -20,19 +20,22 @@ package org.apache.solr.client.solrj.impl;
 import java.io.IOException;
 import java.util.List;
 
-public class Http2ClusterStateProvider<C extends HttpSolrClientBase>
+/** A CSP that uses Solr HTTP APIs. */
+public class HttpClusterStateProvider<C extends HttpSolrClientBase>
     extends BaseHttpClusterStateProvider {
+  // formerly known as Http2ClusterStateProvider
+
   final C httpClient;
 
   /**
    * Provide the solr urls and a solr http client for this cluster state provider to use. It is the
-   * caller's responsibiity to close the client.
+   * caller's responsibility to close the client.
    *
    * @param solrUrls root path solr urls
    * @param httpClient an instance of HttpSolrClientBase
    * @throws Exception if a problem with initialization occurs
    */
-  public Http2ClusterStateProvider(List<String> solrUrls, C httpClient) throws Exception {
+  public HttpClusterStateProvider(List<String> solrUrls, C httpClient) throws Exception {
     if (httpClient == null) {
       throw new IllegalArgumentException("You must provide an Http client.");
     }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
index 4e0472cc2d8..d405dc989f2 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
@@ -90,7 +90,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     HttpClient.Builder b = HttpClient.newBuilder();
 
     HttpClient.Redirect followRedirects =
-        Boolean.TRUE.equals(builder.followRedirects)
+        Boolean.TRUE.equals(builder.getFollowRedirects())
             ? HttpClient.Redirect.NORMAL
             : HttpClient.Redirect.NEVER;
     b.followRedirects(followRedirects);
@@ -103,8 +103,8 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
       b.sslContext(builder.sslContext);
     }
 
-    if (builder.executor != null) {
-      this.executor = builder.executor;
+    if (builder.getExecutor() != null) {
+      this.executor = builder.getExecutor();
       this.shutdownExecutor = false;
     } else {
       BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(1024);
@@ -129,12 +129,13 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
       b.cookieHandler(builder.cookieHandler);
     }
 
-    if (builder.proxyHost != null) {
-      if (builder.proxyIsSocks4) {
+    if (builder.getProxyHost() != null) {
+      if (builder.isProxyIsSocks4()) {
         log.warn(
             "Socks4 is likely not supported by this client.  See https://bugs.openjdk.org/browse/JDK-8214516");
       }
-      b.proxy(ProxySelector.of(new InetSocketAddress(builder.proxyHost, builder.proxyPort)));
+      b.proxy(
+          ProxySelector.of(new InetSocketAddress(builder.getProxyHost(), builder.getProxyPort())));
     }
     this.httpClient = b.build();
     updateDefaultMimeTypeForParser();
@@ -583,7 +584,7 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     @Override
     public Builder withHttpClient(HttpJdkSolrClient httpSolrClient) {
       super.withHttpClient(httpSolrClient);
-      if (this.executor == null) {
+      if (this.getExecutor() == null) {
         this.executor = httpSolrClient.executor;
       }
       if (this.sslContext == null) {
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
index ffed7779fb4..27df0a6ebd9 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
@@ -48,6 +48,11 @@ import org.apache.solr.common.util.ContentStream;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.Utils;
 
+/**
+ * Utility/base functionality for direct HTTP client implementations.
+ *
+ * @lucene.internal
+ */
 public abstract class HttpSolrClientBase extends SolrClient {
 
   protected static final String DEFAULT_PATH = ClientUtils.DEFAULT_PATH;
@@ -116,7 +121,7 @@ public abstract class HttpSolrClientBase extends SolrClient {
     return solrRequest.getResponseParser() == null ? this.parser : solrRequest.getResponseParser();
   }
 
-  protected RequestWriter getRequestWriter() {
+  public RequestWriter getRequestWriter() {
     return requestWriter;
   }
 
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
index 2821ebc4cae..e7ac05a2faa 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBuilderBase.java
@@ -120,7 +120,7 @@ public abstract class HttpSolrClientBuilderBase<
       }
     }
     this.basicAuthAuthorizationStr =
-        Http2SolrClient.basicAuthCredentialsToAuthorizationString(user, pass);
+        HttpSolrClientBase.basicAuthCredentialsToAuthorizationString(user, pass);
     return (B) this;
   }
 
@@ -250,4 +250,32 @@ public abstract class HttpSolrClientBuilderBase<
     }
     return (B) this;
   }
+
+  public Integer getMaxConnectionsPerHost() {
+    return maxConnectionsPerHost;
+  }
+
+  public Boolean getFollowRedirects() {
+    return followRedirects;
+  }
+
+  public String getProxyHost() {
+    return proxyHost;
+  }
+
+  public int getProxyPort() {
+    return proxyPort;
+  }
+
+  public boolean isProxyIsSocks4() {
+    return proxyIsSocks4;
+  }
+
+  public boolean isProxyIsSecure() {
+    return proxyIsSecure;
+  }
+
+  public ExecutorService getExecutor() {
+    return executor;
+  }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
index 1b9fbc5a3c6..920b3c49821 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
@@ -81,7 +81,7 @@ import org.slf4j.MDC;
  * <blockquote>
  *
  * <pre>
- * SolrClient client = new LBSolrClient.Builder(http2SolrClient,
+ * SolrClient client = new LBSolrClient.Builder(httpSolrClient,
  *         new LBSolrClient.Endpoint("http://host1:8080/solr"), new LBSolrClient.Endpoint("http://host2:8080/solr"))
  *     .build();
  * </pre>
@@ -94,7 +94,7 @@ import org.slf4j.MDC;
  * <blockquote>
  *
  * <pre>
- * SolrClient client = new LBSolrClient.Builder(http2SolrClient,
+ * SolrClient client = new LBSolrClient.Builder(httpSolrClient,
  *         new LBSolrClient.Endpoint("http://host1:8080/solr", "coreA"),
  *         new LBSolrClient.Endpoint("http://host2:8080/solr", "coreB"))
  *     .build();
@@ -614,7 +614,8 @@ public abstract class LBSolrClient extends SolrClient {
   private NamedList<Object> doRequest(
       SolrClient solrClient, String baseUrl, String collection, SolrRequest<?> solrRequest)
       throws SolrServerException, IOException {
-    // Some implementations of LBSolrClient.getClient(...) return a Http2SolrClient that may not be
+    // Some implementations of LBSolrClient.getClient(...) return a HttpJettySolrClient that may not
+    // be
     // pointed at the desired URL (or any URL for that matter).  We special case that here to ensure
     // the appropriate URL is provided.
     if (solrClient instanceof HttpSolrClientBase hasReqWithUrl) {
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/AuthenticationStoreHolder.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/AuthenticationStoreHolder.java
similarity index 98%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/AuthenticationStoreHolder.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/AuthenticationStoreHolder.java
index 85b62ab64a4..a80ced5abdb 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/AuthenticationStoreHolder.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/AuthenticationStoreHolder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.net.URI;
 import org.eclipse.jetty.client.Authentication;
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/CloudJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/CloudJettySolrClient.java
new file mode 100644
index 00000000000..2e00c97c276
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/CloudJettySolrClient.java
@@ -0,0 +1,66 @@
+/*
+ * 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.jetty;
+
+import java.util.List;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.ClusterStateProvider;
+
+/**
+ * A {@link org.apache.solr.client.solrj.impl.CloudSolrClient} using Jetty {@code HttpClient} for
+ * HTTP communication. This is Solr's most robust CloudSolrClient.
+ */
+public class CloudJettySolrClient extends CloudHttp2SolrClient {
+
+  protected CloudJettySolrClient(Builder builder) {
+    super(builder);
+  }
+
+  public static class Builder extends CloudSolrClient.Builder {
+
+    public Builder(List<String> solrUrls) {
+      super(solrUrls);
+    }
+
+    public Builder(ClusterStateProvider stateProvider) {
+      super(stateProvider);
+    }
+
+    @Override
+    public CloudJettySolrClient build() {
+      return new CloudJettySolrClient(this);
+    }
+
+    @Override
+    protected HttpJettySolrClient createOrGetHttpClient() {
+      if (httpClient != null) {
+        return (HttpJettySolrClient) httpClient;
+      } else if (internalClientBuilder != null) {
+        return (HttpJettySolrClient) internalClientBuilder.build();
+      } else {
+        return new HttpJettySolrClient.Builder().build();
+      }
+    }
+  }
+
+  @Override
+  public HttpJettySolrClient getHttpClient() {
+    return (HttpJettySolrClient) super.getHttpClient();
+  }
+}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClient.java
similarity index 85%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClient.java
index 866ac66729e..2ac6577d8b2 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClient.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -25,6 +25,8 @@ import java.util.Locale;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.common.params.ModifiableSolrParams;
@@ -35,24 +37,26 @@ import org.eclipse.jetty.client.OutputStreamRequestContent;
 import org.eclipse.jetty.client.Request;
 import org.eclipse.jetty.http.HttpMethod;
 
-/** A ConcurrentUpdate SolrClient using {@link Http2SolrClient}. */
+/** A ConcurrentUpdate SolrClient using {@link HttpJettySolrClient}. */
 public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrClient {
   protected static final Charset FALLBACK_CHARSET = StandardCharsets.UTF_8;
 
-  private final Http2SolrClient client;
+  private final HttpJettySolrClient client;
 
   public static class Builder extends ConcurrentUpdateBaseSolrClient.Builder {
     /**
-     * @see ConcurrentUpdateBaseSolrClient.Builder#Builder(String, HttpSolrClientBase)
+     * @see org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient.Builder#Builder(String,
+     *     HttpSolrClientBase)
      */
-    public Builder(String baseUrl, Http2SolrClient client) {
+    public Builder(String baseUrl, HttpJettySolrClient client) {
       this(baseUrl, client, false);
     }
 
     /**
-     * @see ConcurrentUpdateBaseSolrClient.Builder#Builder(String, HttpSolrClientBase, boolean)
+     * @see org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient.Builder#Builder(String,
+     *     HttpSolrClientBase, boolean)
      */
-    public Builder(String baseSolrUrl, Http2SolrClient client, boolean closeHttpClient) {
+    public Builder(String baseSolrUrl, HttpJettySolrClient client, boolean closeHttpClient) {
       super(baseSolrUrl, client, closeHttpClient);
       this.idleTimeoutMillis = client.getIdleTimeoutMillis();
     }
@@ -65,7 +69,7 @@ public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrCli
 
   protected ConcurrentUpdateJettySolrClient(Builder builder) {
     super(builder);
-    this.client = (Http2SolrClient) builder.client;
+    this.client = (HttpJettySolrClient) builder.getClient();
   }
 
   @Override
@@ -141,7 +145,7 @@ public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrCli
 
   private OutStream initOutStream(String baseUrl, UpdateRequest updateRequest, String collection)
       throws IOException {
-    String contentType = client.requestWriter.getUpdateContentType();
+    String contentType = client.getRequestWriter().getUpdateContentType();
     final SolrParams origParams = updateRequest.getParams();
     ModifiableSolrParams requestParams =
         client.initializeSolrParams(updateRequest, client.responseParser(updateRequest));
@@ -159,10 +163,10 @@ public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrCli
             .body(content);
     client.decorateRequest(postRequest, updateRequest, false);
     InputStreamResponseListener responseListener =
-        new Http2SolrClient.InputStreamReleaseTrackingResponseListener();
+        new HttpJettySolrClient.InputStreamReleaseTrackingResponseListener();
     postRequest.send(responseListener);
 
-    boolean isXml = ClientUtils.TEXT_XML.equals(client.requestWriter.getUpdateContentType());
+    boolean isXml = ClientUtils.TEXT_XML.equals(client.getRequestWriter().getUpdateContentType());
     OutStream outStream = new OutStream(collection, origParams, content, responseListener, isXml);
     if (isXml) {
       outStream.write("<stream>".getBytes(FALLBACK_CHARSET));
@@ -172,7 +176,7 @@ public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrCli
 
   private void send(OutStream outStream, SolrRequest<?> req, String collection) throws IOException {
     assert outStream.belongToThisStream(req, collection);
-    client.requestWriter.write(req, outStream.content.getOutputStream());
+    client.getRequestWriter().write(req, outStream.content.getOutputStream());
     if (outStream.isXml) {
       // check for commit or optimize
       SolrParams params = req.getParams();
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/jetty/HttpJettySolrClient.java
similarity index 87%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
index 7a377946811..b485d2ccf48 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpJettySolrClient.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.io.FilterInputStream;
 import java.io.IOException;
@@ -43,13 +43,17 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrClientFunction;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.embedded.SSLConfig;
-import org.apache.solr.client.solrj.impl.HttpListenerFactory.RequestResponseListener;
-import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
+import org.apache.solr.client.solrj.impl.HttpClientBuilderFactory;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBuilderBase;
+import org.apache.solr.client.solrj.impl.LBSolrClient;
+import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.HttpListenerFactory.RequestResponseListener;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.ContentStream;
+import org.apache.solr.common.util.EnvUtils;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.ObjectReleaseTracker;
@@ -96,7 +100,8 @@ import org.slf4j.MDC;
  * HttpSolrClientBuilderBase#useHttp1_1(boolean)}. In retrospect, the name should have been {@code
  * HttpJettySolrClient}.
  */
-public class Http2SolrClient extends HttpSolrClientBase {
+public class HttpJettySolrClient extends HttpSolrClientBase {
+  // formerly known at Http2SolrClient
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   public static final String REQ_PRINCIPAL_KEY = "solr-req-principal";
   private static final String USER_AGENT =
@@ -119,26 +124,19 @@ public class Http2SolrClient extends HttpSolrClientBase {
 
   private KeyStoreScanner scanner;
 
-  protected Http2SolrClient(String serverBaseUrl, Builder builder) {
+  protected HttpJettySolrClient(String serverBaseUrl, Builder builder) {
     super(serverBaseUrl, builder);
 
     if (builder.httpClient != null) {
       // Validate that no conflicting options are provided when using an existing HttpClient
-      if (builder.followRedirects != null
-          || builder.connectionTimeoutMillis != null
-          || builder.maxConnectionsPerHost != null
-          || builder.useHttp1_1 != null
-          || builder.proxyHost != null
-          || builder.sslConfig != null
-          || builder.cookieStore != null
-          || builder.keyStoreReloadIntervalSecs != null) {
+      if (builder.hasHttpClientBuildingOptions()) {
         throw new IllegalArgumentException(
             "You cannot provide the HttpClient and also specify options that are used to build a new client");
       }
 
       this.httpClient = builder.httpClient;
       if (this.executor == null) {
-        this.executor = builder.executor;
+        this.executor = builder.getExecutor();
       }
 
       initAuthStoreFromExistingClient(httpClient);
@@ -197,7 +195,8 @@ public class Http2SolrClient extends HttpSolrClientBase {
           | ClassNotFoundException
           | InvocationTargetException
           | NoSuchMethodException e) {
-        throw new RuntimeException("Unable to instantiate " + Http2SolrClient.class.getName(), e);
+        throw new RuntimeException(
+            "Unable to instantiate " + HttpJettySolrClient.class.getName(), e);
       }
       factory.setup(this);
     }
@@ -214,7 +213,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   private HttpClient createHttpClient(Builder builder) {
-    executor = builder.executor;
+    executor = builder.getExecutor();
     if (executor == null) {
       BlockingArrayQueue<Runnable> queue = new BlockingArrayQueue<>(256, 256);
       this.executor =
@@ -226,15 +225,17 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
 
     SSLConfig sslConfig =
-        builder.sslConfig != null ? builder.sslConfig : Http2SolrClient.defaultSSLConfig;
+        builder.sslConfig != null ? builder.sslConfig : HttpJettySolrClient.defaultSSLConfig;
     SslContextFactory.Client sslContextFactory =
         (sslConfig == null)
             ? getDefaultSslContextFactory()
             : sslConfig.createClientContextFactory();
 
     Long keyStoreReloadIntervalSecs = builder.keyStoreReloadIntervalSecs;
-    if (keyStoreReloadIntervalSecs == null && Boolean.getBoolean("solr.keyStoreReload.enabled")) {
-      keyStoreReloadIntervalSecs = Long.getLong("solr.jetty.sslContext.reload.scanInterval", 30);
+    if (keyStoreReloadIntervalSecs == null
+        && EnvUtils.getPropertyAsBool("solr.keystore.reload.enabled", false)) {
+      keyStoreReloadIntervalSecs =
+          EnvUtils.getPropertyAsLong("solr.jetty.ssl.context.reload.scan.interval.secs", 30l);
     }
     if (sslContextFactory != null
         && sslContextFactory.getKeyStoreResource() != null
@@ -268,17 +269,17 @@ public class Http2SolrClient extends HttpSolrClientBase {
     HttpClientTransport transport;
     if (builder.shouldUseHttp1_1()) {
       if (log.isDebugEnabled()) {
-        log.debug("Create Http2SolrClient with HTTP/1.1 transport");
+        log.debug("Create HttpJettySolrClient with HTTP/1.1 transport");
       }
 
       transport = new HttpClientTransportOverHTTP(clientConnector);
       httpClient = new HttpClient(transport);
-      if (builder.maxConnectionsPerHost != null) {
-        httpClient.setMaxConnectionsPerDestination(builder.maxConnectionsPerHost);
+      if (builder.getMaxConnectionsPerHost() != null) {
+        httpClient.setMaxConnectionsPerDestination(builder.getMaxConnectionsPerHost());
       }
     } else {
       if (log.isDebugEnabled()) {
-        log.debug("Create Http2SolrClient with HTTP/2 transport");
+        log.debug("Create HttpJettySolrClient with HTTP/2 transport");
       }
 
       HTTP2Client http2client = new HTTP2Client(clientConnector);
@@ -290,7 +291,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
     httpClient.setExecutor(this.executor);
     httpClient.setStrictEventOrdering(false);
     httpClient.setConnectBlocking(true);
-    httpClient.setFollowRedirects(Boolean.TRUE.equals(builder.followRedirects));
+    httpClient.setFollowRedirects(Boolean.TRUE.equals(builder.getFollowRedirects()));
     httpClient.setMaxRequestsQueuedPerDestination(
         asyncTracker.getMaxRequestsQueuedPerDestination());
     httpClient.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, USER_AGENT));
@@ -319,24 +320,24 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   private void setupProxy(Builder builder, HttpClient httpClient) {
-    if (builder.proxyHost == null) {
+    if (builder.getProxyHost() == null) {
       return;
     }
-    Address address = new Address(builder.proxyHost, builder.proxyPort);
+    Address address = new Address(builder.getProxyHost(), builder.getProxyPort());
 
     final ProxyConfiguration.Proxy proxy;
-    if (builder.proxyIsSocks4) {
-      proxy = new Socks4Proxy(address, builder.proxyIsSecure);
+    if (builder.isProxyIsSocks4()) {
+      proxy = new Socks4Proxy(address, builder.isProxyIsSecure());
     } else {
       final Protocol protocol;
       if (builder.shouldUseHttp1_1()) {
         protocol = HttpClientTransportOverHTTP.HTTP11;
       } else {
         // see HttpClientTransportOverHTTP2#newOrigin
-        String protocolName = builder.proxyIsSecure ? "h2" : "h2c";
+        String protocolName = builder.isProxyIsSecure() ? "h2" : "h2c";
         protocol = new Protocol(List.of(protocolName), false);
       }
-      proxy = new HttpProxy(address, builder.proxyIsSecure, protocol);
+      proxy = new HttpProxy(address, builder.isProxyIsSecure(), protocol);
     }
     httpClient.getProxyConfiguration().addProxy(proxy);
   }
@@ -504,7 +505,6 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   @Override
-  @Deprecated
   public NamedList<Object> requestWithBaseUrl(
       String baseUrl, SolrRequest<?> solrRequest, String collection)
       throws SolrServerException, IOException {
@@ -515,17 +515,18 @@ public class Http2SolrClient extends HttpSolrClientBase {
    * Temporarily modifies the client to use a different base URL and runs the provided lambda
    *
    * @param baseUrl the base URL to use on any requests made within the 'clientFunction' lambda
-   * @param clientFunction a Function that consumes a Http2SolrClient and returns an arbitrary value
+   * @param clientFunction a Function that consumes a HttpJettySolrClient and returns an arbitrary
+   *     value
    * @return the value returned after invoking 'clientFunction'
    * @param <R> the type returned by the provided function (and by this method)
    */
   public <R> R requestWithBaseUrl(
-      String baseUrl, SolrClientFunction<Http2SolrClient, R> clientFunction)
+      String baseUrl, SolrClientFunction<HttpJettySolrClient, R> clientFunction)
       throws SolrServerException, IOException {
 
     // Despite the name, try-with-resources used here to avoid IDE and ObjectReleaseTracker
     // complaints
-    try (final var derivedClient = new NoCloseHttp2SolrClient(baseUrl, this)) {
+    try (final var derivedClient = new NoCloseHttpJettySolrClient(baseUrl, this)) {
       return clientFunction.apply(derivedClient);
     }
   }
@@ -537,7 +538,30 @@ public class Http2SolrClient extends HttpSolrClientBase {
 
   @Override
   public HttpSolrClientBuilderBase<?, ?> builder() {
-    return new Http2SolrClient.Builder().withHttpClient(this);
+    return new HttpJettySolrClient.Builder().withHttpClient(this);
+  }
+
+  // merely exposing for superclass's method visibility to this package
+  @Override
+  protected ModifiableSolrParams initializeSolrParams(
+      SolrRequest<?> solrRequest, ResponseParser parserToUse) {
+    return super.initializeSolrParams(solrRequest, parserToUse);
+  }
+
+  // merely exposing for superclass's method visibility to this package
+  @Override
+  protected ResponseParser responseParser(SolrRequest<?> solrRequest) {
+    return super.responseParser(solrRequest);
+  }
+
+  // merely exposing for superclass's method visibility to this package
+  String basicAuthAuthorizationStr() {
+    return basicAuthAuthorizationStr;
+  }
+
+  // merely exposing for superclass's method visibility to this package
+  protected static String basicAuthCredentialsToAuthorizationString(String user, String pass) {
+    return HttpSolrClientBase.basicAuthCredentialsToAuthorizationString(user, pass);
   }
 
   private NamedList<Object> processErrorsAndResponse(
@@ -784,17 +808,17 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   /**
-   * An Http2SolrClient that doesn't close or cleanup any resources
+   * An HttpJettySolrClient that doesn't close or cleanup any resources
    *
    * <p>Only safe to use as a derived copy of an existing instance which retains responsibility for
    * closing all involved resources.
    *
    * @see #requestWithBaseUrl(String, SolrClientFunction)
    */
-  private static class NoCloseHttp2SolrClient extends Http2SolrClient {
+  private static class NoCloseHttpJettySolrClient extends HttpJettySolrClient {
 
-    public NoCloseHttp2SolrClient(String baseUrl, Http2SolrClient parentClient) {
-      super(baseUrl, new Http2SolrClient.Builder(baseUrl).withHttpClient(parentClient));
+    public NoCloseHttpJettySolrClient(String baseUrl, HttpJettySolrClient parentClient) {
+      super(baseUrl, new HttpJettySolrClient.Builder(baseUrl).withHttpClient(parentClient));
 
       this.asyncTracker = parentClient.asyncTracker;
     }
@@ -848,7 +872,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   public static class Builder
-      extends HttpSolrClientBuilderBase<Http2SolrClient.Builder, Http2SolrClient> {
+      extends HttpSolrClientBuilderBase<HttpJettySolrClient.Builder, HttpJettySolrClient> {
 
     private HttpClient httpClient;
 
@@ -870,7 +894,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
      * <p>The provided URL must point to the root Solr path ("/solr"), for example:
      *
      * <pre>
-     *   SolrClient client = new Http2SolrClient.Builder("http://my-solr-server:8983/solr")
+     *   SolrClient client = new HttpJettySolrClient.Builder("http://my-solr-server:8983/solr")
      *       .withDefaultCollection("core1")
      *       .build();
      *   QueryResponse resp = client.query(new SolrQuery("*:*"));
@@ -890,7 +914,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
      * @param listenerFactory a HttpListenerFactory
      * @return This Builder
      */
-    public Http2SolrClient.Builder addListenerFactory(HttpListenerFactory listenerFactory) {
+    public HttpJettySolrClient.Builder addListenerFactory(HttpListenerFactory listenerFactory) {
       if (this.listenerFactories == null) {
         this.listenerFactories = new ArrayList<>(1);
       }
@@ -904,14 +928,14 @@ public class Http2SolrClient extends HttpSolrClientBase {
      * @param listenerFactories a list of HttpListenerFactory instances
      * @return This Builder
      */
-    public Http2SolrClient.Builder withListenerFactories(
+    public HttpJettySolrClient.Builder withListenerFactories(
         List<HttpListenerFactory> listenerFactories) {
       this.listenerFactories = listenerFactories;
       return this;
     }
 
-    public HttpSolrClientBuilderBase<Http2SolrClient.Builder, Http2SolrClient> withSSLConfig(
-        SSLConfig sslConfig) {
+    public HttpSolrClientBuilderBase<HttpJettySolrClient.Builder, HttpJettySolrClient>
+        withSSLConfig(SSLConfig sslConfig) {
       this.sslConfig = sslConfig;
       return this;
     }
@@ -925,7 +949,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
      * @param unit The unit for the interval
      * @return This builder
      */
-    public Http2SolrClient.Builder withKeyStoreReloadInterval(long interval, TimeUnit unit) {
+    public HttpJettySolrClient.Builder withKeyStoreReloadInterval(long interval, TimeUnit unit) {
       this.keyStoreReloadIntervalSecs = unit.toSeconds(interval);
       if (this.keyStoreReloadIntervalSecs == 0 && interval > 0) {
         this.keyStoreReloadIntervalSecs = 1L;
@@ -937,7 +961,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
       if (cookieStore == null) {
         return cookieStore;
       }
-      if (Boolean.getBoolean("solr.http.disableCookies")) {
+      if (!EnvUtils.getPropertyAsBool("solr.solrj.http.cookies.enabled", false)) {
         return new HttpCookieStore.Empty();
       }
       /*
@@ -952,23 +976,23 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
 
     @Override
-    public Http2SolrClient build() {
-      return new Http2SolrClient(baseSolrUrl, this);
+    public HttpJettySolrClient build() {
+      return new HttpJettySolrClient(baseSolrUrl, this);
     }
 
     @Override
-    public Builder withHttpClient(Http2SolrClient http2SolrClient) {
-      super.withHttpClient(http2SolrClient);
-      this.httpClient = http2SolrClient.httpClient;
+    public Builder withHttpClient(HttpJettySolrClient httpJettySolrClient) {
+      super.withHttpClient(httpJettySolrClient);
+      this.httpClient = httpJettySolrClient.httpClient;
 
       if (this.idleTimeoutMillis == null) {
-        this.idleTimeoutMillis = http2SolrClient.idleTimeoutMillis;
+        this.idleTimeoutMillis = httpJettySolrClient.idleTimeoutMillis;
       }
       if (this.listenerFactories == null) {
-        this.listenerFactories = http2SolrClient.listenerFactory;
+        this.listenerFactories = httpJettySolrClient.listenerFactory;
       }
       if (this.executor == null) {
-        this.executor = http2SolrClient.executor;
+        this.executor = httpJettySolrClient.executor;
       }
       return this;
     }
@@ -983,15 +1007,26 @@ public class Http2SolrClient extends HttpSolrClientBase {
       this.cookieStore = cookieStore;
       return this;
     }
+
+    private boolean hasHttpClientBuildingOptions() {
+      return followRedirects != null
+          || connectionTimeoutMillis != null
+          || maxConnectionsPerHost != null
+          || useHttp1_1 != null
+          || proxyHost != null
+          || sslConfig != null
+          || cookieStore != null
+          || keyStoreReloadIntervalSecs != null;
+    }
   }
 
   public static void setDefaultSSLConfig(SSLConfig sslConfig) {
-    Http2SolrClient.defaultSSLConfig = sslConfig;
+    HttpJettySolrClient.defaultSSLConfig = sslConfig;
   }
 
   // public for testing, only used by tests
   public static void resetSslContextFactory() {
-    Http2SolrClient.defaultSSLConfig = null;
+    HttpJettySolrClient.defaultSSLConfig = null;
   }
 
   /* package-private for testing */
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpListenerFactory.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpListenerFactory.java
similarity index 92%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpListenerFactory.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpListenerFactory.java
index cf42a0107f0..e084e3fff65 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpListenerFactory.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/HttpListenerFactory.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import org.eclipse.jetty.client.Request;
 import org.eclipse.jetty.client.Response;
@@ -28,7 +28,7 @@ public interface HttpListenerFactory {
     /**
      * Callback method invoked when the request begins being processed in order to be sent. This is
      * the last opportunity to modify the request. This method will NOT be ensured to be called on
-     * the same thread as the thread calling {@code Http2SolrClient} methods.
+     * the same thread as the thread calling {@code HttpJettySolrClient} methods.
      *
      * @param request the request that begins being processed
      */
@@ -37,7 +37,7 @@ public interface HttpListenerFactory {
 
     /**
      * Callback method invoked when the request is queued, waiting to be sent. This method will be
-     * ensured to be called on the same thread as the thread calling {@code Http2SolrClient}
+     * ensured to be called on the same thread as the thread calling {@code HttpJettySolrClient}
      * methods.
      *
      * @param request the request being queued
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
index 4790fabcf3a..970199edb7a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
@@ -21,7 +21,6 @@ import java.util.concurrent.CompletableFuture;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.LBAsyncSolrClient;
 import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.common.util.NamedList;
@@ -33,9 +32,9 @@ public class LBJettySolrClient extends LBAsyncSolrClient {
     super(builder);
   }
 
-  public static class Builder extends LBSolrClient.Builder<Http2SolrClient> {
+  public static class Builder extends LBSolrClient.Builder<HttpJettySolrClient> {
 
-    public Builder(Http2SolrClient solrClient, Endpoint... endpoints) {
+    public Builder(HttpJettySolrClient solrClient, Endpoint... endpoints) {
       super(solrClient, endpoints);
     }
 
@@ -49,6 +48,6 @@ public class LBJettySolrClient extends LBAsyncSolrClient {
   protected CompletableFuture<NamedList<Object>> requestAsyncWithUrl(
       SolrClient client, String baseUrl, SolrRequest<?> request)
       throws SolrServerException, IOException {
-    return ((Http2SolrClient) client).requestWithBaseUrl(baseUrl, c -> c.requestAsync(request));
+    return ((HttpJettySolrClient) client).requestWithBaseUrl(baseUrl, c -> c.requestAsync(request));
   }
 }
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/jetty/PreemptiveBasicAuthClientBuilderFactory.java
similarity index 94%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/PreemptiveBasicAuthClientBuilderFactory.java
index e0680be0874..a7553d6cb3d 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactory.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/PreemptiveBasicAuthClientBuilderFactory.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -26,7 +26,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import org.apache.solr.client.solrj.util.SolrBasicAuthentication;
+import org.apache.solr.client.solrj.impl.HttpClientBuilderFactory;
+import org.apache.solr.client.solrj.impl.SolrHttpConstants;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.StrUtils;
@@ -69,7 +70,7 @@ public class PreemptiveBasicAuthClientBuilderFactory implements HttpClientBuilde
   public void close() throws IOException {}
 
   @Override
-  public void setup(Http2SolrClient client) {
+  public void setup(HttpJettySolrClient client) {
     final String basicAuthUser =
         CREDENTIAL_RESOLVER.defaultParams.get(SolrHttpConstants.PROP_BASIC_AUTH_USER);
     final String basicAuthPass =
@@ -77,7 +78,7 @@ public class PreemptiveBasicAuthClientBuilderFactory implements HttpClientBuilde
     this.setup(client, basicAuthUser, basicAuthPass);
   }
 
-  public void setup(Http2SolrClient client, String basicAuthUser, String basicAuthPass) {
+  public void setup(HttpJettySolrClient client, String basicAuthUser, String basicAuthPass) {
     if (basicAuthUser == null || basicAuthPass == null) {
       throw new IllegalArgumentException(
           "username & password must be specified with " + getClass().getName());
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SSLConfig.java
similarity index 99%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SSLConfig.java
index 0acb4395e08..b8a344852ab 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/embedded/SSLConfig.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SSLConfig.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.embedded;
+package org.apache.solr.client.solrj.jetty;
 
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/util/SolrBasicAuthentication.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SolrBasicAuthentication.java
similarity index 98%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/util/SolrBasicAuthentication.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SolrBasicAuthentication.java
index 002a34e53ee..69fe77b1097 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/util/SolrBasicAuthentication.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/SolrBasicAuthentication.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.util;
+package org.apache.solr.client.solrj.jetty;
 
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleBinaryHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleBinaryHttp2Test.java
index 8a7987d6ec9..991bbdedf71 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleBinaryHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleBinaryHttp2Test.java
@@ -19,9 +19,9 @@ package org.apache.solr.client.solrj;
 
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
 import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.junit.BeforeClass;
 
 /**
@@ -38,7 +38,7 @@ public class SolrExampleBinaryHttp2Test extends SolrExampleTests {
 
   @Override
   public SolrClient createNewSolrClient() {
-    return new Http2SolrClient.Builder(getBaseUrl())
+    return new HttpJettySolrClient.Builder(getBaseUrl())
         .withDefaultCollection(DEFAULT_TEST_CORENAME)
         .withConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
         .withRequestWriter(new JavaBinRequestWriter())
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
index 9050a0dec7c..338aea55400 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
@@ -60,7 +60,7 @@ public class SolrExampleJettyTest extends SolrExampleTests {
   @Test
   public void testBadSetup() {
     String url = "http" + (isSSLMode() ? "s" : "") + "://127.0.0.1/?core=xxx";
-    // This test does NOT fail for Http2SolrClient
+    // This test does NOT fail for HttpJettySolrClient
     expectThrows(Exception.class, () -> new HttpSolrClient.Builder(url).build());
   }
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
index 46369c1f0cd..1e9ee584448 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
@@ -23,9 +23,9 @@ import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.StreamingResponseCallback;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
 import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrInputDocument;
@@ -38,8 +38,8 @@ public class SolrExampleStreamingBinaryHttp2Test extends SolrExampleStreamingHtt
   public SolrClient createNewSolrClient() {
     String url = getBaseUrl();
     // smaller queue size hits locks more often
-    Http2SolrClient solrClient =
-        new Http2SolrClient.Builder()
+    var solrClient =
+        new HttpJettySolrClient.Builder()
             .withRequestWriter(new JavaBinRequestWriter())
             .withResponseParser(new JavaBinResponseParser())
             .build();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
index cf7bc4bd10e..99027d7c0ff 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
@@ -23,10 +23,10 @@ import java.util.EnumSet;
 import java.util.List;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrExampleTests;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateJettySolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.XMLRequestWriter;
 import org.apache.solr.client.solrj.impl.XMLResponseParser;
+import org.apache.solr.client.solrj.jetty.ConcurrentUpdateJettySolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
@@ -47,8 +47,8 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
   public SolrClient createNewSolrClient() {
     String url = getBaseUrl();
     // smaller queue size hits locks more often
-    Http2SolrClient solrClient =
-        new Http2SolrClient.Builder()
+    var solrClient =
+        new HttpJettySolrClient.Builder()
             .withRequestWriter(new XMLRequestWriter())
             .withResponseParser(new XMLResponseParser())
             .build();
@@ -65,7 +65,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
     // SOLR-3903
     final List<Throwable> failures = new ArrayList<>();
     final String serverUrl = getBaseUrl();
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             new FailureRecordingConcurrentUpdateSolrClient.Builder(serverUrl, http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
@@ -108,7 +108,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
     }
 
     static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
-      public Builder(String baseSolrUrl, Http2SolrClient http2Client) {
+      public Builder(String baseSolrUrl, HttpJettySolrClient http2Client) {
         super(baseSolrUrl, http2Client);
       }
 
@@ -135,7 +135,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
 
     public static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
 
-      public Builder(String baseSolrUrl, Http2SolrClient http2Client) {
+      public Builder(String baseSolrUrl, HttpJettySolrClient http2Client) {
         super(baseSolrUrl, http2Client, true);
       }
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleXMLHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleXMLHttp2Test.java
index 765c2b33573..113e9f1c213 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleXMLHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleXMLHttp2Test.java
@@ -20,9 +20,9 @@ package org.apache.solr.client.solrj.embedded;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrExampleTests;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.XMLRequestWriter;
 import org.apache.solr.client.solrj.impl.XMLResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.junit.BeforeClass;
 
 /**
@@ -38,8 +38,8 @@ public class SolrExampleXMLHttp2Test extends SolrExampleTests {
   @Override
   public SolrClient createNewSolrClient() {
 
-    Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl())
+    var client =
+        new HttpJettySolrClient.Builder(getBaseUrl())
             .withDefaultCollection(DEFAULT_TEST_CORENAME)
             .withConnectionTimeout(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
             .withRequestWriter(new XMLRequestWriter())
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
index 6f8f8587b51..cd9b4d20096 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -55,8 +56,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @Test
   public void testSingleZkHostSpecified() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
@@ -73,7 +73,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     zkHostList.add(ANY_ZK_HOST);
     zkHostList.add(ANY_OTHER_ZK_HOST);
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(zkHostList, Optional.of(ANY_CHROOT)).build()) {
+        new CloudSolrClient.Builder(zkHostList, Optional.of(ANY_CHROOT)).build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
         final String clientZkHost = zkClientClusterStateProvider.getZkHost();
@@ -90,7 +90,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     zkHosts.add(ANY_ZK_HOST);
     zkHosts.add(ANY_OTHER_ZK_HOST);
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(zkHosts, Optional.of(ANY_CHROOT)).build()) {
+        new CloudSolrClient.Builder(zkHosts, Optional.of(ANY_CHROOT)).build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
         final String clientZkHost = zkClientClusterStateProvider.getZkHost();
@@ -104,8 +104,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @Test
   public void testByDefaultConfiguresClientToSendUpdatesOnlyToShardLeaders() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       assertTrue(createdClient.isUpdatesToLeaders());
     }
@@ -114,8 +113,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @Test
   public void testIsDirectUpdatesToLeadersOnlyDefault() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       assertFalse(createdClient.isDirectUpdatesToLeadersOnly());
     }
@@ -126,29 +124,28 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     expectThrows(
         IllegalStateException.class,
         () ->
-            new CloudHttp2SolrClient.Builder(
+            new CloudSolrClient.Builder(
                     Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
-                .withHttpClient(mock(Http2SolrClient.class))
-                .withHttpClientBuilder(mock(Http2SolrClient.Builder.class))
+                .withHttpClient(mock(HttpJettySolrClient.class))
+                .withHttpClientBuilder(mock(HttpJettySolrClient.Builder.class))
                 .build());
     expectThrows(
         IllegalStateException.class,
         () ->
-            new CloudHttp2SolrClient.Builder(
+            new CloudSolrClient.Builder(
                     Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
-                .withHttpClientBuilder(mock(Http2SolrClient.Builder.class))
-                .withHttpClient(mock(Http2SolrClient.class))
+                .withHttpClientBuilder(mock(HttpJettySolrClient.Builder.class))
+                .withHttpClient(mock(HttpJettySolrClient.class))
                 .build());
   }
 
   @Test
   public void testProvideInternalJettyClientBuilder() throws IOException {
-    Http2SolrClient http2Client = mock(Http2SolrClient.class);
-    Http2SolrClient.Builder http2ClientBuilder = mock(Http2SolrClient.Builder.class);
+    HttpJettySolrClient http2Client = mock(HttpJettySolrClient.class);
+    HttpJettySolrClient.Builder http2ClientBuilder = mock(HttpJettySolrClient.Builder.class);
     when(http2ClientBuilder.build()).thenReturn(http2Client);
-    CloudHttp2SolrClient.Builder clientBuilder =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+    CloudSolrClient.Builder clientBuilder =
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withHttpClientBuilder(http2ClientBuilder);
     verify(http2ClientBuilder, never()).build();
     try (CloudHttp2SolrClient client = clientBuilder.build()) {
@@ -165,9 +162,8 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     var http2Client = mock(HttpJdkSolrClient.class);
     HttpJdkSolrClient.Builder http2ClientBuilder = mock(HttpJdkSolrClient.Builder.class);
     when(http2ClientBuilder.build()).thenReturn(http2Client);
-    CloudHttp2SolrClient.Builder clientBuilder =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+    CloudSolrClient.Builder clientBuilder =
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withHttpClientBuilder(http2ClientBuilder);
     verify(http2ClientBuilder, never()).build();
     try (CloudHttp2SolrClient client = clientBuilder.build()) {
@@ -181,10 +177,9 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
 
   @Test
   public void testProvideExternalJettyClient() throws IOException {
-    Http2SolrClient http2Client = mock(Http2SolrClient.class);
-    CloudHttp2SolrClient.Builder clientBuilder =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+    HttpJettySolrClient http2Client = mock(HttpJettySolrClient.class);
+    CloudSolrClient.Builder clientBuilder =
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withHttpClient(http2Client);
     try (CloudHttp2SolrClient client = clientBuilder.build()) {
       assertEquals(http2Client, client.getHttpClient());
@@ -196,9 +191,8 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @Test
   public void testProvideExternalJdkClient() throws IOException {
     HttpJdkSolrClient http2Client = mock(HttpJdkSolrClient.class);
-    CloudHttp2SolrClient.Builder clientBuilder =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+    CloudSolrClient.Builder clientBuilder =
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withHttpClient(http2Client);
     try (CloudHttp2SolrClient client = clientBuilder.build()) {
       assertEquals(http2Client, client.getHttpClient());
@@ -210,19 +204,17 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @Test
   public void testDefaultClientUsesJetty() throws IOException {
     try (CloudHttp2SolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
-      assertTrue(createdClient.getHttpClient() instanceof Http2SolrClient);
-      assertTrue(createdClient.getLbClient().getClient(null) instanceof Http2SolrClient);
+      assertTrue(createdClient.getHttpClient() instanceof HttpJettySolrClient);
+      assertTrue(createdClient.getLbClient().getClient(null) instanceof HttpJettySolrClient);
     }
   }
 
   @Test
   public void testDefaultCollectionPassedFromBuilderToClient() throws IOException {
     try (CloudHttp2SolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withDefaultCollection("aCollection")
             .build()) {
       assertEquals("aCollection", createdClient.getDefaultCollection());
@@ -231,10 +223,10 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
 
   /**
    * Tests the consistency between the HTTP client used by {@link CloudHttp2SolrClient} and the one
-   * used by its associated {@link Http2ClusterStateProvider}. This method ensures that whether a
+   * used by its associated {@link HttpClusterStateProvider}. This method ensures that whether a
    * {@link CloudHttp2SolrClient} is created with a specific HTTP client, an internal client
    * builder, or with no specific HTTP client at all, the same HTTP client instance is used both by
-   * the {@link CloudHttp2SolrClient} and by its {@link Http2ClusterStateProvider}.
+   * the {@link CloudHttp2SolrClient} and by its {@link HttpClusterStateProvider}.
    */
   @Test
   public void testHttpClientPreservedInHttp2ClusterStateProvider() throws IOException {
@@ -244,21 +236,21 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     testHttpClientConsistency(solrUrls, null, null);
 
     // httpClient - No internalClientBuilder
-    try (Http2SolrClient httpClient = new Http2SolrClient.Builder().build()) {
+    try (var httpClient = new HttpJettySolrClient.Builder().build()) {
       testHttpClientConsistency(solrUrls, httpClient, null);
     }
 
     // No httpClient - internalClientBuilder
-    Http2SolrClient.Builder internalClientBuilder = new Http2SolrClient.Builder();
+    var internalClientBuilder = new HttpJettySolrClient.Builder();
     testHttpClientConsistency(solrUrls, null, internalClientBuilder);
   }
 
   private void testHttpClientConsistency(
       List<String> solrUrls,
-      Http2SolrClient httpClient,
-      Http2SolrClient.Builder internalClientBuilder)
+      HttpJettySolrClient httpClient,
+      HttpJettySolrClient.Builder internalClientBuilder)
       throws IOException {
-    CloudHttp2SolrClient.Builder clientBuilder = new CloudHttp2SolrClient.Builder(solrUrls);
+    CloudSolrClient.Builder clientBuilder = new CloudSolrClient.Builder(solrUrls);
 
     if (httpClient != null) {
       clientBuilder.withHttpClient(httpClient);
@@ -269,14 +261,13 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     try (CloudHttp2SolrClient client = clientBuilder.build()) {
       assertEquals(
           client.getHttpClient(),
-          ((Http2ClusterStateProvider) client.getClusterStateProvider()).getHttpClient());
+          ((HttpClusterStateProvider) client.getClusterStateProvider()).getHttpClient());
     }
   }
 
   public void testCustomStateProvider() throws IOException {
     ZkClientClusterStateProvider stateProvider = mock(ZkClientClusterStateProvider.class);
-    try (CloudSolrClient cloudSolrClient =
-        new CloudHttp2SolrClient.Builder(stateProvider).build()) {
+    try (CloudSolrClient cloudSolrClient = new CloudSolrClient.Builder(stateProvider).build()) {
       assertEquals(stateProvider, cloudSolrClient.getClusterStateProvider());
     }
     verify(stateProvider, times(1)).close();
@@ -286,7 +277,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
   @SuppressWarnings({"try"})
   public void test0Timeouts() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.empty())
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.empty())
             .withZkConnectTimeout(0, TimeUnit.MILLISECONDS)
             .withZkClientTimeout(0, TimeUnit.MILLISECONDS)
             .build()) {
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientMultiConstructorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientMultiConstructorTest.java
index c4210bab7b4..5442d705860 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientMultiConstructorTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientMultiConstructorTest.java
@@ -69,7 +69,7 @@ public class CloudHttp2SolrClientMultiConstructorTest extends SolrTestCase {
     }
 
     try (CloudSolrClient client =
-        new CloudHttp2SolrClient.Builder(new ArrayList<>(hosts), Optional.ofNullable(clientChroot))
+        new CloudSolrClient.Builder(new ArrayList<>(hosts), Optional.ofNullable(clientChroot))
             .build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(client)) {
@@ -82,6 +82,6 @@ public class CloudHttp2SolrClientMultiConstructorTest extends SolrTestCase {
   public void testBadChroot() {
     final List<String> zkHosts = new ArrayList<>();
     zkHosts.add("host1:2181");
-    new CloudHttp2SolrClient.Builder(zkHosts, Optional.of("foo")).build();
+    new CloudSolrClient.Builder(zkHosts, Optional.of("foo")).build();
   }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
index 58b1b2db48c..9584ffc65cc 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
@@ -21,6 +21,7 @@ import static org.apache.solr.util.SolrJMetricTestUtils.getPrometheusMetricValue
 
 import java.util.Collections;
 import java.util.Optional;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.SolrInputDocument;
@@ -49,7 +50,7 @@ public class CloudHttp2SolrClientRetryTest extends SolrCloudTestCase {
   public void testRetry() throws Exception {
 
     // Randomly decide to use either the Jetty Http Client or the JDK Http Client
-    var jettyClientBuilder = new Http2SolrClient.Builder();
+    var jettyClientBuilder = new HttpJettySolrClient.Builder();
 
     // forcing Http/1.1 to avoid an extra HEAD request with the first update.
     // (This causes the counts to be 1 greater than what we test for here.)
@@ -59,7 +60,7 @@ public class CloudHttp2SolrClientRetryTest extends SolrCloudTestCase {
             .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT);
 
     var cloudSolrclientBuilder =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
             Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty());
     cloudSolrclientBuilder.withHttpClientBuilder(
         random().nextBoolean() ? jettyClientBuilder : jdkClientBuilder);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
index 8697baf87a8..22d581bd4b7 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
@@ -44,6 +44,7 @@ import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
 import org.apache.solr.client.solrj.apache.HttpClientUtil;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
@@ -121,20 +122,20 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
     solrUrls.add(cluster.getJettySolrRunner(0).getBaseUrl().toString());
 
     httpJettyBasedCloudSolrClient =
-        new CloudHttp2SolrClient.Builder(solrUrls)
-            .withHttpClientBuilder(new Http2SolrClient.Builder())
+        new CloudSolrClient.Builder(solrUrls)
+            .withHttpClientBuilder(new HttpJettySolrClient.Builder())
             .build();
-    assertTrue(httpJettyBasedCloudSolrClient.getHttpClient() instanceof Http2SolrClient);
+    assertTrue(httpJettyBasedCloudSolrClient.getHttpClient() instanceof HttpJettySolrClient);
     assertTrue(
         httpJettyBasedCloudSolrClient.getClusterStateProvider()
-            instanceof Http2ClusterStateProvider<?>);
+            instanceof HttpClusterStateProvider<?>);
     assertTrue(
-        ((Http2ClusterStateProvider<?>) httpJettyBasedCloudSolrClient.getClusterStateProvider())
+        ((HttpClusterStateProvider<?>) httpJettyBasedCloudSolrClient.getClusterStateProvider())
                 .getHttpClient()
-            instanceof Http2SolrClient);
+            instanceof HttpJettySolrClient);
 
     httpJdkBasedCloudSolrClient =
-        new CloudHttp2SolrClient.Builder(solrUrls)
+        new CloudSolrClient.Builder(solrUrls)
             .withHttpClientBuilder(
                 new HttpJdkSolrClient.Builder()
                     .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT))
@@ -142,17 +143,17 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
     assertTrue(httpJdkBasedCloudSolrClient.getHttpClient() instanceof HttpJdkSolrClient);
     assertTrue(
         httpJdkBasedCloudSolrClient.getClusterStateProvider()
-            instanceof Http2ClusterStateProvider<?>);
+            instanceof HttpClusterStateProvider<?>);
     assertTrue(
-        ((Http2ClusterStateProvider<?>) httpJdkBasedCloudSolrClient.getClusterStateProvider())
+        ((HttpClusterStateProvider<?>) httpJdkBasedCloudSolrClient.getClusterStateProvider())
                 .getHttpClient()
             instanceof HttpJdkSolrClient);
 
     zkBasedCloudSolrClient =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
                 Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
             .build();
-    assertTrue(zkBasedCloudSolrClient.getHttpClient() instanceof Http2SolrClient);
+    assertTrue(zkBasedCloudSolrClient.getHttpClient() instanceof HttpJettySolrClient);
     assertTrue(
         zkBasedCloudSolrClient.getClusterStateProvider() instanceof ZkClientClusterStateProvider);
   }
@@ -345,7 +346,7 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
   private CloudSolrClient createHttpCSPBasedCloudSolrClient() {
     final List<String> solrUrls = new ArrayList<>();
     solrUrls.add(cluster.getJettySolrRunner(0).getBaseUrl().toString());
-    return new CloudHttp2SolrClient.Builder(solrUrls).build();
+    return new CloudSolrClient.Builder(solrUrls).build();
   }
 
   @Test
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
index 7af3580e687..80f7b5e625b 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java
@@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.response.CollectionAdminResponse;
 import org.apache.solr.cloud.SolrCloudTestCase;
@@ -101,9 +102,9 @@ public class ClusterStateProviderTest extends SolrCloudTestCase {
         new String[] {"http2ClusterStateProvider"}, new String[] {"zkClientClusterStateProvider"});
   }
 
-  static class ClosingHttp2ClusterStateProvider
-      extends Http2ClusterStateProvider<HttpSolrClientBase> {
-    public ClosingHttp2ClusterStateProvider(List<String> solrUrls, HttpSolrClientBase httpClient)
+  static class ClosingHttpClusterStateProvider
+      extends HttpClusterStateProvider<HttpSolrClientBase> {
+    public ClosingHttpClusterStateProvider(List<String> solrUrls, HttpSolrClientBase httpClient)
         throws Exception {
       super(solrUrls, httpClient);
     }
@@ -119,7 +120,7 @@ public class ClusterStateProviderTest extends SolrCloudTestCase {
     }
   }
 
-  private static Http2ClusterStateProvider<?> http2ClusterStateProvider(String userAgent) {
+  private static HttpClusterStateProvider<?> http2ClusterStateProvider(String userAgent) {
     try {
       var useJdkProvider = random().nextBoolean();
       HttpSolrClientBase client;
@@ -132,7 +133,7 @@ public class ClusterStateProviderTest extends SolrCloudTestCase {
                       .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT),
                   userAgent);
         } else {
-          var http2SolrClient = new Http2SolrClient.Builder().build();
+          var http2SolrClient = new HttpJettySolrClient.Builder().build();
           http2SolrClient
               .getHttpClient()
               .setUserAgentField(new HttpField(HttpHeader.USER_AGENT, userAgent));
@@ -144,13 +145,13 @@ public class ClusterStateProviderTest extends SolrCloudTestCase {
                 ? new HttpJdkSolrClient.Builder()
                     .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT)
                     .build()
-                : new Http2SolrClient.Builder().build();
+                : new HttpJettySolrClient.Builder().build();
       }
       var clientClassName = client.getClass().getName();
       log.info("Using Http client implementation: {}", clientClassName);
 
       var csp =
-          new ClosingHttp2ClusterStateProvider(
+          new ClosingHttpClusterStateProvider(
               List.of(
                   cluster.getJettySolrRunner(0).getBaseUrl().toString(),
                   cluster.getJettySolrRunner(1).getBaseUrl().toString()),
@@ -161,7 +162,7 @@ public class ClusterStateProviderTest extends SolrCloudTestCase {
     }
   }
 
-  private static Http2ClusterStateProvider<?> http2ClusterStateProvider() {
+  private static HttpClusterStateProvider<?> http2ClusterStateProvider() {
     return http2ClusterStateProvider(null);
   }
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
index b791a0286d1..4fde78387a4 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpClusterStateSSLTest.java
@@ -83,19 +83,17 @@ public class HttpClusterStateSSLTest extends SolrCloudTestCase {
 
     // verify the http derived cluster state (on the client side) agrees with what the server stored
     try (CloudSolrClient httpBasedCloudSolrClient =
-        new CloudHttp2SolrClient.Builder(Collections.singletonList(url0.toExternalForm()))
-            .build()) {
+        new CloudSolrClient.Builder(Collections.singletonList(url0.toExternalForm())).build()) {
       ClusterStateProvider csp = httpBasedCloudSolrClient.getClusterStateProvider();
-      assertTrue(csp instanceof Http2ClusterStateProvider);
+      assertTrue(csp instanceof HttpClusterStateProvider);
       verifyUrlSchemeInClusterState(csp.getCollection(collectionId), expectedReplicas);
     }
 
     // http2
     try (CloudSolrClient http2BasedClient =
-        new CloudHttp2SolrClient.Builder(Collections.singletonList(url0.toExternalForm()))
-            .build()) {
+        new CloudSolrClient.Builder(Collections.singletonList(url0.toExternalForm())).build()) {
       ClusterStateProvider csp = http2BasedClient.getClusterStateProvider();
-      assertTrue(csp instanceof Http2ClusterStateProvider);
+      assertTrue(csp instanceof HttpClusterStateProvider);
       verifyUrlSchemeInClusterState(csp.getCollection(collectionId), expectedReplicas);
     }
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
index ed0975b5181..f8d388b239d 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
@@ -31,6 +31,7 @@ import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.client.solrj.response.SolrResponseBase;
 import org.apache.solr.common.SolrInputDocument;
@@ -114,7 +115,7 @@ public class LB2SolrClientTest extends SolrTestCaseJ4 {
   private LBClientHolder client(LBSolrClient.Endpoint... baseSolrEndpoints) {
     if (random().nextBoolean()) {
       var delegateClient =
-          new Http2SolrClient.Builder()
+          new HttpJettySolrClient.Builder()
               .withConnectionTimeout(1000, TimeUnit.MILLISECONDS)
               .withIdleTimeout(2000, TimeUnit.MILLISECONDS)
               .build();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
index a83bd793b49..fafe1557819 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
@@ -29,6 +29,7 @@ import org.apache.solr.SolrTestCase;
 import org.apache.solr.client.solrj.SolrClientFunction;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.common.SolrException;
@@ -45,8 +46,9 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
     LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two");
     List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2);
 
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
+    var b =
+        new HttpJettySolrClient.Builder("http://base.url")
+            .withConnectionTimeout(10, TimeUnit.SECONDS);
 
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
         var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
@@ -73,8 +75,9 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
     LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two");
     List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2);
 
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
+    var b =
+        new HttpJettySolrClient.Builder("http://base.url")
+            .withConnectionTimeout(10, TimeUnit.SECONDS);
 
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
         var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
@@ -131,8 +134,9 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
     LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two");
     List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2);
 
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
+    var b =
+        new HttpJettySolrClient.Builder("http://base.url")
+            .withConnectionTimeout(10, TimeUnit.SECONDS);
 
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
         var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
@@ -195,8 +199,9 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
     LBSolrClient.Endpoint ep2 = new LBSolrClient.Endpoint("http://endpoint.two");
     List<LBSolrClient.Endpoint> endpointList = List.of(ep1, ep2);
 
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
+    var b =
+        new HttpJettySolrClient.Builder("http://base.url")
+            .withConnectionTimeout(10, TimeUnit.SECONDS);
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
         var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
 
@@ -253,7 +258,7 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
     }
   }
 
-  public static class MockHttpSolrClient extends Http2SolrClient {
+  public static class MockHttpSolrClient extends HttpJettySolrClient {
 
     public List<SolrRequest<?>> lastSolrRequests = new ArrayList<>();
 
@@ -287,7 +292,7 @@ public class LBAsyncSolrClientTest extends SolrTestCase {
 
     @Override
     public <R> R requestWithBaseUrl(
-        String baseUrl, SolrClientFunction<Http2SolrClient, R> clientFunction)
+        String baseUrl, SolrClientFunction<HttpJettySolrClient, R> clientFunction)
         throws SolrServerException, IOException {
       // This use of 'tmpBaseUrl' is NOT thread safe, but that's fine for our purposes here.
       try {
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
index 48ca0bd515a..8c9f8a60245 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/SendUpdatesToLeadersOverrideTest.java
@@ -215,7 +215,7 @@ public class SendUpdatesToLeadersOverrideTest extends SolrCloudTestCase {
       assertTrue(client.isUpdatesToLeaders());
     }
     try (CloudSolrClient client =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
                 Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
             .build()) {
       assertTrue(client.isUpdatesToLeaders());
@@ -246,7 +246,7 @@ public class SendUpdatesToLeadersOverrideTest extends SolrCloudTestCase {
 
   public void testHttp2ClientThatDefaultsToLeaders() throws Exception {
     try (CloudSolrClient client =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
                 Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
             .sendUpdatesOnlyToShardLeaders()
             .build()) {
@@ -257,7 +257,7 @@ public class SendUpdatesToLeadersOverrideTest extends SolrCloudTestCase {
 
   public void testHttp2ClientThatDoesNotDefaultToLeaders() throws Exception {
     try (CloudSolrClient client =
-        new CloudHttp2SolrClient.Builder(
+        new CloudSolrClient.Builder(
                 Collections.singletonList(cluster.getZkServer().getZkAddress()), Optional.empty())
             .sendUpdatesToAnyReplica()
             .build()) {
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientBadInputTest.java
similarity index 95%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientBadInputTest.java
index e6adb0d0987..e2e2d18b832 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientBadInputTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,7 +40,7 @@ public class ConcurrentUpdateJettySolrClientBadInputTest extends SolrJettyTestBa
   @Test
   public void testDeleteByIdReportsInvalidIdLists() throws Exception {
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var client =
             new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(ANY_COLLECTION)
@@ -73,7 +73,7 @@ public class ConcurrentUpdateJettySolrClientBadInputTest extends SolrJettyTestBa
           });
     }
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var client =
             new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(ANY_COLLECTION)
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
similarity index 96%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
index ef8ae2927cc..c8f23aaf673 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.io.IOException;
 import org.apache.solr.client.solrj.SolrClient;
@@ -65,7 +65,7 @@ public class ConcurrentUpdateJettySolrClientMultiCollectionTest extends SolrClou
   public void testEnsureDocumentsSentToCorrectCollection() throws Exception {
     int numTotalDocs = 1000;
     int numExpectedPerCollection = numTotalDocs / 2;
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var client =
             new ConcurrentUpdateJettySolrClient.Builder(solrUrl, http2Client)
                 .withQueueSize(numTotalDocs)
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientTest.java
similarity index 96%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientTest.java
index 39a1e75e400..3850d8f45da 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/ConcurrentUpdateJettySolrClientTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServlet;
@@ -188,7 +188,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
     final AtomicInteger errorCounter = new AtomicInteger(0);
     final StringBuilder errors = new StringBuilder();
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             new OutcomeCountingConcurrentUpdateSolrClient.Builder(
                     serverUrl, http2Client, successCounter, errorCounter, errors)
@@ -246,7 +246,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
     int cussThreadCount = 2;
     int cussQueueSize = 10;
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             (new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client))
                 .withQueueSize(cussQueueSize)
@@ -266,7 +266,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
               .getNumFound());
     }
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
@@ -288,7 +288,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
     int numRunnables = 5;
     int expected = numDocs * numRunnables;
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withQueueSize(cussQueueSize)
@@ -325,7 +325,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
       concurrentClient.shutdownNow();
     }
 
-    try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
+    try (var http2Client = new HttpJettySolrClient.Builder().build();
         var concurrentClient =
             new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
@@ -369,7 +369,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
 
       public Builder(
           String baseSolrUrl,
-          Http2SolrClient http2Client,
+          HttpJettySolrClient http2Client,
           AtomicInteger successCounter,
           AtomicInteger failureCounter,
           StringBuilder errors) {
@@ -395,7 +395,7 @@ public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
     InetAddress localHost = InetAddress.getLocalHost();
     try (ServerSocket server = new ServerSocket(0, 1, localHost);
         var http2Client =
-            new Http2SolrClient.Builder().withIdleTimeout(1, TimeUnit.MILLISECONDS).build();
+            new HttpJettySolrClient.Builder().withIdleTimeout(1, TimeUnit.MILLISECONDS).build();
         var client =
             new ConcurrentUpdateJettySolrClient.Builder(
                     "http://"
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientCompatibilityTest.java
similarity index 87%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientCompatibilityTest.java
index fbc3cb394cf..68f1b663a9f 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientCompatibilityTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import org.apache.solr.SolrJettyTestBase;
 import org.apache.solr.SolrTestCaseJ4;
@@ -32,15 +32,15 @@ import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
 
 @LogLevel("org.eclipse.jetty.client=DEBUG;org.eclipse.jetty.util=DEBUG")
 @SolrTestCaseJ4.SuppressSSL
-public class Http2SolrClientCompatibilityTest extends SolrJettyTestBase {
+public class HttpJettySolrClientCompatibilityTest extends SolrJettyTestBase {
 
   public void testSystemPropertyFlag() {
     System.setProperty("solr.http1", "true");
-    try (Http2SolrClient client = new Http2SolrClient.Builder().build()) {
+    try (var client = new HttpJettySolrClient.Builder().build()) {
       assertTrue(client.getHttpClient().getTransport() instanceof HttpClientTransportOverHTTP);
     }
     System.clearProperty("solr.http1");
-    try (Http2SolrClient client = new Http2SolrClient.Builder().build()) {
+    try (var client = new HttpJettySolrClient.Builder().build()) {
       assertTrue(client.getHttpClient().getTransport() instanceof HttpClientTransportOverHTTP2);
     }
   }
@@ -54,8 +54,8 @@ public class Http2SolrClientCompatibilityTest extends SolrJettyTestBase {
             .build();
     createAndStartJetty(legacyExampleCollection1SolrHome(), jettyConfig);
 
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + "/debug/foo").useHttp1_1(true).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + "/debug/foo").useHttp1_1(true).build()) {
       assertTrue(client.getHttpClient().getTransport() instanceof HttpClientTransportOverHTTP);
       try {
         client.query(new SolrQuery("*:*"), SolrRequest.METHOD.GET);
@@ -75,8 +75,8 @@ public class Http2SolrClientCompatibilityTest extends SolrJettyTestBase {
             .build();
     createAndStartJetty(legacyExampleCollection1SolrHome(), jettyConfig);
 
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + "/debug/foo").useHttp1_1(true).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + "/debug/foo").useHttp1_1(true).build()) {
       assertTrue(client.getHttpClient().getTransport() instanceof HttpClientTransportOverHTTP);
       try {
         client.query(new SolrQuery("*:*"), SolrRequest.METHOD.GET);
@@ -100,8 +100,7 @@ public class Http2SolrClientCompatibilityTest extends SolrJettyTestBase {
     createAndStartJetty(legacyExampleCollection1SolrHome(), jettyConfig);
 
     System.clearProperty("solr.http1");
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + "/debug/foo").build()) {
+    try (var client = new HttpJettySolrClient.Builder(getBaseUrl() + "/debug/foo").build()) {
       assertTrue(client.getHttpClient().getTransport() instanceof HttpClientTransportOverHTTP2);
       try {
         client.query(new SolrQuery("*:*"), SolrRequest.METHOD.GET);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientProxyTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientProxyTest.java
similarity index 92%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientProxyTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientProxyTest.java
index 10dc7930cdf..221e1517f33 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientProxyTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientProxyTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import com.carrotsearch.randomizedtesting.RandomizedTest;
 import java.util.Arrays;
@@ -25,6 +25,8 @@ import java.util.stream.Collectors;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.cloud.SocketProxy;
+import org.apache.solr.client.solrj.impl.HttpJdkSolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.embedded.JettyConfig;
 import org.apache.solr.util.ExternalPaths;
@@ -34,7 +36,7 @@ import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
 
-public class Http2SolrClientProxyTest extends SolrTestCaseJ4 {
+public class HttpJettySolrClientProxyTest extends SolrTestCaseJ4 {
 
   @ClassRule public static SolrJettyTestRule solrClientTestRule = new SolrJettyTestRule();
 
@@ -71,10 +73,10 @@ public class Http2SolrClientProxyTest extends SolrTestCaseJ4 {
   public void testProxyWithHttp2SolrClient() throws Exception {
     assertNotNull(proxy);
     var builder =
-        new Http2SolrClient.Builder(url)
+        new HttpJettySolrClient.Builder(url)
             .withProxyConfiguration(host, proxy.getListenPort(), false, false);
 
-    try (Http2SolrClient client = builder.build()) {
+    try (HttpJettySolrClient client = builder.build()) {
       testProxy(client);
     }
   }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientTest.java
similarity index 80%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientTest.java
index 856e2103abe..62d1cea2d76 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/HttpJettySolrClientTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import static org.apache.solr.handler.admin.api.ReplicationAPIBase.FILE_STREAM;
 import static org.hamcrest.core.StringContains.containsStringIgnoringCase;
@@ -35,6 +35,14 @@ 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.HttpSolrClientBuilderBase;
+import org.apache.solr.client.solrj.impl.HttpSolrClientTestBase;
+import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
+import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
+import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
+import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.impl.XMLRequestWriter;
+import org.apache.solr.client.solrj.impl.XMLResponseParser;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.SolrPing;
 import org.apache.solr.common.SolrException;
@@ -50,19 +58,19 @@ import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.junit.Test;
 
-public class Http2SolrClientTest extends HttpSolrClientTestBase {
+public class HttpJettySolrClientTest extends HttpSolrClientTestBase {
 
   @Override
   protected String expectedUserAgent() {
-    return "Solr[" + Http2SolrClient.class.getName() + "] " + SolrVersion.LATEST_STRING;
+    return "Solr[" + HttpJettySolrClient.class.getName() + "] " + SolrVersion.LATEST_STRING;
   }
 
   @Override
   @SuppressWarnings(value = "unchecked")
   protected <B extends HttpSolrClientBuilderBase<?, ?>> B builder(
       String url, int connectionTimeout, int socketTimeout) {
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder(url)
+    var b =
+        new HttpJettySolrClient.Builder(url)
             .withConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS)
             .withIdleTimeout(socketTimeout, TimeUnit.MILLISECONDS);
     return (B) b;
@@ -71,8 +79,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testTimeout() throws Exception {
     SolrQuery q = new SolrQuery("*:*");
-    try (Http2SolrClient client =
-        (Http2SolrClient)
+    try (HttpJettySolrClient client =
+        (HttpJettySolrClient)
             builder(getBaseUrl() + SLOW_SERVLET_PATH, DEFAULT_CONNECTION_TIMEOUT, 2000)
                 .withDefaultCollection(DEFAULT_CORE)
                 .build()) {
@@ -86,8 +94,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void test0IdleTimeout() throws Exception {
     SolrQuery q = new SolrQuery("*:*");
-    try (Http2SolrClient client =
-        (Http2SolrClient)
+    try (HttpJettySolrClient client =
+        (HttpJettySolrClient)
             builder(getBaseUrl() + DEBUG_SERVLET_PATH, DEFAULT_CONNECTION_TIMEOUT, 0)
                 .withDefaultCollection(DEFAULT_CORE)
                 .build()) {
@@ -101,8 +109,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testRequestTimeout() throws Exception {
     SolrQuery q = new SolrQuery("*:*");
-    try (Http2SolrClient client =
-        (Http2SolrClient)
+    try (HttpJettySolrClient client =
+        (HttpJettySolrClient)
             builder(getBaseUrl() + SLOW_SERVLET_PATH, DEFAULT_CONNECTION_TIMEOUT, 0)
                 .withDefaultCollection(DEFAULT_CORE)
                 .withRequestTimeout(500, TimeUnit.MILLISECONDS)
@@ -120,8 +128,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
    */
   @Test
   public void testSolrExceptionCodeNotFromSolr() throws IOException, SolrServerException {
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .build()) {
       super.testSolrExceptionCodeNotFromSolr(client);
@@ -136,7 +144,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     final int status = 527;
     DebugServlet.setErrorCode(status);
 
-    try (Http2SolrClient client = new Http2SolrClient.Builder(null).build()) {
+    try (var client = new HttpJettySolrClient.Builder(null).build()) {
       try {
         // if client base url is null, request url will be used in exception message
         client.requestWithBaseUrl(getBaseUrl() + DEBUG_SERVLET_PATH, new SolrPing(), DEFAULT_CORE);
@@ -159,12 +167,11 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     q.setParam("a", MUST_ENCODE);
     q.setParam("case_sensitive_param", "lowercase");
     q.setParam("CASE_SENSITIVE_PARAM", "uppercase");
-    Http2SolrClient.Builder b =
-        new Http2SolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE);
+    var b = new HttpJettySolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE);
     if (rp != null) {
       b.withResponseParser(rp);
     }
-    try (Http2SolrClient client = b.build()) {
+    try (HttpJettySolrClient client = b.build()) {
       client.query(q, method);
       assertEquals(
           client.getParser().getWriterType(), DebugServlet.parameters.get(CommonParams.WT)[0]);
@@ -218,8 +225,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     queryParams.add("q", "*:*");
 
     // Ensure the correct URL is used by the lambda-based requestWithBaseUrl method
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(defaultUrl).withDefaultCollection(DEFAULT_CORE).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(defaultUrl).withDefaultCollection(DEFAULT_CORE).build()) {
       try {
         client.requestWithBaseUrl(urlToUse, (c) -> c.query(queryParams));
       } catch (SolrClient.RemoteSolrException rse) {
@@ -229,8 +236,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     }
 
     // Ensure the correct URL is used by the SolrRequest-based requestWithBaseUrl method
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(defaultUrl).withDefaultCollection(DEFAULT_CORE).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(defaultUrl).withDefaultCollection(DEFAULT_CORE).build()) {
       try {
         client.requestWithBaseUrl(urlToUse, new QueryRequest(queryParams), null);
       } catch (SolrClient.RemoteSolrException rse) {
@@ -244,8 +251,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   public void testDelete() throws Exception {
     DebugServlet.clear();
     String url = getBaseUrl() + DEBUG_SERVLET_PATH;
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE).build()) {
       try {
         client.deleteById("id");
       } catch (SolrClient.RemoteSolrException ignored) {
@@ -259,8 +266,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   public void testDeleteXml() throws Exception {
     DebugServlet.clear();
     String url = getBaseUrl() + "/debug/foo";
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(url)
+    try (var client =
+        new HttpJettySolrClient.Builder(url)
             .withDefaultCollection(DEFAULT_CORE)
             .withResponseParser(new XMLResponseParser())
             .build()) {
@@ -276,8 +283,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testGetById() throws Exception {
     DebugServlet.clear();
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .build()) {
       super.testGetById(client);
@@ -287,8 +294,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testUpdateDefault() throws Exception {
     String url = getBaseUrl() + DEBUG_SERVLET_PATH;
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE).build()) {
       testUpdate(client, WT.JAVABIN, "application/javabin", MUST_ENCODE);
     }
   }
@@ -296,8 +303,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testUpdateXml() throws Exception {
     String url = getBaseUrl() + "/debug/foo";
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(url)
+    try (var client =
+        new HttpJettySolrClient.Builder(url)
             .withDefaultCollection(DEFAULT_CORE)
             .withRequestWriter(new XMLRequestWriter())
             .withResponseParser(new XMLResponseParser())
@@ -309,8 +316,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testUpdateJavabin() throws Exception {
     String url = getBaseUrl() + "/debug/foo";
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(url)
+    try (var client =
+        new HttpJettySolrClient.Builder(url)
             .withDefaultCollection(DEFAULT_CORE)
             .withRequestWriter(new JavaBinRequestWriter())
             .withResponseParser(new JavaBinResponseParser())
@@ -345,8 +352,10 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     ResponseParser rp = new XMLResponseParser();
     final var queryParams = new MapSolrParams(Collections.singletonMap("q", "*:*"));
     final var builder =
-        new Http2SolrClient.Builder(url).withDefaultCollection(DEFAULT_CORE).withResponseParser(rp);
-    try (Http2SolrClient originalClient = builder.build()) {
+        new HttpJettySolrClient.Builder(url)
+            .withDefaultCollection(DEFAULT_CORE)
+            .withResponseParser(rp);
+    try (HttpJettySolrClient originalClient = builder.build()) {
       final var derivedBuilder = builder.withHttpClient(originalClient);
       super.testQueryAsync(derivedBuilder);
     }
@@ -355,8 +364,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testFollowRedirect() throws Exception {
     final String clientUrl = getBaseUrl() + REDIRECT_SERVLET_PATH;
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(clientUrl)
+    try (var client =
+        new HttpJettySolrClient.Builder(clientUrl)
             .withDefaultCollection(DEFAULT_CORE)
             .withFollowRedirects(true)
             .build()) {
@@ -368,8 +377,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testDoNotFollowRedirect() throws Exception {
     final String clientUrl = getBaseUrl() + REDIRECT_SERVLET_PATH;
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(clientUrl)
+    try (var client =
+        new HttpJettySolrClient.Builder(clientUrl)
             .withDefaultCollection(DEFAULT_CORE)
             .withFollowRedirects(false)
             .build()) {
@@ -386,15 +395,15 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     SolrQuery q = new SolrQuery("*:*");
 
     // default for follow redirects is false
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(clientUrl).withDefaultCollection(DEFAULT_CORE).build()) {
+    try (var client =
+        new HttpJettySolrClient.Builder(clientUrl).withDefaultCollection(DEFAULT_CORE).build()) {
 
       SolrServerException e = expectThrows(SolrServerException.class, () -> client.query(q));
       assertTrue(e.getMessage().contains("redirect"));
     }
 
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(clientUrl)
+    try (var client =
+        new HttpJettySolrClient.Builder(clientUrl)
             .withDefaultCollection(DEFAULT_CORE)
             .withFollowRedirects(true)
             .build()) {
@@ -403,8 +412,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     }
 
     // set explicit false for following redirects
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(clientUrl)
+    try (var client =
+        new HttpJettySolrClient.Builder(clientUrl)
             .withDefaultCollection(DEFAULT_CORE)
             .withFollowRedirects(false)
             .build()) {
@@ -416,8 +425,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testCollectionParameters() throws IOException, SolrServerException {
-    Http2SolrClient baseUrlClient = new Http2SolrClient.Builder(getBaseUrl()).build();
-    Http2SolrClient collection1UrlClient = new Http2SolrClient.Builder(getCoreUrl()).build();
+    var baseUrlClient = new HttpJettySolrClient.Builder(getBaseUrl()).build();
+    var collection1UrlClient = new HttpJettySolrClient.Builder(getCoreUrl()).build();
     testCollectionParameters(baseUrlClient, collection1UrlClient);
   }
 
@@ -431,11 +440,11 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   public void testGetDefaultSslContextFactory() {
     assertEquals(
         "HTTPS",
-        Http2SolrClient.getDefaultSslContextFactory().getEndpointIdentificationAlgorithm());
+        HttpJettySolrClient.getDefaultSslContextFactory().getEndpointIdentificationAlgorithm());
 
     System.setProperty("javax.net.ssl.keyStoreType", "foo");
     System.setProperty("javax.net.ssl.trustStoreType", "bar");
-    SslContextFactory.Client sslContextFactory = Http2SolrClient.getDefaultSslContextFactory();
+    SslContextFactory.Client sslContextFactory = HttpJettySolrClient.getDefaultSslContextFactory();
     assertEquals("HTTPS", sslContextFactory.getEndpointIdentificationAlgorithm());
     assertEquals("foo", sslContextFactory.getKeyStoreType());
     assertEquals("bar", sslContextFactory.getTrustStoreType());
@@ -445,7 +454,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     System.setProperty("solr.ssl.check.peer.name.enabled", "true");
     System.setProperty("javax.net.ssl.keyStoreType", "foo");
     System.setProperty("javax.net.ssl.trustStoreType", "bar");
-    SslContextFactory.Client sslContextFactory2 = Http2SolrClient.getDefaultSslContextFactory();
+    SslContextFactory.Client sslContextFactory2 = HttpJettySolrClient.getDefaultSslContextFactory();
     assertEquals("HTTPS", sslContextFactory2.getEndpointIdentificationAlgorithm());
     assertEquals("foo", sslContextFactory2.getKeyStoreType());
     assertEquals("bar", sslContextFactory2.getTrustStoreType());
@@ -456,7 +465,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     System.setProperty("solr.ssl.check.peer.name.enabled", "false");
     System.setProperty("javax.net.ssl.keyStoreType", "foo");
     System.setProperty("javax.net.ssl.trustStoreType", "bar");
-    SslContextFactory.Client sslContextFactory3 = Http2SolrClient.getDefaultSslContextFactory();
+    SslContextFactory.Client sslContextFactory3 = HttpJettySolrClient.getDefaultSslContextFactory();
     assertNull(sslContextFactory3.getEndpointIdentificationAlgorithm());
     assertEquals("foo", sslContextFactory3.getKeyStoreType());
     assertEquals("bar", sslContextFactory3.getTrustStoreType());
@@ -477,8 +486,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testSetCredentialsExplicitly() {
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .withBasicAuthCredentials("foo", "explicit")
             .build(); ) {
@@ -496,8 +505,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     // Hack to ensure we get a new set of parameters for this test
     PreemptiveBasicAuthClientBuilderFactory.setDefaultSolrParams(
         new PreemptiveBasicAuthClientBuilderFactory.CredentialsResolver().defaultParams);
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .build()) {
       QueryRequest r = new QueryRequest(new SolrQuery("quick brown fox"));
@@ -526,8 +535,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testPerRequestCredentials() {
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .withBasicAuthCredentials("foo2", "explicit")
             .build(); ) {
@@ -537,8 +546,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testNoCredentials() {
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .build(); ) {
       super.testNoCredentials(client);
@@ -548,8 +557,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testUseOptionalCredentials() {
     // username foo, password with embedded colon separator is "expli:cit".
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .withOptionalBasicAuthCredentials("foo:expli:cit")
             .build(); ) {
@@ -559,8 +568,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testUseOptionalCredentialsWithNull() {
-    try (Http2SolrClient client =
-        new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+    try (var client =
+        new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
             .withDefaultCollection(DEFAULT_CORE)
             .withOptionalBasicAuthCredentials(null)
             .build(); ) {
@@ -573,12 +582,14 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
     expectThrowsAndMessage(
         IllegalStateException.class,
-        () -> new Http2SolrClient.Builder().withOptionalBasicAuthCredentials("usernamepassword"),
+        () ->
+            new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials("usernamepassword"),
         "Invalid Authentication credential formatting. Provide username and password in the 'username:password' format.");
 
     expectThrowsAndMessage(
         IllegalStateException.class,
-        () -> new Http2SolrClient.Builder().withOptionalBasicAuthCredentials("username password"),
+        () ->
+            new HttpJettySolrClient.Builder().withOptionalBasicAuthCredentials("username password"),
         "Invalid Authentication credential formatting. Provide username and password in the 'username:password' format.");
   }
 
@@ -586,11 +597,11 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   public void testBadExplicitCredentials() {
     expectThrowsAndMessage(
         IllegalStateException.class,
-        () -> new Http2SolrClient.Builder().withBasicAuthCredentials("foo", null),
+        () -> new HttpJettySolrClient.Builder().withBasicAuthCredentials("foo", null),
         "Invalid Authentication credentials");
     expectThrowsAndMessage(
         IllegalStateException.class,
-        () -> new Http2SolrClient.Builder().withBasicAuthCredentials(null, "foo"),
+        () -> new HttpJettySolrClient.Builder().withBasicAuthCredentials(null, "foo"),
         "Invalid Authentication credentials");
   }
 
@@ -599,7 +610,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     System.setProperty(SolrHttpConstants.SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY, "FakeClassName");
     try {
       SolrClient client =
-          new Http2SolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
+          new HttpJettySolrClient.Builder(getBaseUrl() + DEBUG_SERVLET_PATH)
               .withDefaultCollection(DEFAULT_CORE)
               .build();
       fail("Expecting exception");
@@ -610,8 +621,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testGetRawStream() throws Exception {
-    try (Http2SolrClient client =
-        (Http2SolrClient)
+    try (HttpJettySolrClient client =
+        (HttpJettySolrClient)
             builder(
                     getBaseUrl() + DEBUG_SERVLET_PATH,
                     DEFAULT_CONNECTION_TIMEOUT,
@@ -624,38 +635,38 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
   @Test
   public void testBuilder() {
-    try (Http2SolrClient seed =
-        new Http2SolrClient.Builder("baseSolrUrl")
+    try (var seed =
+        new HttpJettySolrClient.Builder("baseSolrUrl")
             .withBasicAuthCredentials("testu", "testp")
             .build()) {
 
-      try (Http2SolrClient clone1 =
-          new Http2SolrClient.Builder("baseSolrUrl").withHttpClient(seed).build()) {
+      try (var clone1 =
+          new HttpJettySolrClient.Builder("baseSolrUrl").withHttpClient(seed).build()) {
         String expected1 =
-            Http2SolrClient.basicAuthCredentialsToAuthorizationString("testu", "testp");
-        assertEquals(expected1, clone1.basicAuthAuthorizationStr);
+            HttpJettySolrClient.basicAuthCredentialsToAuthorizationString("testu", "testp");
+        assertEquals(expected1, clone1.basicAuthAuthorizationStr());
       }
 
       // test overwrite seed value
-      try (Http2SolrClient clone2 =
-          new Http2SolrClient.Builder("baseSolrUrl")
+      try (var clone2 =
+          new HttpJettySolrClient.Builder("baseSolrUrl")
               .withHttpClient(seed)
               .withBasicAuthCredentials("testu2", "testp2")
               .build()) {
         String expected2 =
-            Http2SolrClient.basicAuthCredentialsToAuthorizationString("testu2", "testp2");
-        assertEquals(expected2, clone2.basicAuthAuthorizationStr);
+            HttpJettySolrClient.basicAuthCredentialsToAuthorizationString("testu2", "testp2");
+        assertEquals(expected2, clone2.basicAuthAuthorizationStr());
       }
 
       // test overwrite seed value, order of builder method calls reversed
-      try (Http2SolrClient clone3 =
-          new Http2SolrClient.Builder("baseSolrUrl")
+      try (var clone3 =
+          new HttpJettySolrClient.Builder("baseSolrUrl")
               .withBasicAuthCredentials("testu3", "testp3")
               .withHttpClient(seed)
               .build()) {
         String expected3 =
-            Http2SolrClient.basicAuthCredentialsToAuthorizationString("testu3", "testp3");
-        assertEquals(expected3, clone3.basicAuthAuthorizationStr);
+            HttpJettySolrClient.basicAuthCredentialsToAuthorizationString("testu3", "testp3");
+        assertEquals(expected3, clone3.basicAuthAuthorizationStr());
       }
     }
   }
@@ -663,14 +674,14 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testIdleTimeoutWithHttpClient() throws Exception {
     String url = getBaseUrl() + SLOW_STREAM_SERVLET_PATH;
-    try (Http2SolrClient oldClient =
-        new Http2SolrClient.Builder(url)
+    try (var oldClient =
+        new HttpJettySolrClient.Builder(url)
             .withRequestTimeout(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
             .withIdleTimeout(100, TimeUnit.MILLISECONDS)
             .build()) {
 
-      try (Http2SolrClient onlyBaseUrlChangedClient =
-          new Http2SolrClient.Builder(url).withHttpClient(oldClient).build()) {
+      try (var onlyBaseUrlChangedClient =
+          new HttpJettySolrClient.Builder(url).withHttpClient(oldClient).build()) {
         assertEquals(
             oldClient.getIdleTimeoutMillis(), onlyBaseUrlChangedClient.getIdleTimeoutMillis());
         assertEquals(oldClient.getHttpClient(), onlyBaseUrlChangedClient.getHttpClient());
@@ -684,8 +695,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
       assertIsTimeout(expectThrows(SolrServerException.class, () -> oldClient.request(req)));
 
       long newIdleTimeoutMs = timeToSendMs + 1000; // enough time to succeed
-      try (Http2SolrClient idleTimeoutChangedClient =
-          new Http2SolrClient.Builder(url)
+      try (var idleTimeoutChangedClient =
+          new HttpJettySolrClient.Builder(url)
               .withHttpClient(oldClient)
               .withIdleTimeout(newIdleTimeoutMs, TimeUnit.MILLISECONDS)
               .build()) {
@@ -705,14 +716,14 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
   @Test
   public void testRequestTimeoutWithHttpClient() throws Exception {
     String url = getBaseUrl() + SLOW_STREAM_SERVLET_PATH;
-    try (Http2SolrClient oldClient =
-        new Http2SolrClient.Builder(url)
+    try (var oldClient =
+        new HttpJettySolrClient.Builder(url)
             .withIdleTimeout(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
             .withRequestTimeout(100, TimeUnit.MILLISECONDS)
             .build()) {
 
-      try (Http2SolrClient onlyBaseUrlChangedClient =
-          new Http2SolrClient.Builder(url).withHttpClient(oldClient).build()) {
+      try (var onlyBaseUrlChangedClient =
+          new HttpJettySolrClient.Builder(url).withHttpClient(oldClient).build()) {
         // Client created with the same HTTP client should have the same behavior
         assertEquals(oldClient.getHttpClient(), onlyBaseUrlChangedClient.getHttpClient());
       }
@@ -723,8 +734,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
       assertIsTimeout(expectThrows(SolrServerException.class, () -> oldClient.request(req)));
 
       int newRequestTimeoutMs = 10 * 1000; // enough time to succeed
-      try (Http2SolrClient requestTimeoutChangedClient =
-          new Http2SolrClient.Builder(url)
+      try (var requestTimeoutChangedClient =
+          new HttpJettySolrClient.Builder(url)
               .withHttpClient(oldClient)
               .withRequestTimeout(newRequestTimeoutMs, TimeUnit.MILLISECONDS)
               .build()) {
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactoryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/PreemptiveBasicAuthClientBuilderFactoryTest.java
similarity index 99%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactoryTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/jetty/PreemptiveBasicAuthClientBuilderFactoryTest.java
index d50ede6ba57..3ee8f4dcc78 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/PreemptiveBasicAuthClientBuilderFactoryTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/jetty/PreemptiveBasicAuthClientBuilderFactoryTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.jetty;
 
 import java.io.BufferedWriter;
 import java.io.IOException;
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
index da99074d3a8..0668215e3ea 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
@@ -84,9 +84,9 @@ import org.apache.lucene.util.Constants;
 import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
 import org.apache.solr.client.solrj.apache.HttpClientUtil;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.ClusterStateProvider;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.response.SolrResponseBase;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.cloud.IpTables;
@@ -290,7 +290,7 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
     // based on randomized SSL config, set SocketFactoryRegistryProvider appropriately
     HttpClientUtil.setSocketFactoryRegistryProvider(
         sslConfig.buildClientSocketFactoryRegistryProvider());
-    Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
+    HttpJettySolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig());
     if (isSSLMode()) {
       // SolrCloud tests should usually clear this
       System.setProperty(URL_SCHEME, HTTPS);
@@ -331,7 +331,7 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
       System.clearProperty("solr.cloud.wait-for-updates-with-stale-state-pause");
       System.clearProperty("solr.zkclienttmeout");
       HttpClientUtil.resetHttpClientBuilder();
-      Http2SolrClient.resetSslContextFactory();
+      HttpJettySolrClient.resetSslContextFactory();
 
       clearNumericTypesProperties();
 
@@ -2526,10 +2526,10 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
   }
 
   /**
-   * A variant of {@link org.apache.solr.client.solrj.impl.CloudHttp2SolrClient.Builder} that will
+   * A variant of {@link org.apache.solr.client.solrj.impl.CloudSolrClient.Builder} that will
    * randomize some internal settings.
    */
-  public static class RandomizingCloudHttp2SolrClientBuilder extends CloudHttp2SolrClient.Builder {
+  public static class RandomizingCloudHttp2SolrClientBuilder extends CloudSolrClient.Builder {
 
     public RandomizingCloudHttp2SolrClientBuilder(List<String> zkHosts, Optional<String> zkChroot) {
       super(zkHosts, zkChroot);
@@ -2627,7 +2627,8 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
 
   /**
    * This method creates a basic HttpSolrClient. Tests that want to control the creation process
-   * should use the {@link org.apache.solr.client.solrj.impl.Http2SolrClient.Builder} class directly
+   * should use the {@link org.apache.solr.client.solrj.jetty.HttpJettySolrClient.Builder} class
+   * directly
    *
    * @param url the base URL for a Solr node. Should not contain a core or collection name.
    */
@@ -2642,7 +2643,8 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
 
   /**
    * This method creates a basic HttpSolrClient. Tests that want to control the creation process
-   * should use the {@link org.apache.solr.client.solrj.impl.Http2SolrClient.Builder} class directly
+   * should use the {@link org.apache.solr.client.solrj.jetty.HttpJettySolrClient.Builder} class
+   * directly
    *
    * @param url the base URL of a Solr node. Should <em>not</em> include a collection or core name.
    * @param defaultCoreName the name of a core that the created client should default to when making
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpClusterStateProvider.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpClusterStateProvider.java
index 7c715ba23f6..9cbc41899f4 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpClusterStateProvider.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpClusterStateProvider.java
@@ -21,12 +21,11 @@ import java.util.List;
 import org.apache.http.client.HttpClient;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.BaseHttpClusterStateProvider;
-import org.apache.solr.client.solrj.impl.Http2ClusterStateProvider;
 
 /**
  * Retrieves cluster state via Solr HTTP APIs
  *
- * @deprecated Please use {@link Http2ClusterStateProvider}
+ * @deprecated Please use {@link org.apache.solr.client.solrj.impl.HttpClusterStateProvider}
  */
 @Deprecated(since = "9.0")
 public class HttpClusterStateProvider extends BaseHttpClusterStateProvider {
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
index ec2f3d2844c..58d4fa63a74 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/HttpSolrClient.java
@@ -76,12 +76,12 @@ 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.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.HttpJdkSolrClient;
 import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
 import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
 import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
 import org.apache.solr.client.solrj.impl.RemoteExecutionException;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.common.SolrException;
@@ -98,7 +98,7 @@ import org.slf4j.MDC;
 /**
  * A SolrClient implementation that talks directly to a Solr server via Apache HTTP client
  *
- * @deprecated Please use {@link Http2SolrClient} or {@link HttpJdkSolrClient}
+ * @deprecated Please use {@link HttpJettySolrClient} or {@link HttpJdkSolrClient}
  */
 @Deprecated(since = "9.0")
 public class HttpSolrClient extends SolrClient {
@@ -809,7 +809,7 @@ public class HttpSolrClient extends SolrClient {
   /**
    * Constructs {@link HttpSolrClient} instances from provided configuration.
    *
-   * @deprecated Please use {@link Http2SolrClient}
+   * @deprecated Please use {@link HttpJettySolrClient}
    */
   @Deprecated(since = "9.0")
   public static class Builder extends SolrClientBuilder<Builder> {
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
index 060902f2b07..cbac9ba4c69 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
@@ -2153,7 +2153,7 @@ public abstract class AbstractFullDistribZkTestBase extends AbstractDistribZkTes
   /**
    * This method <i>may</i> randomize unspecified aspects of the resulting SolrClient. Tests that do
    * not wish to have any randomized behavior should use the {@link
-   * org.apache.solr.client.solrj.impl.CloudHttp2SolrClient.Builder} class directly
+   * org.apache.solr.client.solrj.impl.CloudSolrClient.Builder} class directly
    */
   public static CloudSolrClient getCloudSolrClient(
       String zkHost,
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index 8883aca1ef3..a632928d361 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -57,8 +57,8 @@ import java.util.function.Consumer;
 import org.apache.lucene.tests.util.LuceneTestCase;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
-import org.apache.solr.client.solrj.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.jetty.SSLConfig;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
 import org.apache.solr.common.SolrException;
diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java
index 6ec7e5d1ca7..0f8cfb28238 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java
@@ -21,7 +21,7 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.TreeMap;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.embedded.SSLConfig;
+import org.apache.solr.client.solrj.jetty.SSLConfig;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 
 public class JettyConfig {
diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
index 8bc00fec346..2e7e0f4ae07 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
@@ -55,7 +55,7 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.cloud.SocketProxy;
-import org.apache.solr.client.solrj.embedded.SSLConfig;
+import org.apache.solr.client.solrj.jetty.SSLConfig;
 import org.apache.solr.client.solrj.request.CoresApi;
 import org.apache.solr.common.util.TimeSource;
 import org.apache.solr.common.util.Utils;
diff --git a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
index 74e4fdf6f39..78be26e2e34 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/SSLTestConfig.java
@@ -37,8 +37,8 @@ import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
 import org.apache.http.ssl.SSLContextBuilder;
 import org.apache.http.ssl.SSLContexts;
 import org.apache.solr.client.solrj.apache.HttpClientUtil.SocketFactoryRegistryProvider;
-import org.apache.solr.client.solrj.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.SSLConfig;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.resource.ResourceFactory;
 import org.eclipse.jetty.util.security.CertificateUtils;
diff --git a/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
index ccff28c55c9..5e8f7a9d7a8 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
@@ -23,8 +23,8 @@ import java.nio.charset.StandardCharsets;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
@@ -56,7 +56,7 @@ public final class SolrJMetricTestUtils {
       String baseUrl, String collectionName, String category, String handler)
       throws SolrServerException, IOException {
 
-    try (Http2SolrClient client = new Http2SolrClient.Builder(baseUrl).build()) {
+    try (var client = new HttpJettySolrClient.Builder(baseUrl).build()) {
       var req =
           new GenericSolrRequest(
               SolrRequest.METHOD.GET,
@@ -89,7 +89,7 @@ public final class SolrJMetricTestUtils {
   public static Double getNumNodeRequestErrors(String baseUrl, String category, String handler)
       throws SolrServerException, IOException {
 
-    try (Http2SolrClient client = new Http2SolrClient.Builder(baseUrl).build()) {
+    try (var client = new HttpJettySolrClient.Builder(baseUrl).build()) {
       var req =
           new GenericSolrRequest(
               SolrRequest.METHOD.GET,
diff --git a/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
index 280add47de7..9ff91933e94 100644
--- a/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCase;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.junit.Test;
@@ -37,8 +36,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
   @Test
   public void testSingleZkHostSpecified() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build(); ) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
@@ -54,7 +52,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
     zkHostList.add(ANY_ZK_HOST);
     zkHostList.add(ANY_OTHER_ZK_HOST);
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(zkHostList, Optional.of(ANY_CHROOT)).build()) {
+        new CloudSolrClient.Builder(zkHostList, Optional.of(ANY_CHROOT)).build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
         final String clientZkHost = zkClientClusterStateProvider.getZkHost();
@@ -70,7 +68,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
     zkHosts.add(ANY_ZK_HOST);
     zkHosts.add(ANY_OTHER_ZK_HOST);
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(zkHosts, Optional.of(ANY_CHROOT)).build()) {
+        new CloudSolrClient.Builder(zkHosts, Optional.of(ANY_CHROOT)).build()) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(createdClient)) {
         final String clientZkHost = zkClientClusterStateProvider.getZkHost();
@@ -83,8 +81,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
   @Test
   public void testByDefaultConfiguresClientToSendUpdatesOnlyToShardLeaders() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       assertTrue(createdClient.isUpdatesToLeaders());
     }
@@ -93,8 +90,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
   @Test
   public void testIsDirectUpdatesToLeadersOnlyDefault() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
-                Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
+        new CloudSolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       assertFalse(createdClient.isDirectUpdatesToLeadersOnly());
     }
diff --git a/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
index 6d8f3ef8a61..5ee728580b8 100644
--- a/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Optional;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.SolrTestCase;
-import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.junit.Test;
@@ -72,7 +71,7 @@ public class CloudSolrClientMultiConstructorTest extends SolrTestCase {
     }
 
     try (CloudSolrClient client =
-        (new CloudHttp2SolrClient.Builder(new ArrayList<>(hosts), Optional.ofNullable(clientChroot))
+        (new CloudSolrClient.Builder(new ArrayList<>(hosts), Optional.ofNullable(clientChroot))
             .build())) {
       try (ZkClientClusterStateProvider zkClientClusterStateProvider =
           ZkClientClusterStateProvider.from(client)) {
@@ -116,6 +115,6 @@ public class CloudSolrClientMultiConstructorTest extends SolrTestCase {
   public void testBadChroot() {
     final List<String> zkHosts = new ArrayList<>();
     zkHosts.add("host1:2181");
-    new CloudHttp2SolrClient.Builder(zkHosts, Optional.of("foo")).build();
+    new CloudSolrClient.Builder(zkHosts, Optional.of("foo")).build();
   }
 }


(solr) 01/12: Tests: ServletFixtures (#3877)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 221879461d07c9d9cfa8ffbf634efe7c5659f383
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Nov 20 09:11:39 2025 -0500

    Tests: ServletFixtures (#3877)
    
    Deduplicate DebugServlet into one; resolve some semantic differences regarding case sensitivity of headers.  Put several test servlets under a ServletFixtures class.
---
 .../client/solrj/impl/BasicHttpSolrClientTest.java | 225 ++++----------------
 .../solr/client/solrj/impl/DebugServlet.java       | 180 ----------------
 .../impl/Http2SolrClientCompatibilityTest.java     |   1 +
 .../client/solrj/impl/Http2SolrClientTest.java     |   3 +-
 .../client/solrj/impl/HttpJdkSolrClientTest.java   |   1 +
 .../client/solrj/impl/HttpSolrClientTestBase.java  |  15 +-
 .../solr/client/solrj/impl/ServletFixtures.java    | 228 +++++++++++++++++++++
 7 files changed, 281 insertions(+), 372 deletions(-)

diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
index 601c2442c10..ef372e93e50 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
@@ -16,26 +16,17 @@
  */
 package org.apache.solr.client.solrj.impl;
 
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.invoke.MethodHandles;
 import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
 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.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.IntStream;
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpException;
@@ -64,6 +55,10 @@ import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.HttpClientUtil;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.apache.SolrPortAwareCookieSpecFactory;
+import org.apache.solr.client.solrj.impl.ServletFixtures.DebugServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.RedirectServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.SlowServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.SlowStreamServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -73,7 +68,6 @@ 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.util.NamedList;
-import org.apache.solr.common.util.SuppressForbidden;
 import org.apache.solr.embedded.JettyConfig;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 import org.junit.BeforeClass;
@@ -86,141 +80,6 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private static final String UA_VERSION = SolrVersion.LATEST_STRING;
 
-  public static class RedirectServlet extends HttpServlet {
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      resp.sendRedirect("/solr/collection1/select?" + req.getQueryString());
-    }
-  }
-
-  public static class SlowServlet extends HttpServlet {
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      try {
-        Thread.sleep(5000);
-      } catch (InterruptedException ignored) {
-      }
-    }
-  }
-
-  public static class SlowStreamServlet extends HttpServlet {
-
-    public static final int PACKET_MS = 500;
-
-    @Override
-    @SuppressForbidden(reason = "don't forbid in tests")
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-      String countStr = req.getParameter("count");
-      IntStream.range(0, countStr == null ? 10 : Integer.parseInt(countStr))
-          .forEach(
-              i -> {
-                try {
-                  Thread.sleep(PACKET_MS);
-                  resp.getOutputStream().write(String.valueOf(i).getBytes(StandardCharsets.UTF_8));
-                  resp.getOutputStream().flush();
-                } catch (IOException | InterruptedException e) {
-                  throw new RuntimeException(e);
-                }
-              });
-    }
-  }
-
-  public static class DebugServlet extends HttpServlet {
-    public static void clear() {
-      lastMethod = null;
-      headers = null;
-      parameters = null;
-      errorCode = null;
-      queryString = null;
-      cookies = null;
-    }
-
-    public static Integer errorCode = null;
-    public static String lastMethod = null;
-    public static HashMap<String, String> headers = null;
-    public static Map<String, String[]> parameters = null;
-    public static String queryString = null;
-    public static jakarta.servlet.http.Cookie[] cookies = null;
-
-    public static void setErrorCode(Integer code) {
-      errorCode = code;
-    }
-
-    @Override
-    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      lastMethod = "delete";
-      recordRequest(req, resp);
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      lastMethod = "get";
-      recordRequest(req, resp);
-    }
-
-    @Override
-    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      lastMethod = "head";
-      recordRequest(req, resp);
-    }
-
-    private void setHeaders(HttpServletRequest req) {
-      Enumeration<String> headerNames = req.getHeaderNames();
-      headers = new HashMap<>();
-      while (headerNames.hasMoreElements()) {
-        final String name = headerNames.nextElement();
-        headers.put(name, req.getHeader(name));
-      }
-    }
-
-    @SuppressForbidden(reason = "fake servlet only")
-    private void setParameters(HttpServletRequest req) {
-      parameters = req.getParameterMap();
-    }
-
-    private void setQueryString(HttpServletRequest req) {
-      queryString = req.getQueryString();
-    }
-
-    private void setCookies(HttpServletRequest req) {
-      jakarta.servlet.http.Cookie[] ck = req.getCookies();
-      cookies = req.getCookies();
-    }
-
-    @Override
-    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      lastMethod = "post";
-      recordRequest(req, resp);
-    }
-
-    @Override
-    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException {
-      lastMethod = "put";
-      recordRequest(req, resp);
-    }
-
-    private void recordRequest(HttpServletRequest req, HttpServletResponse resp) {
-      setHeaders(req);
-      setParameters(req);
-      setQueryString(req);
-      setCookies(req);
-      if (null != errorCode) {
-        try {
-          resp.sendError(errorCode);
-        } catch (IOException e) {
-          throw new RuntimeException("sendError IO fail in DebugServlet", e);
-        }
-      }
-    }
-  }
-
   @BeforeClass
   public static void beforeTest() throws Exception {
     JettyConfig jettyConfig =
@@ -290,18 +149,18 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       // agent
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       // default wt
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
       // agent
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       // keepalive
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
       // content-type
-      assertNull(DebugServlet.headers.get("Content-Type"));
+      assertNull(DebugServlet.headers.get("content-type"));
       // param encoding
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
@@ -314,18 +173,18 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("post", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
       assertEquals(
           "application/x-www-form-urlencoded; charset=UTF-8",
-          DebugServlet.headers.get("Content-Type"));
+          DebugServlet.headers.get("content-type"));
 
       // PUT
       DebugServlet.clear();
@@ -335,18 +194,18 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("put", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
       assertEquals(
           "application/x-www-form-urlencoded; charset=UTF-8",
-          DebugServlet.headers.get("Content-Type"));
+          DebugServlet.headers.get("content-type"));
     }
 
     // XML
@@ -362,15 +221,15 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("get", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("xml", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
 
       // XML/POST
       DebugServlet.clear();
@@ -380,18 +239,18 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("post", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("xml", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
       assertEquals(
           "application/x-www-form-urlencoded; charset=UTF-8",
-          DebugServlet.headers.get("Content-Type"));
+          DebugServlet.headers.get("content-type"));
 
       DebugServlet.clear();
       queryRequest.setMethod(METHOD.PUT);
@@ -400,18 +259,18 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("put", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("xml", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
       assertEquals(
           "application/x-www-form-urlencoded; charset=UTF-8",
-          DebugServlet.headers.get("Content-Type"));
+          DebugServlet.headers.get("content-type"));
     }
   }
 
@@ -431,16 +290,16 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       // agent
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       // default wt
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
       // agent
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       // keepalive
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
     }
 
     // XML
@@ -458,13 +317,13 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("post", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("xml", DebugServlet.parameters.get(CommonParams.WT)[0]);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
-      assertEquals("keep-alive", DebugServlet.headers.get("Connection"));
+          DebugServlet.headers.get("user-agent"));
+      assertEquals("keep-alive", DebugServlet.headers.get("connection"));
     }
   }
 
@@ -497,12 +356,12 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       // agent
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       // default wt
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
       // content type
-      assertEquals("application/javabin", DebugServlet.headers.get("Content-Type"));
+      assertEquals("application/javabin", DebugServlet.headers.get("content-type"));
       // parameter encoding
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
@@ -524,10 +383,10 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("post", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("xml", DebugServlet.parameters.get(CommonParams.WT)[0]);
-      assertEquals("application/xml; charset=UTF-8", DebugServlet.headers.get("Content-Type"));
+      assertEquals("application/xml; charset=UTF-8", DebugServlet.headers.get("content-type"));
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
     }
@@ -548,10 +407,10 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       assertEquals("post", DebugServlet.lastMethod);
       assertEquals(
           "Solr[" + HttpSolrClient.class.getName() + "] " + UA_VERSION,
-          DebugServlet.headers.get("User-Agent"));
+          DebugServlet.headers.get("user-agent"));
       assertEquals(1, DebugServlet.parameters.get(CommonParams.WT).length);
       assertEquals("javabin", DebugServlet.parameters.get(CommonParams.WT)[0]);
-      assertEquals("application/javabin", DebugServlet.headers.get("Content-Type"));
+      assertEquals("application/javabin", DebugServlet.headers.get("content-type"));
       assertEquals(1, DebugServlet.parameters.get("a").length);
       assertEquals("\u1234", DebugServlet.parameters.get("a")[0]);
     }
@@ -597,7 +456,7 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       // verify request header gets set
       DebugServlet.clear();
       expectThrows(SolrClient.RemoteSolrException.class, () -> queryRequest.process(client));
-      assertNull(DebugServlet.headers.toString(), DebugServlet.headers.get("Accept-Encoding"));
+      assertNull(DebugServlet.headers.toString(), DebugServlet.headers.get("accept-encoding"));
     }
 
     try (SolrClient client =
@@ -606,7 +465,7 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
         queryRequest.process(client);
       } catch (SolrClient.RemoteSolrException ignored) {
       }
-      assertNotNull(DebugServlet.headers.get("Accept-Encoding"));
+      assertNotNull(DebugServlet.headers.get("accept-encoding"));
     }
 
     try (SolrClient client =
@@ -616,7 +475,7 @@ public class BasicHttpSolrClientTest extends SolrJettyTestBase {
       } catch (SolrClient.RemoteSolrException ignored) {
       }
     }
-    assertNull(DebugServlet.headers.get("Accept-Encoding"));
+    assertNull(DebugServlet.headers.get("accept-encoding"));
 
     // verify server compresses output
     HttpGet get = new HttpGet(getCoreUrl() + "/select?q=foo&wt=xml");
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java
deleted file mode 100644
index dee9b4a2549..00000000000
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/DebugServlet.java
+++ /dev/null
@@ -1,180 +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.client.solrj.impl;
-
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.Cookie;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.solr.common.util.SuppressForbidden;
-
-public class DebugServlet extends HttpServlet {
-  public static void clear() {
-    lastMethod = null;
-    url = null;
-    headers = null;
-    parameters = null;
-    errorCode = null;
-    queryString = null;
-    cookies = null;
-    responseHeaders = null;
-    responseBodyByQueryFragment = new ConcurrentHashMap<>();
-  }
-
-  public static Integer errorCode = null;
-  public static String lastMethod = null;
-  public static String url = null;
-  public static HashMap<String, String> headers = null;
-  public static Map<String, String[]> parameters = null;
-  public static String queryString = null;
-  public static Cookie[] cookies = null;
-  public static List<String[]> responseHeaders = null;
-  public static Map<String, Object> responseBodyByQueryFragment = new ConcurrentHashMap<>();
-  public static byte[] requestBody = null;
-
-  public static void setErrorCode(Integer code) {
-    errorCode = code;
-  }
-
-  public static void addResponseHeader(String headerName, String headerValue) {
-    if (responseHeaders == null) {
-      responseHeaders = new ArrayList<>();
-    }
-    responseHeaders.add(new String[] {headerName, headerValue});
-  }
-
-  @Override
-  protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
-      throws ServletException, IOException {
-    lastMethod = "delete";
-    recordRequest(req, resp);
-  }
-
-  @Override
-  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-      throws ServletException, IOException {
-    lastMethod = "get";
-    recordRequest(req, resp);
-  }
-
-  @Override
-  protected void doHead(HttpServletRequest req, HttpServletResponse resp)
-      throws ServletException, IOException {
-    lastMethod = "head";
-    recordRequest(req, resp);
-  }
-
-  private void setHeaders(HttpServletRequest req) {
-    Enumeration<String> headerNames = req.getHeaderNames();
-    headers = new HashMap<>();
-    while (headerNames.hasMoreElements()) {
-      final String name = headerNames.nextElement();
-      headers.put(name.toLowerCase(Locale.getDefault()), req.getHeader(name));
-    }
-  }
-
-  @SuppressForbidden(reason = "fake servlet only")
-  private void setParameters(HttpServletRequest req) {
-    parameters = req.getParameterMap();
-  }
-
-  private void setQueryString(HttpServletRequest req) {
-    queryString = req.getQueryString();
-  }
-
-  private void setCookies(HttpServletRequest req) {
-    Cookie[] ck = req.getCookies();
-    cookies = req.getCookies();
-  }
-
-  @Override
-  protected void doPost(HttpServletRequest req, HttpServletResponse resp)
-      throws ServletException, IOException {
-    lastMethod = "post";
-    recordRequest(req, resp);
-  }
-
-  @Override
-  protected void doPut(HttpServletRequest req, HttpServletResponse resp)
-      throws ServletException, IOException {
-    lastMethod = "put";
-    recordRequest(req, resp);
-  }
-
-  @SuppressForbidden(reason = "tests needn't comply")
-  private void recordRequest(HttpServletRequest req, HttpServletResponse resp) {
-    url = req.getRequestURL().toString();
-    setHeaders(req);
-    setParameters(req);
-    setQueryString(req);
-    setCookies(req);
-    try {
-      requestBody = req.getInputStream().readAllBytes();
-    } catch (Exception e) {
-      // ignore
-    }
-    if (responseHeaders != null) {
-      for (String[] h : responseHeaders) {
-        resp.addHeader(h[0], h[1]);
-      }
-    }
-    String qs = req.getQueryString();
-    qs = qs == null ? "" : qs;
-    Object responseBody = null;
-
-    // Tests can set this up to return different response bodies based on substrings in the query
-    // string
-    for (Map.Entry<String, Object> entry : responseBodyByQueryFragment.entrySet()) {
-      if (qs.contains(entry.getKey())) {
-        responseBody = entry.getValue();
-        break;
-      }
-    }
-
-    if (responseBody != null) {
-      try {
-        if (responseBody instanceof String) {
-          resp.getWriter().print((String) responseBody);
-        } else if (responseBody instanceof byte[]) {
-          resp.getOutputStream().write((byte[]) responseBody);
-        } else {
-          throw new IllegalArgumentException(
-              "Only String and byte[] are supported for responseBody.");
-        }
-      } catch (IOException ioe) {
-        throw new RuntimeException(ioe);
-      }
-    }
-    if (null != errorCode) {
-      try {
-        resp.sendError(errorCode);
-      } catch (IOException e) {
-        throw new RuntimeException("sendError IO fail in DebugServlet", e);
-      }
-    }
-  }
-}
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
index 3763e251da6..e58a016f9b6 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
@@ -23,6 +23,7 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.embedded.JettyConfig;
 import org.apache.solr.util.LogLevel;
 import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index 92d089f5ad2..b05da93f3f3 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -35,6 +35,7 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.SolrPing;
 import org.apache.solr.common.SolrException;
@@ -675,7 +676,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
       // too little time to succeed
       int packets = LuceneTestCase.RANDOM_MULTIPLIER == 1 ? 10 : 80; // 60 crosses a default timeout
-      long timeToSendMs = (long) packets * BasicHttpSolrClientTest.SlowStreamServlet.PACKET_MS;
+      long timeToSendMs = (long) packets * ServletFixtures.SlowStreamServlet.PACKET_MS;
       QueryRequest req = new QueryRequest(SolrParams.of("count", "" + packets));
       req.setResponseParser(new InputStreamResponseParser(FILE_STREAM));
       assertIsTimeout(expectThrows(SolrServerException.class, () -> oldClient.request(req)));
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
index 8616cca08e9..b29be52e5f2 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
@@ -37,6 +37,7 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.SolrPingResponse;
 import org.apache.solr.common.params.CommonParams;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
index 79266ac9024..6a870b2b58e 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
@@ -42,6 +42,10 @@ 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.apache.HttpClientUtil;
+import org.apache.solr.client.solrj.impl.ServletFixtures.DebugServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.RedirectServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.SlowServlet;
+import org.apache.solr.client.solrj.impl.ServletFixtures.SlowStreamServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -74,15 +78,10 @@ public abstract class HttpSolrClientTestBase extends SolrJettyTestBase {
   public static void beforeTest() throws Exception {
     JettyConfig jettyConfig =
         JettyConfig.builder()
-            .withServlet(
-                new ServletHolder(BasicHttpSolrClientTest.RedirectServlet.class),
-                REDIRECT_SERVLET_REGEX)
-            .withServlet(
-                new ServletHolder(BasicHttpSolrClientTest.SlowServlet.class), SLOW_SERVLET_REGEX)
+            .withServlet(new ServletHolder(RedirectServlet.class), REDIRECT_SERVLET_REGEX)
+            .withServlet(new ServletHolder(SlowServlet.class), SLOW_SERVLET_REGEX)
             .withServlet(new ServletHolder(DebugServlet.class), DEBUG_SERVLET_REGEX)
-            .withServlet(
-                new ServletHolder(BasicHttpSolrClientTest.SlowStreamServlet.class),
-                SLOW_STREAM_SERVLET_REGEX)
+            .withServlet(new ServletHolder(SlowStreamServlet.class), SLOW_STREAM_SERVLET_REGEX)
             .withSSLConfig(sslConfig.buildServerSSLConfig())
             .build();
     createAndStartJetty(legacyExampleCollection1SolrHome(), jettyConfig);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ServletFixtures.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ServletFixtures.java
new file mode 100644
index 00000000000..c093cd673ca
--- /dev/null
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ServletFixtures.java
@@ -0,0 +1,228 @@
+/*
+ * 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 jakarta.servlet.ServletException;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.IntStream;
+import org.apache.solr.common.util.SuppressForbidden;
+
+public class ServletFixtures {
+
+  private ServletFixtures() {}
+
+  public static class RedirectServlet extends HttpServlet {
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      resp.sendRedirect("/solr/collection1/select?" + req.getQueryString());
+    }
+  }
+
+  public static class SlowServlet extends HttpServlet {
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      try {
+        Thread.sleep(5000);
+      } catch (InterruptedException ignored) {
+      }
+    }
+  }
+
+  public static class SlowStreamServlet extends HttpServlet {
+
+    public static final int PACKET_MS = 500;
+
+    @SuppressForbidden(reason = "forbiddenApis: getParameter is fine in tests")
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+      String countStr = req.getParameter("count");
+      IntStream.range(0, countStr == null ? 10 : Integer.parseInt(countStr))
+          .forEach(
+              i -> {
+                try {
+                  Thread.sleep(PACKET_MS);
+                  resp.getOutputStream().write(String.valueOf(i).getBytes(StandardCharsets.UTF_8));
+                  resp.getOutputStream().flush();
+                } catch (IOException | InterruptedException e) {
+                  throw new RuntimeException(e);
+                }
+              });
+    }
+  }
+
+  public static class DebugServlet extends HttpServlet {
+    public static void clear() {
+      lastMethod = null;
+      url = null;
+      headers = null;
+      parameters = null;
+      errorCode = null;
+      queryString = null;
+      cookies = null;
+      responseHeaders = null;
+      responseBodyByQueryFragment = new ConcurrentHashMap<>();
+    }
+
+    public static Integer errorCode = null;
+    public static String lastMethod = null;
+    public static String url = null;
+    public static HashMap<String, String> headers = null; // lowercase keys!
+    public static Map<String, String[]> parameters = null;
+    public static String queryString = null;
+    public static Cookie[] cookies = null;
+    public static List<String[]> responseHeaders = null;
+    public static Map<String, Object> responseBodyByQueryFragment = new ConcurrentHashMap<>();
+    public static byte[] requestBody = null;
+
+    public static void setErrorCode(Integer code) {
+      errorCode = code;
+    }
+
+    public static void addResponseHeader(String headerName, String headerValue) {
+      if (responseHeaders == null) {
+        responseHeaders = new ArrayList<>();
+      }
+      responseHeaders.add(new String[] {headerName, headerValue});
+    }
+
+    @Override
+    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "delete";
+      recordRequest(req, resp);
+    }
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "get";
+      recordRequest(req, resp);
+    }
+
+    @Override
+    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "head";
+      recordRequest(req, resp);
+    }
+
+    private void setHeaders(HttpServletRequest req) {
+      Enumeration<String> headerNames = req.getHeaderNames();
+      headers = new HashMap<>();
+      while (headerNames.hasMoreElements()) {
+        final String name = headerNames.nextElement();
+        headers.put(name.toLowerCase(Locale.ROOT), req.getHeader(name));
+      }
+    }
+
+    @SuppressForbidden(reason = "fake servlet only")
+    private void setParameters(HttpServletRequest req) {
+      parameters = req.getParameterMap();
+    }
+
+    private void setQueryString(HttpServletRequest req) {
+      queryString = req.getQueryString();
+    }
+
+    private void setCookies(HttpServletRequest req) {
+      Cookie[] ck = req.getCookies();
+      cookies = req.getCookies();
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "post";
+      recordRequest(req, resp);
+    }
+
+    @Override
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+      lastMethod = "put";
+      recordRequest(req, resp);
+    }
+
+    @SuppressForbidden(reason = "forbiddenApis: getWriter is fine in tests")
+    private void recordRequest(HttpServletRequest req, HttpServletResponse resp) {
+      url = req.getRequestURL().toString();
+      setHeaders(req);
+      setParameters(req);
+      setQueryString(req);
+      setCookies(req);
+      try {
+        requestBody = req.getInputStream().readAllBytes();
+      } catch (Exception e) {
+        // ignore
+      }
+      if (responseHeaders != null) {
+        for (String[] h : responseHeaders) {
+          resp.addHeader(h[0], h[1]);
+        }
+      }
+      String qs = req.getQueryString();
+      qs = qs == null ? "" : qs;
+      Object responseBody = null;
+
+      // Tests can set this up to return different response bodies based on substrings in the query
+      // string
+      for (Map.Entry<String, Object> entry : responseBodyByQueryFragment.entrySet()) {
+        if (qs.contains(entry.getKey())) {
+          responseBody = entry.getValue();
+          break;
+        }
+      }
+
+      if (responseBody != null) {
+        try {
+          if (responseBody instanceof String) {
+            resp.getWriter().print((String) responseBody);
+          } else if (responseBody instanceof byte[]) {
+            resp.getOutputStream().write((byte[]) responseBody);
+          } else {
+            throw new IllegalArgumentException(
+                "Only String and byte[] are supported for responseBody.");
+          }
+        } catch (IOException ioe) {
+          throw new RuntimeException(ioe);
+        }
+      }
+      if (null != errorCode) {
+        try {
+          resp.sendError(errorCode);
+        } catch (IOException e) {
+          throw new RuntimeException("sendError IO fail in DebugServlet", e);
+        }
+      }
+    }
+  }
+}


(solr) 07/12: Tests: SolrJettyTestRule: harden cleanup

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 49d61c96563a960c4534c562efb7a5ee70eaa073
Author: David Smiley <ds...@apache.org>
AuthorDate: Fri Nov 28 01:15:12 2025 -0500

    Tests: SolrJettyTestRule: harden cleanup
    
    If we don't insist `jetty` get removed, then one test failure of jetty not stopping can lead to follow-on tests failing as they can't start
---
 .../src/java/org/apache/solr/util/SolrJettyTestRule.java             | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/solr/test-framework/src/java/org/apache/solr/util/SolrJettyTestRule.java b/solr/test-framework/src/java/org/apache/solr/util/SolrJettyTestRule.java
index 061bdaa0d5f..467c6bb2b1b 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/SolrJettyTestRule.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/SolrJettyTestRule.java
@@ -55,9 +55,10 @@ public class SolrJettyTestRule extends SolrClientTestRule {
         throw e;
       } catch (Exception e) {
         throw new RuntimeException(e);
+      } finally {
+        jetty = null;
+        enableProxy = false;
       }
-      jetty = null;
-      enableProxy = false;
     }
   }
 


(solr) 03/12: minor: CloudSolrClient.close: don't throw (#3878)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 0ead8d3e0df42ea9d45ebc1ccbb8c78dbbaebcf5
Author: David Smiley <ds...@apache.org>
AuthorDate: Tue Nov 25 23:12:06 2025 -0500

    minor: CloudSolrClient.close: don't throw (#3878)
    
    CloudSolrClient.close needn't declare that it throws anything; it should generally throw nothing if there's an unexpected problem.
---
 .../client/solrj/impl/CloudHttp2SolrClient.java    | 29 ++++++++--------------
 .../solr/client/solrj/impl/CloudSolrClient.java    |  2 +-
 .../solrj/impl/CloudHttp2SolrClientTest.java       | 26 +++----------------
 .../client/solrj/impl/CloudSolrClientTest.java     |  9 ++-----
 .../client/solrj/apache/CloudLegacySolrClient.java |  6 ++---
 5 files changed, 20 insertions(+), 52 deletions(-)

diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index 0bb4d3eb67a..4fc82b630d3 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -17,7 +17,6 @@
 
 package org.apache.solr.client.solrj.impl;
 
-import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -29,6 +28,7 @@ import org.apache.solr.client.solrj.impl.SolrZkClientTimeout.SolrZkClientTimeout
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -128,8 +128,8 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
       }
       return stateProvider;
     } catch (Exception e) {
-      closeMyClientIfNeeded();
-      throw (e);
+      close();
+      throw e;
     }
   }
 
@@ -138,7 +138,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     try {
       return new Http2ClusterStateProvider<>(solrUrls, httpClient);
     } catch (Exception e) {
-      closeMyClientIfNeeded();
+      close(); // doesn't throw
       throw new RuntimeException(
           "Couldn't initialize a HttpClusterStateProvider (is/are the "
               + "Solr server(s), "
@@ -148,23 +148,14 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     }
   }
 
-  private void closeMyClientIfNeeded() {
-    try {
-      if (clientIsInternal && myClient != null) {
-        myClient.close();
-      }
-    } catch (Exception e) {
-      throw new RuntimeException("Exception on closing myClient", e);
-    }
-  }
-
   @Override
-  public void close() throws IOException {
-    stateProvider.close();
-    lbClient.close();
-
-    closeMyClientIfNeeded();
+  public void close() {
+    IOUtils.closeQuietly(stateProvider);
+    IOUtils.closeQuietly(lbClient);
 
+    if (clientIsInternal) {
+      IOUtils.closeQuietly(myClient);
+    }
     super.close();
   }
 
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index 4922edc8aaa..cecbc4fcd23 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -227,7 +227,7 @@ public abstract class CloudSolrClient extends SolrClient {
   }
 
   @Override
-  public void close() throws IOException {
+  public void close() {
     if (this.threadPool != null && !ExecutorUtil.isShutdown(this.threadPool)) {
       ExecutorUtil.shutdownAndAwaitTermination(this.threadPool);
       this.threadPool = null;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
index 060d01a6414..cf490ce042e 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
@@ -68,6 +68,7 @@ import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.ShardParams;
 import org.apache.solr.common.params.UpdateParams;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.URLUtil;
@@ -153,28 +154,9 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
 
   @AfterClass
   public static void tearDownAfterClass() throws Exception {
-
-    if (httpJettyBasedCloudSolrClient != null) {
-      try {
-        httpJettyBasedCloudSolrClient.close();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-    if (httpJdkBasedCloudSolrClient != null) {
-      try {
-        httpJdkBasedCloudSolrClient.close();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
-    if (zkBasedCloudSolrClient != null) {
-      try {
-        zkBasedCloudSolrClient.close();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
+    IOUtils.closeQuietly(httpJettyBasedCloudSolrClient);
+    IOUtils.closeQuietly(httpJdkBasedCloudSolrClient);
+    IOUtils.closeQuietly(zkBasedCloudSolrClient);
 
     shutdownCluster();
     httpJettyBasedCloudSolrClient = null;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
index 7f45e692827..5a7ae3963b0 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
@@ -72,6 +72,7 @@ import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.ShardParams;
 import org.apache.solr.common.params.UpdateParams;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.URLUtil;
@@ -121,13 +122,7 @@ public class CloudSolrClientTest extends SolrCloudTestCase {
 
   @AfterClass
   public static void tearDownAfterClass() throws Exception {
-    if (httpBasedCloudSolrClient != null) {
-      try {
-        httpBasedCloudSolrClient.close();
-      } catch (IOException e) {
-        throw new RuntimeException(e);
-      }
-    }
+    IOUtils.closeQuietly(httpBasedCloudSolrClient);
     httpBasedCloudSolrClient = null;
 
     shutdownCluster();
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/CloudLegacySolrClient.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/CloudLegacySolrClient.java
index 51ec0cca26a..1be96a11d46 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/CloudLegacySolrClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/CloudLegacySolrClient.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.client.solrj.apache;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -38,6 +37,7 @@ import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.common.cloud.DocRouter;
 import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.NamedList;
 
 /**
@@ -116,8 +116,8 @@ public class CloudLegacySolrClient extends CloudSolrClient {
   }
 
   @Override
-  public void close() throws IOException {
-    stateProvider.close();
+  public void close() {
+    IOUtils.closeQuietly(stateProvider);
 
     if (shutdownLBHttpSolrServer) {
       lbClient.close();


(solr) 04/12: SOLR-17996: SolrRequest.requestWithBaseUrl and on HttpSolrClientBase (#3881)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit fbf197fead895a972caf8b55b2594c148167c566
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Nov 27 09:50:07 2025 -0500

    SOLR-17996: SolrRequest.requestWithBaseUrl and on HttpSolrClientBase (#3881)
    
    New HttpSolrClientBase.requestWithBaseUrl (abstract, returns NamedList).
    Change Http2SolrClient.requestWithBaseUrl to implement it, return NamedList instead of SolrRequest generic return type.
    New SolrRequest.processWithBaseUrl that returns the SolrRequest response.
---
 .../unreleased/SOLR-17996-requestWithBaseUrl.yml      |  8 ++++++++
 .../java/org/apache/solr/bench/MiniClusterState.java  | 15 +++++++--------
 .../org/apache/solr/bench/index/CloudIndexing.java    |  2 +-
 .../org/apache/solr/bench/search/FilterCache.java     |  8 ++++----
 .../org/apache/solr/bench/search/JsonFaceting.java    |  5 +----
 .../org/apache/solr/bench/search/NumericSearch.java   |  4 ++--
 .../api/collections/CollectionHandlingUtils.java      |  2 +-
 .../cloud/api/collections/ReindexCollectionCmd.java   |  5 ++---
 .../src/java/org/apache/solr/core/CoreContainer.java  |  5 +++--
 .../org/apache/solr/filestore/DistribFileStore.java   |  2 +-
 .../java/org/apache/solr/handler/IndexFetcher.java    |  9 ++++-----
 .../org/apache/solr/handler/SolrConfigHandler.java    |  4 +---
 .../java/org/apache/solr/handler/admin/ColStatus.java |  2 +-
 .../handler/component/IterativeMergeStrategy.java     |  2 +-
 .../org/apache/solr/schema/ManagedIndexSchema.java    |  3 +--
 .../apache/solr/security/PKIAuthenticationPlugin.java |  2 +-
 .../org/apache/solr/update/PeerSyncWithLeader.java    |  2 +-
 .../org/apache/solr/update/SolrCmdDistributor.java    |  2 +-
 .../update/processor/DistributedUpdateProcessor.java  |  6 +-----
 .../handler/TestUserManagedReplicationWithAuth.java   |  4 ++--
 .../solrj/impl/SolrClientNodeStateProvider.java       |  8 ++------
 .../apache/solr/common/cloud/NodesSysPropsCacher.java |  4 +---
 .../org/apache/solr/client/solrj/SolrRequest.java     | 19 +++++++++++++++++++
 .../solrj/impl/ConcurrentUpdateHttp2SolrClient.java   |  9 ++++-----
 .../solr/client/solrj/impl/Http2SolrClient.java       | 18 ++++--------------
 .../solr/client/solrj/impl/HttpJdkSolrClient.java     |  3 ++-
 .../solr/client/solrj/impl/HttpSolrClientBase.java    | 15 +++++++++++++++
 .../apache/solr/client/solrj/impl/LBSolrClient.java   |  6 ++----
 .../solr/client/solrj/impl/Http2SolrClientTest.java   |  4 ++--
 29 files changed, 95 insertions(+), 83 deletions(-)

diff --git a/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml b/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
new file mode 100644
index 00000000000..99c4c8c40e9
--- /dev/null
+++ b/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: New SolrJ SolrRequest.requestWithBaseUrl, new HttpSolrClientBase.requestWithBaseUrl
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+  - name: David Smiley
+links:
+  - name: SOLR-17996
+    url: https://issues.apache.org/jira/browse/SOLR-17996
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
index 2dfce895ddc..baf4ccedbfb 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/MiniClusterState.java
@@ -322,7 +322,7 @@ public class MiniClusterState {
           CollectionAdminRequest.Create request =
               CollectionAdminRequest.createCollection(collection, "conf", numShards, numReplicas);
           client.requestWithBaseUrl(
-              nodes.get(random.nextInt(cluster.getJettySolrRunners().size())), null, request);
+              nodes.get(random.nextInt(cluster.getJettySolrRunners().size())), request, null);
 
           cluster.waitForActiveCollection(
               collection, 15, TimeUnit.SECONDS, numShards, numShards * numReplicas);
@@ -372,7 +372,7 @@ public class MiniClusterState {
         UpdateRequest commitRequest = new UpdateRequest();
         final var url = nodes.get(random.nextInt(cluster.getJettySolrRunners().size()));
         commitRequest.setAction(UpdateRequest.ACTION.COMMIT, false, true);
-        client.requestWithBaseUrl(url, collection, commitRequest);
+        client.requestWithBaseUrl(url, commitRequest, collection);
         log("done committing data");
       } else {
         cluster.waitForActiveCollection(collection, 15, TimeUnit.SECONDS);
@@ -380,8 +380,7 @@ public class MiniClusterState {
 
       QueryRequest queryRequest = new QueryRequest(new SolrQuery("q", "*:*", "rows", "1"));
       final var url = nodes.get(random.nextInt(cluster.getJettySolrRunners().size()));
-      NamedList<Object> result =
-          client.requestWithBaseUrl(url, collection, queryRequest).getResponse();
+      NamedList<Object> result = client.requestWithBaseUrl(url, queryRequest, collection);
 
       log("sanity check of single row query result: " + result);
       log("");
@@ -428,7 +427,7 @@ public class MiniClusterState {
                 meter.mark();
 
                 try {
-                  client.requestWithBaseUrl(url, collection, updateRequest);
+                  client.requestWithBaseUrl(url, updateRequest, collection);
                 } catch (Exception e) {
                   throw new RuntimeException(e);
                 }
@@ -456,7 +455,7 @@ public class MiniClusterState {
         if (i % batchSize == 0) {
           UpdateRequest updateRequest = new UpdateRequest();
           updateRequest.add(batch);
-          client.requestWithBaseUrl(nodes.get(0), collection, updateRequest);
+          client.requestWithBaseUrl(nodes.get(0), updateRequest, collection);
           meter.mark(batch.size());
           batch.clear();
           log(meter.getCount() + " docs at " + (long) meter.getMeanRate() + " doc/s");
@@ -465,7 +464,7 @@ public class MiniClusterState {
       if (!batch.isEmpty()) {
         UpdateRequest updateRequest = new UpdateRequest();
         updateRequest.add(batch);
-        client.requestWithBaseUrl(nodes.get(0), collection, updateRequest);
+        client.requestWithBaseUrl(nodes.get(0), updateRequest, collection);
         meter.mark(batch.size());
         batch = null;
       }
@@ -503,7 +502,7 @@ public class MiniClusterState {
         UpdateRequest optimizeRequest = new UpdateRequest();
         final var url = nodes.get(random.nextInt(cluster.getJettySolrRunners().size()));
         optimizeRequest.setAction(UpdateRequest.ACTION.OPTIMIZE, false, true, maxMergeSegments);
-        client.requestWithBaseUrl(url, collection, optimizeRequest);
+        client.requestWithBaseUrl(url, optimizeRequest, collection);
       }
     }
 
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java b/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
index 0ee950e7231..5f6c6526690 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/index/CloudIndexing.java
@@ -130,6 +130,6 @@ public class CloudIndexing {
     updateRequest.add(state.getNextDoc());
     final var url =
         miniClusterState.nodes.get(miniClusterState.getRandom().nextInt(state.nodeCount));
-    return miniClusterState.client.requestWithBaseUrl(url, BenchState.COLLECTION, updateRequest);
+    return miniClusterState.client.requestWithBaseUrl(url, updateRequest, BenchState.COLLECTION);
   }
 }
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java b/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
index 6b7f6bf4a4f..4b1bf9485e8 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/FilterCache.java
@@ -109,7 +109,7 @@ public class FilterCache {
         throws SolrServerException, IOException {
       // Reload the collection/core to drop existing caches
       CollectionAdminRequest.Reload reload = CollectionAdminRequest.reloadCollection(COLLECTION);
-      miniClusterState.client.requestWithBaseUrl(miniClusterState.nodes.get(0), null, reload);
+      miniClusterState.client.requestWithBaseUrl(miniClusterState.nodes.get(0), reload, null);
     }
 
     @TearDown(Level.Iteration)
@@ -139,8 +139,8 @@ public class FilterCache {
       throws SolrServerException, IOException {
     return miniClusterState.client.requestWithBaseUrl(
         benchState.baseUrl,
-        COLLECTION,
-        miniClusterState.getRandom().nextBoolean() ? benchState.q1 : benchState.q2);
+        miniClusterState.getRandom().nextBoolean() ? benchState.q1 : benchState.q2,
+        COLLECTION);
   }
 
   @Benchmark
@@ -148,6 +148,6 @@ public class FilterCache {
       BenchState benchState, MiniClusterState.MiniClusterBenchState miniClusterState)
       throws SolrServerException, IOException {
     return miniClusterState.client.requestWithBaseUrl(
-        benchState.baseUrl, COLLECTION, benchState.q1);
+        benchState.baseUrl, benchState.q1, COLLECTION);
   }
 }
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java b/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
index f1b2e21f2ad..9482f8a588b 100755
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/JsonFaceting.java
@@ -193,10 +193,7 @@ public class JsonFaceting {
     final var url = miniClusterState.nodes.get(threadState.random.nextInt(state.nodeCount));
     QueryRequest queryRequest = new QueryRequest(state.params);
     NamedList<Object> result =
-        miniClusterState
-            .client
-            .requestWithBaseUrl(url, state.collection, queryRequest)
-            .getResponse();
+        miniClusterState.client.requestWithBaseUrl(url, queryRequest, state.collection);
 
     // MiniClusterState.log("result: " + result);
 
diff --git a/solr/benchmark/src/java/org/apache/solr/bench/search/NumericSearch.java b/solr/benchmark/src/java/org/apache/solr/bench/search/NumericSearch.java
index 398d102ec7c..7717b005fb3 100644
--- a/solr/benchmark/src/java/org/apache/solr/bench/search/NumericSearch.java
+++ b/solr/benchmark/src/java/org/apache/solr/bench/search/NumericSearch.java
@@ -102,7 +102,7 @@ public class NumericSearch {
       q.setParam("facet.limit", String.valueOf(maxCardinality));
       QueryRequest req = new QueryRequest(q);
       QueryResponse response =
-          miniClusterState.client.requestWithBaseUrl(basePath, COLLECTION, req);
+          req.processWithBaseUrl(miniClusterState.client, basePath, COLLECTION);
       Set<String> numbers =
           response.getFacetField("numbers_i_dv").getValues().stream()
               .map(FacetField.Count::getName)
@@ -144,7 +144,7 @@ public class NumericSearch {
         throws SolrServerException, IOException {
       // Reload the collection/core to drop existing caches
       CollectionAdminRequest.Reload reload = CollectionAdminRequest.reloadCollection(COLLECTION);
-      miniClusterState.client.requestWithBaseUrl(miniClusterState.nodes.get(0), null, reload);
+      miniClusterState.client.requestWithBaseUrl(miniClusterState.nodes.get(0), reload, null);
     }
 
     public QueryRequest intSetQuery(boolean dvs) {
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
index 806ad86ba04..22f3618d111 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java
@@ -248,7 +248,7 @@ public class CollectionHandlingUtils {
       throws SolrServerException, IOException {
     UpdateRequest ureq = new UpdateRequest();
     ureq.setAction(AbstractUpdateRequest.ACTION.COMMIT, false, true, true);
-    return solrClient.requestWithBaseUrl(baseUrl, coreName, ureq);
+    return ureq.processWithBaseUrl(solrClient, baseUrl, coreName);
   }
 
   public static String waitForCoreNodeName(
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
index 23a7f7a6553..8909841c936 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/ReindexCollectionCmd.java
@@ -864,9 +864,8 @@ public class ReindexCollectionCmd implements CollApiCmds.CollectionApiCommand {
         new GenericSolrRequest(
                 SolrRequest.METHOD.POST, "/stream", SolrRequest.SolrRequestType.ADMIN, solrParams)
             .setRequiresCollection(true);
-    final var solrResponse =
-        solrClient.requestWithBaseUrl(daemonReplica.getBaseUrl(), daemonReplica.getCoreName(), req);
-    return solrResponse.getResponse();
+    return solrClient.requestWithBaseUrl(
+        daemonReplica.getBaseUrl(), req, daemonReplica.getCoreName());
   }
 
   private void cleanup(
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index f9c88031ab6..be631383c6b 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -70,6 +70,7 @@ import org.apache.solr.api.ContainerPluginsRegistry;
 import org.apache.solr.api.JerseyResource;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.client.solrj.io.SolrClientCache;
 import org.apache.solr.client.solrj.util.SolrIdentifierValidator;
 import org.apache.solr.cloud.CloudDescriptor;
@@ -703,7 +704,7 @@ public class CoreContainer {
    *
    * @see #getDefaultHttpSolrClient()
    * @see ZkController#getSolrClient()
-   * @see Http2SolrClient#requestWithBaseUrl(String, String, SolrRequest)
+   * @see HttpSolrClientBase#requestWithBaseUrl(String, SolrRequest, String)
    * @deprecated likely to simply be moved to the ObjectCache so as to not be used
    */
   @Deprecated
@@ -2389,7 +2390,7 @@ public class CoreContainer {
    * <p>The caller does not need to close the client.
    *
    * @return the existing {@link Http2SolrClient}
-   * @see Http2SolrClient#requestWithBaseUrl(String, String, SolrRequest)
+   * @see HttpSolrClientBase#requestWithBaseUrl(String, SolrRequest, String)
    */
   public Http2SolrClient getDefaultHttpSolrClient() {
     return solrClientProvider.getSolrClient();
diff --git a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
index 94177f65180..797b1f584e5 100644
--- a/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
+++ b/solr/core/src/java/org/apache/solr/filestore/DistribFileStore.java
@@ -205,7 +205,7 @@ public class DistribFileStore implements FileStore {
       ByteBuffer filedata = null;
       try {
         final var fileRequest = new FileStoreApi.GetFile(path);
-        final var fileResponse = solrClient.requestWithBaseUrl(baseUrl, null, fileRequest);
+        final var fileResponse = fileRequest.processWithBaseUrl(solrClient, baseUrl, null);
         try (final var stream = fileResponse.getResponseStreamIfSuccessful()) {
           filedata = Utils.newBytesConsumer((int) MAX_PKG_SIZE).accept(stream);
         }
diff --git a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
index 7eb563d1629..430fad38971 100644
--- a/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
+++ b/solr/core/src/java/org/apache/solr/handler/IndexFetcher.java
@@ -358,7 +358,7 @@ public class IndexFetcher {
     params.set(CommonParams.WT, JAVABIN);
     var req = createReplicationHandlerRequest(params);
     try {
-      return solrClient.requestWithBaseUrl(leaderBaseUrl, leaderCoreName, req).getResponse();
+      return solrClient.requestWithBaseUrl(leaderBaseUrl, req, leaderCoreName);
     } catch (SolrServerException e) {
       throw new SolrException(ErrorCode.SERVER_ERROR, e.getMessage(), e);
     }
@@ -376,8 +376,7 @@ public class IndexFetcher {
     params.set(CommonParams.WT, JAVABIN);
     var req = createReplicationHandlerRequest(params);
     try {
-      NamedList<?> response =
-          solrClient.requestWithBaseUrl(leaderBaseUrl, leaderCoreName, req).getResponse();
+      NamedList<?> response = solrClient.requestWithBaseUrl(leaderBaseUrl, req, leaderCoreName);
 
       List<Map<String, Object>> files = (List<Map<String, Object>>) response.get(CMD_GET_FILE_LIST);
       if (files != null) filesToDownload = Collections.synchronizedList(files);
@@ -1907,7 +1906,7 @@ public class IndexFetcher {
         var req = createReplicationHandlerRequest(params);
         req.setResponseParser(new InputStreamResponseParser(FILE_STREAM));
         if (useExternalCompression) req.addHeader("Accept-Encoding", "gzip");
-        response = solrClient.requestWithBaseUrl(leaderBaseUrl, leaderCoreName, req).getResponse();
+        response = solrClient.requestWithBaseUrl(leaderBaseUrl, req, leaderCoreName);
         final var responseStatus = (Integer) response.get("responseStatus");
         is = (InputStream) response.get("stream");
 
@@ -2050,7 +2049,7 @@ public class IndexFetcher {
     params.set("follower", false);
 
     var request = createReplicationHandlerRequest(params);
-    return solrClient.requestWithBaseUrl(leaderBaseUrl, leaderCoreName, request).getResponse();
+    return solrClient.requestWithBaseUrl(leaderBaseUrl, request, leaderCoreName);
   }
 
   public void destroy() {
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index e1f9e030232..b22626f3339 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -1006,9 +1006,7 @@ public class SolrConfigHandler extends RequestHandlerBase
           Thread.sleep(100);
 
           NamedList<Object> resp =
-              solrClient
-                  .requestWithBaseUrl(replica.getBaseUrl(), replica.getCoreName(), this)
-                  .getResponse();
+              solrClient.requestWithBaseUrl(replica.getBaseUrl(), this, replica.getCoreName());
           if (resp != null) {
             @SuppressWarnings({"rawtypes"})
             Map m = (Map) resp.get(ZNODEVER);
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
index 0ef42781ddf..efca9d39fa5 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
@@ -206,7 +206,7 @@ public class ColStatus {
                         SolrRequest.SolrRequestType.ADMIN,
                         params)
                     .setRequiresCollection(true);
-            NamedList<Object> rsp = solrClient.requestWithBaseUrl(url, null, req).getResponse();
+            NamedList<Object> rsp = solrClient.requestWithBaseUrl(url, req, null);
             final var segmentResponse =
                 SolrJacksonMapper.getObjectMapper().convertValue(rsp, GetSegmentDataResponse.class);
             segmentResponse.responseHeader = null;
diff --git a/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java b/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
index 7fb538315ff..31f1d95ed0f 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/IterativeMergeStrategy.java
@@ -104,7 +104,7 @@ public abstract class IterativeMergeStrategy implements MergeStrategy {
 
     @Override
     public CallBack call() throws Exception {
-      response = httpSolrClient.requestWithBaseUrl(shardBaseUrl, shardCoreName, req);
+      response = req.processWithBaseUrl(httpSolrClient, shardBaseUrl, shardCoreName);
       return this;
     }
   }
diff --git a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
index 671aafe0555..75c2f9c9a93 100644
--- a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
+++ b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchema.java
@@ -381,8 +381,7 @@ public final class ManagedIndexSchema extends IndexSchema {
               zkController
                   .getCoreContainer()
                   .getDefaultHttpSolrClient()
-                  .requestWithBaseUrl(baseUrl, coreName, this)
-                  .getResponse();
+                  .requestWithBaseUrl(baseUrl, this, coreName);
           if (zkversionResp != null) remoteVersion = (Integer) zkversionResp.get("zkversion");
 
           if (remoteVersion < expectedZkVersion) {
diff --git a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
index 44251bb324d..baec6f23e3b 100644
--- a/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/PKIAuthenticationPlugin.java
@@ -383,7 +383,7 @@ public class PKIAuthenticationPlugin extends AuthenticationPlugin
       final var request = new GenericSolrRequest(GET, PublicKeyHandler.PATH, solrParams);
       log.debug("Fetching fresh public key from: {}", url);
       var solrClient = cores.getDefaultHttpSolrClient();
-      NamedList<Object> resp = solrClient.requestWithBaseUrl(url, request::process).getResponse();
+      NamedList<Object> resp = solrClient.requestWithBaseUrl(url, request, null);
 
       String key = (String) resp.get("key");
       if (key == null) {
diff --git a/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java b/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
index 49027b1665c..b6dc8e14fdf 100644
--- a/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
+++ b/solr/core/src/java/org/apache/solr/update/PeerSyncWithLeader.java
@@ -354,7 +354,7 @@ public class PeerSyncWithLeader implements SolrMetricProducer {
           new GenericSolrRequest(
                   SolrRequest.METHOD.GET, "/get", SolrRequest.SolrRequestType.QUERY, params)
               .setRequiresCollection(true);
-      var rsp = clientToLeader.requestWithBaseUrl(leaderBaseUrl, coreName, request);
+      var rsp = request.processWithBaseUrl(clientToLeader, leaderBaseUrl, coreName);
       Exception exception = rsp.getException();
       if (exception != null) {
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, onFail);
diff --git a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
index 02b88932154..5046d9ed876 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
@@ -316,7 +316,7 @@ public class SolrCmdDistributor implements Closeable {
       try {
         clients
             .getHttpClient()
-            .requestWithBaseUrl(req.node.getBaseUrl(), req.node.getCoreName(), req.uReq);
+            .requestWithBaseUrl(req.node.getBaseUrl(), req.uReq, req.node.getCoreName());
       } catch (Exception e) {
         log.error("Exception making request", e);
         SolrError error = new SolrError();
diff --git a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
index 0dc45a046c9..82424a5f06a 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
@@ -692,11 +692,7 @@ public class DistributedUpdateProcessor extends UpdateRequestProcessor {
 
     NamedList<Object> rsp;
     try {
-      rsp =
-          updateShardHandler
-              .getUpdateOnlyHttpClient()
-              .requestWithBaseUrl(leaderUrl, null, ur)
-              .getResponse();
+      rsp = updateShardHandler.getUpdateOnlyHttpClient().requestWithBaseUrl(leaderUrl, ur, null);
     } catch (SolrServerException e) {
       throw new SolrException(
           ErrorCode.SERVER_ERROR,
diff --git a/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java b/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
index ccd150a740b..af7d03fec4c 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestUserManagedReplicationWithAuth.java
@@ -236,7 +236,7 @@ public class TestUserManagedReplicationWithAuth extends SolrTestCaseJ4 {
     withBasicAuth(req);
 
     final var baseUrl = buildUrl(Jetty.getLocalPort());
-    solrClient.requestWithBaseUrl(baseUrl, DEFAULT_TEST_CORENAME, req);
+    solrClient.requestWithBaseUrl(baseUrl, req, DEFAULT_TEST_CORENAME);
   }
 
   private void pullIndexFromTo(
@@ -245,7 +245,7 @@ public class TestUserManagedReplicationWithAuth extends SolrTestCaseJ4 {
     String srcUrl = buildUrl(srcSolr.getLocalPort()) + "/" + DEFAULT_TEST_CORENAME;
     QueryRequest req = getQueryRequestForFetchIndex(authEnabled, srcUrl);
     final var baseUrl = buildUrl(destSolr.getLocalPort());
-    followerClient.requestWithBaseUrl(baseUrl, DEFAULT_TEST_CORENAME, req);
+    followerClient.requestWithBaseUrl(baseUrl, req, DEFAULT_TEST_CORENAME);
   }
 
   private QueryRequest getQueryRequestForFetchIndex(boolean authEnabled, String srcUrl) {
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
index 7e21f30daf1..da494738281 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
@@ -220,10 +220,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
 
     try (InputStream in =
         (InputStream)
-            ctx.http2SolrClient()
-                .requestWithBaseUrl(baseUrl, req::process)
-                .getResponse()
-                .get(STREAM_KEY)) {
+            ctx.http2SolrClient().requestWithBaseUrl(baseUrl, req, null).get(STREAM_KEY)) {
 
       NodeValueFetcher.Metrics.prometheusMetricStream(in).forEach(lineProcessor);
     } catch (Exception e) {
@@ -322,8 +319,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
       request.setResponseParser(new JavaBinResponseParser());
 
       try {
-        return ((Http2SolrClient) cloudSolrClient.getHttpClient())
-            .requestWithBaseUrl(url, request::process);
+        return request.processWithBaseUrl(cloudSolrClient.getHttpClient(), url, null);
       } catch (SolrServerException | IOException e) {
         throw new SolrException(ErrorCode.SERVER_ERROR, "Fetching replica metrics failed", e);
       }
diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
index c3b679546ec..f8773b6e213 100644
--- a/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
+++ b/solr/solrj-zookeeper/src/java/org/apache/solr/common/cloud/NodesSysPropsCacher.java
@@ -83,9 +83,7 @@ public class NodesSysPropsCacher implements NodesSysProps, AutoCloseable {
     try {
       LinkedHashMap<String, Object> result = new LinkedHashMap<>();
       NavigableObject response =
-          solrClient
-              .requestWithBaseUrl(zkStateReader.getBaseUrlForNodeName(nodeName), null, req)
-              .getResponse();
+          solrClient.requestWithBaseUrl(zkStateReader.getBaseUrlForNodeName(nodeName), req, null);
       var metrics = NavigableObject.wrap(response._get("system.properties"));
       tags.forEach((tag) -> result.put(tag, metrics._get(tag)));
       return result;
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
index 72ccc3e2987..b3a4eee893e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.ContentStream;
@@ -291,6 +292,24 @@ public abstract class SolrRequest<T> implements Serializable {
     return typedResponse;
   }
 
+  /**
+   * @lucene.experimental
+   */
+  public final T processWithBaseUrl(HttpSolrClientBase client, String url, String collection)
+      throws SolrServerException, IOException {
+    // duplicative with process(), except for requestWithBaseUrl
+    long startNanos = System.nanoTime();
+    var namedList = client.requestWithBaseUrl(url, this, collection);
+    long endNanos = System.nanoTime();
+    T typedResponse = createResponse(namedList);
+    // SolrResponse is pre-V2 API
+    if (typedResponse instanceof SolrResponse res) {
+      res.setResponse(namedList); // TODO insist createResponse does this ?
+      res.setElapsedTime(TimeUnit.NANOSECONDS.toMillis(endNanos - startNanos));
+    }
+    return typedResponse;
+  }
+
   /**
    * Send this request to a {@link SolrClient} and return the response
    *
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java
index 91f57fe55cc..0b9684f6b5b 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java
@@ -374,7 +374,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
             ? defaultCollection
             : collection;
     if (!(request instanceof UpdateRequest req)) {
-      return client.requestWithBaseUrl(basePath, (c) -> c.request(request, effectiveCollection));
+      return client.requestWithBaseUrl(basePath, request, effectiveCollection);
     }
     // this happens for commit...
     if (streamDeletes) {
@@ -383,14 +383,13 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
           && (req.getDeleteByIdMap() == null || req.getDeleteByIdMap().isEmpty())) {
         if (req.getDeleteQuery() == null) {
           blockUntilFinished();
-          return client.requestWithBaseUrl(
-              basePath, (c) -> c.request(request, effectiveCollection));
+          return client.requestWithBaseUrl(basePath, request, effectiveCollection);
         }
       }
     } else {
       if ((req.getDocuments() == null || req.getDocuments().isEmpty())) {
         blockUntilFinished();
-        return client.requestWithBaseUrl(basePath, (c) -> c.request(request, effectiveCollection));
+        return client.requestWithBaseUrl(basePath, request, effectiveCollection);
       }
     }
 
@@ -400,7 +399,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
       if (params.getBool(UpdateParams.WAIT_SEARCHER, false)) {
         log.info("blocking for commit/optimize");
         blockUntilFinished(); // empty the queue
-        return client.requestWithBaseUrl(basePath, (c) -> c.request(request, effectiveCollection));
+        return client.requestWithBaseUrl(basePath, request, effectiveCollection);
       }
     }
 
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 a7b777cbc54..83b24671bbe 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
@@ -44,7 +44,6 @@ import org.apache.solr.client.solrj.ResponseParser;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrClientFunction;
 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.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.HttpListenerFactory.RequestResponseListener;
@@ -613,20 +612,11 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
   }
 
-  /**
-   * Executes a SolrRequest using the provided URL to temporarily override any "base URL" currently
-   * used by this client
-   *
-   * @param baseUrl a URL to a root Solr path (i.e. "/solr") that should be used for this request
-   * @param collection an optional collection or core name used to override the client's "default
-   *     collection". May be 'null' for any requests that don't require a collection or wish to rely
-   *     on the client's default
-   * @param req the SolrRequest to send
-   */
-  public final <R extends SolrResponse> R requestWithBaseUrl(
-      String baseUrl, String collection, SolrRequest<R> req)
+  @Override
+  public NamedList<Object> requestWithBaseUrl(
+      String baseUrl, SolrRequest<?> solrRequest, String collection)
       throws SolrServerException, IOException {
-    return requestWithBaseUrl(baseUrl, (c) -> req.process(c, collection));
+    return requestWithBaseUrl(baseUrl, c -> c.request(solrRequest, collection));
   }
 
   /**
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
index 5f3fa63fe15..d65a8fb9681 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
@@ -165,7 +165,8 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     }
   }
 
-  protected NamedList<Object> requestWithBaseUrl(
+  @Override
+  public NamedList<Object> requestWithBaseUrl(
       String baseUrl, SolrRequest<?> solrRequest, String collection)
       throws SolrServerException, IOException {
     PreparedRequest pReq = prepareRequest(solrRequest, collection, baseUrl);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
index 92cc3b79caf..0fa2936cbfc 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
@@ -340,6 +340,21 @@ public abstract class HttpSolrClientBase extends SolrClient {
 
   protected abstract void updateDefaultMimeTypeForParser();
 
+  /**
+   * Executes a SolrRequest using the provided URL to temporarily override any "base URL" currently
+   * used by this client
+   *
+   * @param baseUrl a URL to a root Solr path (i.e. "/solr") that should be used for this request
+   * @param solrRequest the SolrRequest to send
+   * @param collection an optional collection or core name used to override the client's "default
+   *     collection". May be 'null' for any requests that don't require a collection or wish to rely
+   *     on the client's default
+   * @see SolrRequest#processWithBaseUrl(HttpSolrClientBase, String, String)
+   */
+  public abstract NamedList<Object> requestWithBaseUrl(
+      String baseUrl, SolrRequest<?> solrRequest, String collection)
+      throws SolrServerException, IOException;
+
   /**
    * Execute an asynchronous request against a Solr server for a given collection.
    *
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
index b2560bc7eae..c71d0210bec 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
@@ -506,10 +506,8 @@ public abstract class LBSolrClient extends SolrClient {
     // Some implementations of LBSolrClient.getClient(...) return a Http2SolrClient that may not be
     // pointed at the desired URL (or any URL for that matter).  We special case that here to ensure
     // the appropriate URL is provided.
-    if (solrClient instanceof Http2SolrClient httpSolrClient) {
-      return httpSolrClient.requestWithBaseUrl(baseUrl, (c) -> c.request(solrRequest, collection));
-    } else if (solrClient instanceof HttpJdkSolrClient) {
-      return ((HttpJdkSolrClient) solrClient).requestWithBaseUrl(baseUrl, solrRequest, collection);
+    if (solrClient instanceof HttpSolrClientBase hasReqWithUrl) {
+      return hasReqWithUrl.requestWithBaseUrl(baseUrl, solrRequest, collection);
     }
 
     // Assume provided client already uses 'baseUrl'
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index b05da93f3f3..a56f1bab1b0 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -138,7 +138,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     try (Http2SolrClient client = new Http2SolrClient.Builder(null).build()) {
       try {
         // if client base url is null, request url will be used in exception message
-        client.requestWithBaseUrl(getBaseUrl() + DEBUG_SERVLET_PATH, DEFAULT_CORE, new SolrPing());
+        client.requestWithBaseUrl(getBaseUrl() + DEBUG_SERVLET_PATH, new SolrPing(), DEFAULT_CORE);
 
         fail("Didn't get excepted exception from oversided request");
       } catch (SolrException e) {
@@ -231,7 +231,7 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
     try (Http2SolrClient client =
         new Http2SolrClient.Builder(defaultUrl).withDefaultCollection(DEFAULT_CORE).build()) {
       try {
-        client.requestWithBaseUrl(urlToUse, null, new QueryRequest(queryParams));
+        client.requestWithBaseUrl(urlToUse, new QueryRequest(queryParams), null);
       } catch (SolrClient.RemoteSolrException rse) {
       }
 


(solr) 10/12: SOLR-18005: ConcurrentUpdateJettySolrClient (#3885)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit ed08c58ae8585f7dbea5db6c0187ab903278b516
Author: David Smiley <ds...@apache.org>
AuthorDate: Fri Nov 28 23:40:04 2025 -0500

    SOLR-18005: ConcurrentUpdateJettySolrClient (#3885)
    
    Renamed ConcurrentUpdateHttp2SolrClient to ConcurrentUpdateBaseSolrClient, made it abstract, and made a new ConcurrentUpdateJettySolrClient as the one implementation.  Moved logic from Http2SolrClient that was only related to this logic, over here.
    
    * javadoc record: MissingDoclet didn't consider Java record
---
 .../apache/lucene/missingdoclet/MissingDoclet.java |   1 +
 .../SOLR-18005-ConcurrentUpdateJettySolrClient.yml |   8 +
 ...Client.yml => SOLR-18005-LBAsyncSolrClient.yml} |   4 +-
 .../org/apache/solr/update/SolrCmdDistributor.java |   4 +-
 .../apache/solr/update/StreamingSolrClients.java   |  15 +-
 .../solr/core/TestHttpSolrClientProvider.java      |   4 +-
 .../modules/deployment-guide/pages/solrj.adoc      |   4 +-
 ...nt.java => ConcurrentUpdateBaseSolrClient.java} | 111 +++++-------
 .../impl/ConcurrentUpdateJettySolrClient.java      | 198 +++++++++++++++++++++
 .../solr/client/solrj/impl/Http2SolrClient.java    | 124 +------------
 .../solr/client/solrj/impl/StallDetection.java     |   6 +-
 .../SolrExampleStreamingBinaryHttp2Test.java       |   3 +-
 .../embedded/SolrExampleStreamingHttp2Test.java    |  20 ++-
 ...ncurrentUpdateJettySolrClientBadInputTest.java} |  11 +-
 ...tUpdateJettySolrClientMultiCollectionTest.java} |  12 +-
 ...va => ConcurrentUpdateJettySolrClientTest.java} |  26 +--
 .../client/solrj/impl/Http2SolrClientTest.java     |   8 +-
 .../solrj/apache/ConcurrentUpdateSolrClient.java   |   4 +-
 18 files changed, 324 insertions(+), 239 deletions(-)

diff --git a/build-tools/missing-doclet/src/main/java/org/apache/lucene/missingdoclet/MissingDoclet.java b/build-tools/missing-doclet/src/main/java/org/apache/lucene/missingdoclet/MissingDoclet.java
index bb0b0fb3ea0..14487731f6f 100644
--- a/build-tools/missing-doclet/src/main/java/org/apache/lucene/missingdoclet/MissingDoclet.java
+++ b/build-tools/missing-doclet/src/main/java/org/apache/lucene/missingdoclet/MissingDoclet.java
@@ -237,6 +237,7 @@ public class MissingDoclet extends StandardDoclet {
       case INTERFACE:
       case ENUM:
       case ANNOTATION_TYPE:
+      case RECORD:
         if (level(element) >= CLASS) {
           checkComment(element);
           for (var subElement : element.getEnclosedElements()) {
diff --git a/changelog/unreleased/SOLR-18005-ConcurrentUpdateJettySolrClient.yml b/changelog/unreleased/SOLR-18005-ConcurrentUpdateJettySolrClient.yml
new file mode 100644
index 00000000000..905a259630f
--- /dev/null
+++ b/changelog/unreleased/SOLR-18005-ConcurrentUpdateJettySolrClient.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Renamed ConcurrentUpdateHttp2SolrClient to ConcurrentUpdateBaseSolrClient, made abstract, with a new ConcurrentUpdateJettySolrClient as it's only implementation.
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+  - name: David Smiley
+links:
+  - name: SOLR-18005
+    url: https://issues.apache.org/jira/browse/SOLR-18005
diff --git a/changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml b/changelog/unreleased/SOLR-18005-LBAsyncSolrClient.yml
similarity index 80%
rename from changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml
rename to changelog/unreleased/SOLR-18005-LBAsyncSolrClient.yml
index f722b2ffb46..03d328d7128 100644
--- a/changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml
+++ b/changelog/unreleased/SOLR-18005-LBAsyncSolrClient.yml
@@ -4,5 +4,5 @@ type: other # added, changed, fixed, deprecated, removed, dependency_update, sec
 authors:
   - name: David Smiley
 links:
-  - name: SOLR-17161
-    url: https://issues.apache.org/jira/browse/SOLR-17161
+  - name: SOLR-18005
+    url: https://issues.apache.org/jira/browse/SOLR-18005
diff --git a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
index 5046d9ed876..ec4e7c1f14c 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
@@ -34,7 +34,7 @@ import java.util.concurrent.ExecutorCompletionService;
 import java.util.concurrent.Future;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient;
 import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -482,7 +482,7 @@ public class SolrCmdDistributor implements Closeable {
     /**
      * NOTE: This is the request that happened to be executed when this error was <b>triggered</b>
      * the error, but because of how {@link StreamingSolrClients} uses {@link
-     * ConcurrentUpdateHttp2SolrClient} it might not actaully be the request that <b>caused</b> the
+     * ConcurrentUpdateBaseSolrClient} it might not actaully be the request that <b>caused</b> the
      * error -- multiple requests are merged &amp; processed as a sequential batch.
      */
     public Req req;
diff --git a/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java b/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
index 5790fcf9aea..35bfc79dc63 100644
--- a/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
+++ b/solr/core/src/java/org/apache/solr/update/StreamingSolrClients.java
@@ -27,7 +27,8 @@ import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateJettySolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.StrUtils;
@@ -46,7 +47,7 @@ public class StreamingSolrClients {
 
   private Http2SolrClient httpClient;
 
-  private Map<String, ConcurrentUpdateHttp2SolrClient> solrClients = new HashMap<>();
+  private Map<String, ConcurrentUpdateBaseSolrClient> solrClients = new HashMap<>();
   private List<SolrError> errors = Collections.synchronizedList(new ArrayList<>());
 
   private ExecutorService updateExecutor;
@@ -66,7 +67,7 @@ public class StreamingSolrClients {
 
   public synchronized SolrClient getSolrClient(final SolrCmdDistributor.Req req) {
     String url = getFullUrl(req.node.getUrl());
-    ConcurrentUpdateHttp2SolrClient client = solrClients.get(url);
+    ConcurrentUpdateBaseSolrClient client = solrClients.get(url);
     if (client == null) {
       // NOTE: increasing to more than 1 threadCount for the client could cause updates to be
       // reordered on a greater scale since the current behavior is to only increase the number of
@@ -92,13 +93,13 @@ public class StreamingSolrClients {
   }
 
   public synchronized void blockUntilFinished() throws IOException {
-    for (ConcurrentUpdateHttp2SolrClient client : solrClients.values()) {
+    for (ConcurrentUpdateBaseSolrClient client : solrClients.values()) {
       client.blockUntilFinished();
     }
   }
 
   public synchronized void shutdown() {
-    for (ConcurrentUpdateHttp2SolrClient client : solrClients.values()) {
+    for (ConcurrentUpdateBaseSolrClient client : solrClients.values()) {
       client.close();
     }
   }
@@ -122,7 +123,7 @@ public class StreamingSolrClients {
   }
 }
 
-class ErrorReportingConcurrentUpdateSolrClient extends ConcurrentUpdateHttp2SolrClient {
+class ErrorReportingConcurrentUpdateSolrClient extends ConcurrentUpdateJettySolrClient {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private final SolrCmdDistributor.Req req;
   private final List<SolrError> errors;
@@ -154,7 +155,7 @@ class ErrorReportingConcurrentUpdateSolrClient extends ConcurrentUpdateHttp2Solr
     req.trackRequestResult(resp, respBody, true);
   }
 
-  static class Builder extends ConcurrentUpdateHttp2SolrClient.Builder {
+  static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
     protected SolrCmdDistributor.Req req;
     protected List<SolrError> errors;
 
diff --git a/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java b/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java
index 29a1844869f..42e98a0c282 100644
--- a/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java
+++ b/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java
@@ -48,7 +48,7 @@ public class TestHttpSolrClientProvider extends SolrTestCase {
   public void test_when_updateShardHandler_cfg_is_null() {
     try (var httpSolrClientProvider = new HttpSolrClientProvider(null, parentSolrMetricCtx); ) {
       assertEquals(
-          httpSolrClientProvider.getSolrClient().getIdleTimeout(),
+          httpSolrClientProvider.getSolrClient().getIdleTimeoutMillis(),
           SolrHttpConstants.DEFAULT_SO_TIMEOUT);
     }
   }
@@ -59,7 +59,7 @@ public class TestHttpSolrClientProvider extends SolrTestCase {
     assertNotEquals(idleTimeout, UpdateShardHandlerConfig.DEFAULT.getDistributedSocketTimeout());
     UpdateShardHandlerConfig cfg = new UpdateShardHandlerConfig(-1, -1, idleTimeout, -1, null, -1);
     try (var httpSolrClientProvider = new HttpSolrClientProvider(cfg, parentSolrMetricCtx); ) {
-      assertEquals(httpSolrClientProvider.getSolrClient().getIdleTimeout(), idleTimeout);
+      assertEquals(httpSolrClientProvider.getSolrClient().getIdleTimeoutMillis(), idleTimeout);
     }
   }
 }
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
index 5582621ec7b..6df63275c39 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
@@ -99,7 +99,7 @@ Requests are sent in the form of {solr-javadocs}/solrj/org/apache/solr/client/so
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.html[`HttpJdkSolrClient`] - a general purpose client based on JDK HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Has no dependencies.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBSolrClient.html[`LBSolrClient`] - an internal client that delegates to other clients pointed at different URLs for fail-over/availability.  Adjusts the list of "in-service" nodes based on node health.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/CloudSolrClient.html[`CloudSolrClient`] - the ideal client for SolrCloud.  Using the "cluster state", it routes requests to the optimal nodes, including splitting out the documents in an UpdateRequest to different nodes.
-- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.html[`ConcurrentUpdateHttp2SolrClient`] - geared towards indexing-centric workloads.
+- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.html[`ConcurrentUpdateJettySolrClient`] - geared towards indexing-centric workloads.
 Buffers documents internally before sending larger batches to Solr.
 
 === Common Configuration Options
@@ -147,7 +147,7 @@ include::example$UsingSolrJRefGuideExamplesTest.java[tag=solrj-solrclient-timeou
 
 When these values are not explicitly provided, SolrJ falls back to using the defaults for the OS/environment is running on.
 
-`ConcurrentUpdateSolrClient` and its counterpart `ConcurrentUpdateHttp2SolrClient` also implement a stall prevention
+`ConcurrentUpdateSolrClient` and its counterpart `ConcurrentUpdateBaseSolrClient` also implement a stall prevention
 timeout that allows requests to non-responsive nodes to fail quicker than waiting for a socket timeout.
 The default value of this timeout is set to 15000 ms and can be adjusted by a system property `solr.cloud.client.stallTime`.
 This value should be smaller than `solr.jetty.http.idleTimeout` (Which is 120000 ms by default) and greater than the
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
similarity index 88%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
index 0b9684f6b5b..745bc1ffd2d 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateBaseSolrClient.java
@@ -38,6 +38,7 @@ import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.params.UpdateParams;
 import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SolrNamedThreadFactory;
 import org.eclipse.jetty.client.InputStreamResponseListener;
@@ -47,29 +48,31 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
-/** A Solr client using {@link Http2SolrClient} to send concurrent updates to Solr. */
-public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
+/** A ConcurrentUpdate {@link SolrClient} -- it sends updates concurrently and asynchronously. */
+public abstract class ConcurrentUpdateBaseSolrClient extends SolrClient {
+  // formerly known as ConcurrentUpdateBaseSolrClient
   private static final long serialVersionUID = 1L;
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private static final Update END_UPDATE = new Update(null, null);
 
-  private Http2SolrClient client;
-  private final String basePath;
-  private final CustomBlockingQueue<Update> queue;
+  private HttpSolrClientBase client;
+  protected final String basePath;
+  protected final CustomBlockingQueue<Update> queue;
   private final ExecutorService scheduler;
   private final Queue<Runner> runners;
   private final int threadCount;
+  private final long idleTimeoutMillis;
 
   private boolean shutdownClient;
   private boolean shutdownExecutor;
-  private long pollQueueTimeMillis;
+  protected long pollQueueTimeMillis;
   private final boolean streamDeletes;
   private volatile boolean closed;
   private volatile CountDownLatch lock = null; // used to block everything
 
   protected StallDetection stallDetection;
 
-  private static class CustomBlockingQueue<E> implements Iterable<E> {
+  protected static class CustomBlockingQueue<E> implements Iterable<E> {
     private final BlockingQueue<E> queue;
     private final Semaphore available;
     private final int queueSize;
@@ -140,9 +143,15 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
   }
 
-  protected ConcurrentUpdateHttp2SolrClient(Builder builder) {
+  protected ConcurrentUpdateBaseSolrClient(Builder builder) {
+    if (builder.baseSolrUrl == null) {
+      throw new IllegalArgumentException(
+          "Cannot create HttpSolrClient without a valid baseSolrUrl!");
+    }
+
     this.client = builder.client;
-    this.shutdownClient = builder.closeHttp2Client;
+    this.shutdownClient = builder.closeHttpClient;
+    this.idleTimeoutMillis = builder.idleTimeoutMillis;
     this.threadCount = builder.threadCount;
     this.queue = new CustomBlockingQueue<>(builder.queueSize, threadCount, END_UPDATE);
     this.runners = new ArrayDeque<>();
@@ -177,7 +186,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
   }
 
   /** Class representing an UpdateRequest and an optional collection. */
-  private record Update(UpdateRequest request, String collection) {}
+  protected record Update(UpdateRequest request, String collection) {}
 
   /** Opens a connection and sends everything... */
   class Runner implements Runnable {
@@ -238,28 +247,10 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
             }
 
             InputStreamResponseListener responseListener = null;
-            try (Http2SolrClient.OutStream out =
-                client.initOutStream(basePath, update.request(), update.collection())) {
-              Update upd = update;
-              while (upd != null) {
-                UpdateRequest req = upd.request();
-                if (!out.belongToThisStream(req, upd.collection())) {
-                  // Request has different params or destination core/collection, return to queue
-                  queue.add(upd);
-                  break;
-                }
-                client.send(out, upd.request(), upd.collection());
-                out.flush();
-
-                notifyQueueAndRunnersIfEmptyQueue();
-                upd = queue.poll(pollQueueTimeMillis, TimeUnit.MILLISECONDS);
-              }
-              responseListener = out.getResponseListener();
-            }
+            responseListener = doSendUpdateStream(update);
 
             // just wait for the headers, so the idle timeout is sensible
-            Response response =
-                responseListener.get(client.getIdleTimeout(), TimeUnit.MILLISECONDS);
+            Response response = responseListener.get(idleTimeoutMillis, TimeUnit.MILLISECONDS);
             rspBody = responseListener.getInputStream();
 
             int statusCode = response.getStatus();
@@ -288,8 +279,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
                 // don't want to fail to report error if parsing the response fails
                 log.warn("Failed to parse error response from {} due to: ", basePath, exc);
               } finally {
-                solrExc =
-                    new SolrClient.RemoteSolrException(basePath, statusCode, msg.toString(), null);
+                solrExc = new RemoteSolrException(basePath, statusCode, msg.toString(), null);
                 if (metadata != null) {
                   solrExc.setMetadata(metadata);
                 }
@@ -316,6 +306,9 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
   }
 
+  protected abstract InputStreamResponseListener doSendUpdateStream(Update update)
+      throws IOException, InterruptedException;
+
   private void consumeFully(InputStream is) {
     if (is != null) {
       try (is) {
@@ -330,7 +323,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
   }
 
-  private void notifyQueueAndRunnersIfEmptyQueue() {
+  protected void notifyQueueAndRunnersIfEmptyQueue() {
     if (queue.size() == 0) {
       synchronized (queue) {
         // queue may be empty
@@ -348,7 +341,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
   // *must* be called with runners monitor held, e.g. synchronized(runners){ addRunner() }
   private void addRunner() {
     MDC.put(
-        "ConcurrentUpdateHttp2SolrClient.url",
+        "ConcurrentUpdateBaseSolrClient.url",
         String.valueOf(client.getBaseURL())); // MDC can't have null value
     try {
       Runner r = new Runner();
@@ -362,7 +355,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
         throw e;
       }
     } finally {
-      MDC.remove("ConcurrentUpdateHttp2SolrClient.url");
+      MDC.remove("ConcurrentUpdateBaseSolrClient.url");
     }
   }
 
@@ -605,7 +598,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
         interruptRunnerThreadsPolling();
       }
     } finally {
-      if (shutdownClient) client.close();
+      if (shutdownClient) IOUtils.closeQuietly(client);
     }
   }
 
@@ -639,16 +632,17 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
   }
 
-  /** Constructs {@link ConcurrentUpdateHttp2SolrClient} instances from provided configuration. */
-  public static class Builder {
-    protected Http2SolrClient client;
+  /** Constructs {@link ConcurrentUpdateBaseSolrClient} instances from provided configuration. */
+  public abstract static class Builder {
+    public long idleTimeoutMillis;
+    protected HttpSolrClientBase client;
     protected String baseSolrUrl;
     protected String defaultCollection;
     protected int queueSize = 10;
     protected int threadCount;
     protected ExecutorService executorService;
     protected boolean streamDeletes;
-    protected boolean closeHttp2Client;
+    protected boolean closeHttpClient;
     private long pollQueueTimeMillis;
 
     /**
@@ -657,7 +651,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
      * <p>The provided URL must point to the root Solr path (i.e. "/solr"), for example:
      *
      * <pre>
-     *   SolrClient client = new ConcurrentUpdateHttp2SolrClient.Builder("http://my-solr-server:8983/solr", http2Client)
+     *   SolrClient client = new ConcurrentUpdateJettySolrClient.Builder("http://my-solr-server:8983/solr", http2Client)
      *       .withDefaultCollection("core1")
      *       .build();
      *   QueryResponse resp = client.query(new SolrQuery("*:*"));
@@ -665,11 +659,11 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
      *
      * @param baseSolrUrl a URL pointing to the root Solr path, typically of the form
      *     "http[s]://host:port/solr"
-     * @param client a client for this ConcurrentUpdateHttp2SolrClient to use for all requests
+     * @param client a client for this ConcurrentUpdateJettySolrClient to use for all requests
      *     internally. Callers are responsible for closing the provided client (after closing any
      *     clients created by this builder)
      */
-    public Builder(String baseSolrUrl, Http2SolrClient client) {
+    public Builder(String baseSolrUrl, HttpSolrClientBase client) {
       this(baseSolrUrl, client, false);
     }
 
@@ -679,7 +673,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
      * <p>The provided URL must point to the root Solr path (i.e. "/solr"), for example:
      *
      * <pre>
-     *   SolrClient client = new ConcurrentUpdateHttp2SolrClient.Builder("http://my-solr-server:8983/solr", http2Client)
+     *   SolrClient client = new ConcurrentUpdateJettySolrClient.Builder("http://my-solr-server:8983/solr", http2Client)
      *       .withDefaultCollection("core1")
      *       .build();
      *   QueryResponse resp = client.query(new SolrQuery("*:*"));
@@ -687,16 +681,16 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
      *
      * @param baseSolrUrl a URL pointing to the root Solr path, typically of the form
      *     "http[s]://host:port/solr"
-     * @param client a client for this ConcurrentUpdateHttp2SolrClient to use for all requests
+     * @param client a client for this ConcurrentUpdateJettySolrClient to use for all requests
      *     internally.
-     * @param closeHttp2Client a boolean flag indicating whether the created
-     *     ConcurrentUpdateHttp2SolrClient should assume responsibility for closing the provided
+     * @param closeHttpClient a boolean flag indicating whether the created
+     *     ConcurrentUpdateBaseSolrClient should assume responsibility for closing the provided
      *     'client'
      */
-    public Builder(String baseSolrUrl, Http2SolrClient client, boolean closeHttp2Client) {
+    public Builder(String baseSolrUrl, HttpSolrClientBase client, boolean closeHttpClient) {
       this.baseSolrUrl = baseSolrUrl;
       this.client = client;
-      this.closeHttp2Client = closeHttp2Client;
+      this.closeHttpClient = closeHttpClient;
     }
 
     /**
@@ -706,7 +700,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
      * <p>This value should be carefully paired with the number of queue-consumer threads. A queue
      * with a maximum size set too high may require more memory. A queue with a maximum size set too
      * low may suffer decreased throughput as {@link
-     * ConcurrentUpdateHttp2SolrClient#request(SolrRequest)} calls block waiting to add requests to
+     * ConcurrentUpdateBaseSolrClient#request(SolrRequest)} calls block waiting to add requests to
      * the queue.
      *
      * <p>If not set, this defaults to 10.
@@ -722,14 +716,14 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
 
     /**
-     * The maximum number of threads used to empty {@link ConcurrentUpdateHttp2SolrClient}s queue.
+     * The maximum number of threads used to empty {@link ConcurrentUpdateBaseSolrClient}s queue.
      *
      * <p>Threads are created when documents are added to the client's internal queue and exit when
      * no updates remain in the queue.
      *
      * <p>This value should be carefully paired with the maximum queue capacity. A client with too
      * few threads may suffer decreased throughput as the queue fills up and {@link
-     * ConcurrentUpdateHttp2SolrClient#request(SolrRequest)} calls block waiting to add requests to
+     * ConcurrentUpdateBaseSolrClient#request(SolrRequest)} calls block waiting to add requests to
      * the queue.
      */
     public Builder withThreadCount(int threadCount) {
@@ -763,7 +757,7 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     /**
      * Configures created clients to not stream delete requests.
      *
-     * <p>With this option set when the created ConcurrentUpdateHttp2SolrClient sends a delete
+     * <p>With this option set when the created ConcurrentUpdateBaseSolrClient sends a delete
      * request it will first will lock the queue and block until all queued updates have been sent,
      * and then send the delete request.
      */
@@ -787,15 +781,8 @@ public class ConcurrentUpdateHttp2SolrClient extends SolrClient {
     }
 
     /**
-     * Create a {@link ConcurrentUpdateHttp2SolrClient} based on the provided configuration options.
+     * Create a {@link ConcurrentUpdateBaseSolrClient} based on the provided configuration options.
      */
-    public ConcurrentUpdateHttp2SolrClient build() {
-      if (baseSolrUrl == null) {
-        throw new IllegalArgumentException(
-            "Cannot create HttpSolrClient without a valid baseSolrUrl!");
-      }
-
-      return new ConcurrentUpdateHttp2SolrClient(this);
-    }
+    public abstract ConcurrentUpdateBaseSolrClient build();
   }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java
new file mode 100644
index 00000000000..866ac66729e
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClient.java
@@ -0,0 +1,198 @@
+/*
+ * 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 java.io.Closeable;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.util.ClientUtils;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.UpdateParams;
+import org.eclipse.jetty.client.InputStreamResponseListener;
+import org.eclipse.jetty.client.OutputStreamRequestContent;
+import org.eclipse.jetty.client.Request;
+import org.eclipse.jetty.http.HttpMethod;
+
+/** A ConcurrentUpdate SolrClient using {@link Http2SolrClient}. */
+public class ConcurrentUpdateJettySolrClient extends ConcurrentUpdateBaseSolrClient {
+  protected static final Charset FALLBACK_CHARSET = StandardCharsets.UTF_8;
+
+  private final Http2SolrClient client;
+
+  public static class Builder extends ConcurrentUpdateBaseSolrClient.Builder {
+    /**
+     * @see ConcurrentUpdateBaseSolrClient.Builder#Builder(String, HttpSolrClientBase)
+     */
+    public Builder(String baseUrl, Http2SolrClient client) {
+      this(baseUrl, client, false);
+    }
+
+    /**
+     * @see ConcurrentUpdateBaseSolrClient.Builder#Builder(String, HttpSolrClientBase, boolean)
+     */
+    public Builder(String baseSolrUrl, Http2SolrClient client, boolean closeHttpClient) {
+      super(baseSolrUrl, client, closeHttpClient);
+      this.idleTimeoutMillis = client.getIdleTimeoutMillis();
+    }
+
+    @Override
+    public ConcurrentUpdateJettySolrClient build() {
+      return new ConcurrentUpdateJettySolrClient(this);
+    }
+  }
+
+  protected ConcurrentUpdateJettySolrClient(Builder builder) {
+    super(builder);
+    this.client = (Http2SolrClient) builder.client;
+  }
+
+  @Override
+  protected InputStreamResponseListener doSendUpdateStream(
+      ConcurrentUpdateBaseSolrClient.Update update) throws IOException, InterruptedException {
+    InputStreamResponseListener responseListener;
+    try (OutStream out = initOutStream(basePath, update.request(), update.collection())) {
+      ConcurrentUpdateBaseSolrClient.Update upd = update;
+      while (upd != null) {
+        UpdateRequest req = upd.request();
+        if (!out.belongToThisStream(req, upd.collection())) {
+          // Request has different params or destination core/collection, return to queue
+          queue.add(upd);
+          break;
+        }
+        send(out, upd.request(), upd.collection());
+        out.flush();
+
+        notifyQueueAndRunnersIfEmptyQueue();
+        upd = queue.poll(pollQueueTimeMillis, TimeUnit.MILLISECONDS);
+      }
+      responseListener = out.getResponseListener();
+    }
+    return responseListener;
+  }
+
+  private static class OutStream implements Closeable {
+    private final String origCollection;
+    private final SolrParams origParams;
+    private final OutputStreamRequestContent content;
+    private final InputStreamResponseListener responseListener;
+    private final boolean isXml;
+
+    public OutStream(
+        String origCollection,
+        SolrParams origParams,
+        OutputStreamRequestContent content,
+        InputStreamResponseListener responseListener,
+        boolean isXml) {
+      this.origCollection = origCollection;
+      this.origParams = origParams;
+      this.content = content;
+      this.responseListener = responseListener;
+      this.isXml = isXml;
+    }
+
+    boolean belongToThisStream(SolrRequest<?> solrRequest, String collection) {
+      return origParams.equals(solrRequest.getParams())
+          && Objects.equals(origCollection, collection);
+    }
+
+    public void write(byte[] b) throws IOException {
+      this.content.getOutputStream().write(b);
+    }
+
+    public void flush() throws IOException {
+      this.content.getOutputStream().flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+      if (isXml) {
+        write("</stream>".getBytes(FALLBACK_CHARSET));
+      }
+      this.content.getOutputStream().close();
+    }
+
+    // TODO this class should be hidden
+    public InputStreamResponseListener getResponseListener() {
+      return responseListener;
+    }
+  }
+
+  private OutStream initOutStream(String baseUrl, UpdateRequest updateRequest, String collection)
+      throws IOException {
+    String contentType = client.requestWriter.getUpdateContentType();
+    final SolrParams origParams = updateRequest.getParams();
+    ModifiableSolrParams requestParams =
+        client.initializeSolrParams(updateRequest, client.responseParser(updateRequest));
+
+    String basePath = baseUrl;
+    if (collection != null) basePath += "/" + collection;
+    if (!basePath.endsWith("/")) basePath += "/";
+
+    OutputStreamRequestContent content = new OutputStreamRequestContent(contentType);
+    Request postRequest =
+        client
+            .getHttpClient()
+            .newRequest(basePath + "update" + requestParams.toQueryString())
+            .method(HttpMethod.POST)
+            .body(content);
+    client.decorateRequest(postRequest, updateRequest, false);
+    InputStreamResponseListener responseListener =
+        new Http2SolrClient.InputStreamReleaseTrackingResponseListener();
+    postRequest.send(responseListener);
+
+    boolean isXml = ClientUtils.TEXT_XML.equals(client.requestWriter.getUpdateContentType());
+    OutStream outStream = new OutStream(collection, origParams, content, responseListener, isXml);
+    if (isXml) {
+      outStream.write("<stream>".getBytes(FALLBACK_CHARSET));
+    }
+    return outStream;
+  }
+
+  private void send(OutStream outStream, SolrRequest<?> req, String collection) throws IOException {
+    assert outStream.belongToThisStream(req, collection);
+    client.requestWriter.write(req, outStream.content.getOutputStream());
+    if (outStream.isXml) {
+      // check for commit or optimize
+      SolrParams params = req.getParams();
+      assert params != null : "params should not be null";
+      if (params != null) {
+        String fmt = null;
+        if (params.getBool(UpdateParams.OPTIMIZE, false)) {
+          fmt = "<optimize waitSearcher=\"%s\" />";
+        } else if (params.getBool(UpdateParams.COMMIT, false)) {
+          fmt = "<commit waitSearcher=\"%s\" />";
+        }
+        if (fmt != null) {
+          byte[] content =
+              String.format(
+                      Locale.ROOT, fmt, params.getBool(UpdateParams.WAIT_SEARCHER, false) + "")
+                  .getBytes(FALLBACK_CHARSET);
+          outStream.write(content);
+        }
+      }
+    }
+    outStream.flush();
+  }
+}
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 d8005979e90..7a377946811 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
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.client.solrj.impl;
 
-import java.io.Closeable;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -29,7 +28,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -49,13 +47,9 @@ import org.apache.solr.client.solrj.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.HttpListenerFactory.RequestResponseListener;
 import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.RequestWriter;
-import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.util.ClientUtils;
 import org.apache.solr.common.params.ModifiableSolrParams;
-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.EnvUtils;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.ObjectReleaseTracker;
@@ -239,10 +233,8 @@ public class Http2SolrClient extends HttpSolrClientBase {
             : sslConfig.createClientContextFactory();
 
     Long keyStoreReloadIntervalSecs = builder.keyStoreReloadIntervalSecs;
-    if (keyStoreReloadIntervalSecs == null
-        && EnvUtils.getPropertyAsBool("solr.keystore.reload.enabled", false)) {
-      keyStoreReloadIntervalSecs =
-          EnvUtils.getPropertyAsLong("solr.jetty.ssl.context.reload.scan.interval.secs", 30l);
+    if (keyStoreReloadIntervalSecs == null && Boolean.getBoolean("solr.keyStoreReload.enabled")) {
+      keyStoreReloadIntervalSecs = Long.getLong("solr.jetty.sslContext.reload.scanInterval", 30);
     }
     if (sslContextFactory != null
         && sslContextFactory.getKeyStoreResource() != null
@@ -381,112 +373,10 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   /** (visible for testing) */
-  public long getIdleTimeout() {
+  public long getIdleTimeoutMillis() {
     return idleTimeoutMillis;
   }
 
-  public static class OutStream implements Closeable {
-    private final String origCollection;
-    private final SolrParams origParams;
-    private final OutputStreamRequestContent content;
-    private final InputStreamResponseListener responseListener;
-    private final boolean isXml;
-
-    public OutStream(
-        String origCollection,
-        SolrParams origParams,
-        OutputStreamRequestContent content,
-        InputStreamResponseListener responseListener,
-        boolean isXml) {
-      this.origCollection = origCollection;
-      this.origParams = origParams;
-      this.content = content;
-      this.responseListener = responseListener;
-      this.isXml = isXml;
-    }
-
-    boolean belongToThisStream(SolrRequest<?> solrRequest, String collection) {
-      return origParams.equals(solrRequest.getParams())
-          && Objects.equals(origCollection, collection);
-    }
-
-    public void write(byte[] b) throws IOException {
-      this.content.getOutputStream().write(b);
-    }
-
-    public void flush() throws IOException {
-      this.content.getOutputStream().flush();
-    }
-
-    @Override
-    public void close() throws IOException {
-      if (isXml) {
-        write("</stream>".getBytes(FALLBACK_CHARSET));
-      }
-      this.content.getOutputStream().close();
-    }
-
-    // TODO this class should be hidden
-    public InputStreamResponseListener getResponseListener() {
-      return responseListener;
-    }
-  }
-
-  public OutStream initOutStream(String baseUrl, UpdateRequest updateRequest, String collection)
-      throws IOException {
-    String contentType = requestWriter.getUpdateContentType();
-    final SolrParams origParams = updateRequest.getParams();
-    ModifiableSolrParams requestParams =
-        initializeSolrParams(updateRequest, responseParser(updateRequest));
-
-    String basePath = baseUrl;
-    if (collection != null) basePath += "/" + collection;
-    if (!basePath.endsWith("/")) basePath += "/";
-
-    OutputStreamRequestContent content = new OutputStreamRequestContent(contentType);
-    Request postRequest =
-        httpClient
-            .newRequest(basePath + "update" + requestParams.toQueryString())
-            .method(HttpMethod.POST)
-            .body(content);
-    decorateRequest(postRequest, updateRequest, false);
-    InputStreamResponseListener responseListener = new InputStreamReleaseTrackingResponseListener();
-    postRequest.send(responseListener);
-
-    boolean isXml = ClientUtils.TEXT_XML.equals(requestWriter.getUpdateContentType());
-    OutStream outStream = new OutStream(collection, origParams, content, responseListener, isXml);
-    if (isXml) {
-      outStream.write("<stream>".getBytes(FALLBACK_CHARSET));
-    }
-    return outStream;
-  }
-
-  public void send(OutStream outStream, SolrRequest<?> req, String collection) throws IOException {
-    assert outStream.belongToThisStream(req, collection);
-    this.requestWriter.write(req, outStream.content.getOutputStream());
-    if (outStream.isXml) {
-      // check for commit or optimize
-      SolrParams params = req.getParams();
-      assert params != null : "params should not be null";
-      if (params != null) {
-        String fmt = null;
-        if (params.getBool(UpdateParams.OPTIMIZE, false)) {
-          fmt = "<optimize waitSearcher=\"%s\" />";
-        } else if (params.getBool(UpdateParams.COMMIT, false)) {
-          fmt = "<commit waitSearcher=\"%s\" />";
-        }
-        if (fmt != null) {
-          byte[] content =
-              String.format(
-                      Locale.ROOT, fmt, params.getBool(UpdateParams.WAIT_SEARCHER, false) + "")
-                  .getBytes(FALLBACK_CHARSET);
-          outStream.write(content);
-        }
-      }
-    }
-    outStream.flush();
-  }
-
   @Override
   public CompletableFuture<NamedList<Object>> requestAsync(
       final SolrRequest<?> solrRequest, String collection) {
@@ -614,6 +504,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
   }
 
   @Override
+  @Deprecated
   public NamedList<Object> requestWithBaseUrl(
       String baseUrl, SolrRequest<?> solrRequest, String collection)
       throws SolrServerException, IOException {
@@ -687,7 +578,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
   }
 
-  private void decorateRequest(Request req, SolrRequest<?> solrRequest, boolean isAsync) {
+  protected void decorateRequest(Request req, SolrRequest<?> solrRequest, boolean isAsync) {
     req.headers(headers -> headers.remove(HttpHeader.ACCEPT_ENCODING));
     req.idleTimeout(idleTimeoutMillis, TimeUnit.MILLISECONDS);
     req.timeout(requestTimeoutMillis, TimeUnit.MILLISECONDS);
@@ -1046,7 +937,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
       if (cookieStore == null) {
         return cookieStore;
       }
-      if (!EnvUtils.getPropertyAsBool("solr.solrj.http.cookies.enabled", false)) {
+      if (Boolean.getBoolean("solr.http.disableCookies")) {
         return new HttpCookieStore.Empty();
       }
       /*
@@ -1168,8 +1059,7 @@ public class Http2SolrClient extends HttpSolrClientBase {
    *
    * @see ObjectReleaseTracker
    */
-  private static class InputStreamReleaseTrackingResponseListener
-      extends InputStreamResponseListener {
+  static class InputStreamReleaseTrackingResponseListener extends InputStreamResponseListener {
 
     @Override
     public InputStream getInputStream() {
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/StallDetection.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/StallDetection.java
index 2530e5c0fb8..4d8f6737e13 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/StallDetection.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/StallDetection.java
@@ -25,9 +25,9 @@ import java.util.function.IntSupplier;
 /**
  * Utility class for detecting stalls in request processing.
  *
- * <p>This class is used by {@link ConcurrentUpdateHttp2SolrClient} to detect when request
- * processing has stalled, which can happen if the server is unresponsive or if there's a problem
- * with the connection.
+ * <p>This class is used by {@link ConcurrentUpdateBaseSolrClient} to detect when request processing
+ * has stalled, which can happen if the server is unresponsive or if there's a problem with the
+ * connection.
  */
 public class StallDetection {
   private final LongAdder processedCount;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
index 81e9aca90bb..46369c1f0cd 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingBinaryHttp2Test.java
@@ -23,7 +23,6 @@ import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.StreamingResponseCallback;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
 import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
@@ -44,7 +43,7 @@ public class SolrExampleStreamingBinaryHttp2Test extends SolrExampleStreamingHtt
             .withRequestWriter(new JavaBinRequestWriter())
             .withResponseParser(new JavaBinResponseParser())
             .build();
-    ConcurrentUpdateHttp2SolrClient concurrentClient =
+    var concurrentClient =
         new ErrorTrackingConcurrentUpdateSolrClient.Builder(url, solrClient)
             .withDefaultCollection(DEFAULT_TEST_CORENAME)
             .withQueueSize(2)
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
index 00087381f1f..cf7bc4bd10e 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleStreamingHttp2Test.java
@@ -23,7 +23,7 @@ import java.util.EnumSet;
 import java.util.List;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrExampleTests;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateJettySolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.XMLRequestWriter;
 import org.apache.solr.client.solrj.impl.XMLResponseParser;
@@ -52,7 +52,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
             .withRequestWriter(new XMLRequestWriter())
             .withResponseParser(new XMLResponseParser())
             .build();
-    ConcurrentUpdateHttp2SolrClient concurrentClient =
+    var concurrentClient =
         new ErrorTrackingConcurrentUpdateSolrClient.Builder(url, solrClient)
             .withDefaultCollection(DEFAULT_TEST_CORENAME)
             .withQueueSize(2)
@@ -66,7 +66,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
     final List<Throwable> failures = new ArrayList<>();
     final String serverUrl = getBaseUrl();
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
+        var concurrentClient =
             new FailureRecordingConcurrentUpdateSolrClient.Builder(serverUrl, http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
                 .withQueueSize(2)
@@ -94,10 +94,11 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
     }
   }
 
-  static class FailureRecordingConcurrentUpdateSolrClient extends ConcurrentUpdateHttp2SolrClient {
+  static class FailureRecordingConcurrentUpdateSolrClient extends ConcurrentUpdateJettySolrClient {
     private final List<Throwable> failures = new ArrayList<>();
 
-    public FailureRecordingConcurrentUpdateSolrClient(Builder builder) {
+    public FailureRecordingConcurrentUpdateSolrClient(
+        ConcurrentUpdateJettySolrClient.Builder builder) {
       super(builder);
     }
 
@@ -106,7 +107,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
       failures.add(ex);
     }
 
-    static class Builder extends ConcurrentUpdateHttp2SolrClient.Builder {
+    static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
       public Builder(String baseSolrUrl, Http2SolrClient http2Client) {
         super(baseSolrUrl, http2Client);
       }
@@ -119,10 +120,11 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
   }
 
   public static class ErrorTrackingConcurrentUpdateSolrClient
-      extends ConcurrentUpdateHttp2SolrClient {
+      extends ConcurrentUpdateJettySolrClient {
     public Throwable lastError = null;
 
-    public ErrorTrackingConcurrentUpdateSolrClient(Builder builder) {
+    public ErrorTrackingConcurrentUpdateSolrClient(
+        ConcurrentUpdateJettySolrClient.Builder builder) {
       super(builder);
     }
 
@@ -131,7 +133,7 @@ public class SolrExampleStreamingHttp2Test extends SolrExampleTests {
       lastError = ex;
     }
 
-    public static class Builder extends ConcurrentUpdateHttp2SolrClient.Builder {
+    public static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
 
       public Builder(String baseSolrUrl, Http2SolrClient http2Client) {
         super(baseSolrUrl, http2Client, true);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientBadInputTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java
similarity index 93%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientBadInputTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java
index 1b21772d88c..e6adb0d0987 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientBadInputTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientBadInputTest.java
@@ -20,12 +20,11 @@ package org.apache.solr.client.solrj.impl;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.solr.SolrJettyTestBase;
-import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.embedded.JettyConfig;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-public class ConcurrentUpdateHttp2SolrClientBadInputTest extends SolrJettyTestBase {
+public class ConcurrentUpdateJettySolrClientBadInputTest extends SolrJettyTestBase {
   private static final List<String> NULL_STR_LIST = null;
   private static final List<String> EMPTY_STR_LIST = new ArrayList<>();
   private static final String ANY_COLLECTION = "ANY_COLLECTION";
@@ -42,8 +41,8 @@ public class ConcurrentUpdateHttp2SolrClientBadInputTest extends SolrJettyTestBa
   public void testDeleteByIdReportsInvalidIdLists() throws Exception {
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        SolrClient client =
-            new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client)
+        var client =
+            new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(ANY_COLLECTION)
                 .withQueueSize(ANY_QUEUE_SIZE)
                 .withThreadCount(ANY_MAX_NUM_THREADS)
@@ -75,8 +74,8 @@ public class ConcurrentUpdateHttp2SolrClientBadInputTest extends SolrJettyTestBa
     }
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        SolrClient client =
-            new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client)
+        var client =
+            new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(ANY_COLLECTION)
                 .withQueueSize(ANY_QUEUE_SIZE)
                 .withThreadCount(ANY_MAX_NUM_THREADS)
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientMultiCollectionTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
similarity index 88%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientMultiCollectionTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
index b5a0526a4db..ef8ae2927cc 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientMultiCollectionTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientMultiCollectionTest.java
@@ -21,7 +21,6 @@ import java.io.IOException;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.SolrInputDocument;
@@ -32,11 +31,10 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 /**
- * {@link ConcurrentUpdateSolrClient} reuses the same HTTP connection to send multiple requests.
- * These tests ensure that this connection-reuse never results in documents being sent to the wrong
- * collection. See SOLR-12803
+ * CUSC reuses the same HTTP connection to send multiple requests. These tests ensure that this
+ * connection-reuse never results in documents being sent to the wrong collection. See SOLR-12803
  */
-public class ConcurrentUpdateHttp2SolrClientMultiCollectionTest extends SolrCloudTestCase {
+public class ConcurrentUpdateJettySolrClientMultiCollectionTest extends SolrCloudTestCase {
 
   private static final String COLLECTION_ONE_NAME = "collection1";
   private static final String COLLECTION_TWO_NAME = "collection2";
@@ -68,8 +66,8 @@ public class ConcurrentUpdateHttp2SolrClientMultiCollectionTest extends SolrClou
     int numTotalDocs = 1000;
     int numExpectedPerCollection = numTotalDocs / 2;
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        SolrClient client =
-            new ConcurrentUpdateHttp2SolrClient.Builder(solrUrl, http2Client)
+        var client =
+            new ConcurrentUpdateJettySolrClient.Builder(solrUrl, http2Client)
                 .withQueueSize(numTotalDocs)
                 .build()) {
       splitDocumentsAcrossCollections(client, numTotalDocs);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java
similarity index 94%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java
index d202834dd83..39a1e75e400 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateJettySolrClientTest.java
@@ -52,7 +52,7 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
+public class ConcurrentUpdateJettySolrClientTest extends SolrJettyTestBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   /** Mock endpoint where the CUSS being tested in this class sends requests. */
@@ -189,7 +189,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     final StringBuilder errors = new StringBuilder();
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
+        var concurrentClient =
             new OutcomeCountingConcurrentUpdateSolrClient.Builder(
                     serverUrl, http2Client, successCounter, errorCounter, errors)
                 .withQueueSize(cussQueueSize)
@@ -247,8 +247,8 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     int cussQueueSize = 10;
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
-            (new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client))
+        var concurrentClient =
+            (new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client))
                 .withQueueSize(cussQueueSize)
                 .withThreadCount(cussThreadCount)
                 .build()) {
@@ -267,8 +267,8 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     }
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
-            new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client)
+        var concurrentClient =
+            new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
                 .withQueueSize(cussQueueSize)
                 .withThreadCount(cussThreadCount)
@@ -289,8 +289,8 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     int expected = numDocs * numRunnables;
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
-            new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client)
+        var concurrentClient =
+            new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withQueueSize(cussQueueSize)
                 .withThreadCount(cussThreadCount)
                 .setPollQueueTime(0, TimeUnit.MILLISECONDS)
@@ -326,8 +326,8 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     }
 
     try (Http2SolrClient http2Client = new Http2SolrClient.Builder().build();
-        ConcurrentUpdateHttp2SolrClient concurrentClient =
-            new ConcurrentUpdateHttp2SolrClient.Builder(getBaseUrl(), http2Client)
+        var concurrentClient =
+            new ConcurrentUpdateJettySolrClient.Builder(getBaseUrl(), http2Client)
                 .withDefaultCollection(DEFAULT_TEST_CORENAME)
                 .withQueueSize(cussQueueSize)
                 .withThreadCount(cussThreadCount)
@@ -338,7 +338,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
     }
   }
 
-  static class OutcomeCountingConcurrentUpdateSolrClient extends ConcurrentUpdateHttp2SolrClient {
+  static class OutcomeCountingConcurrentUpdateSolrClient extends ConcurrentUpdateJettySolrClient {
     private final AtomicInteger successCounter;
     private final AtomicInteger failureCounter;
     private final StringBuilder errors;
@@ -362,7 +362,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
       successCounter.incrementAndGet();
     }
 
-    static class Builder extends ConcurrentUpdateHttp2SolrClient.Builder {
+    static class Builder extends ConcurrentUpdateJettySolrClient.Builder {
       protected final AtomicInteger successCounter;
       protected final AtomicInteger failureCounter;
       protected final StringBuilder errors;
@@ -397,7 +397,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
         var http2Client =
             new Http2SolrClient.Builder().withIdleTimeout(1, TimeUnit.MILLISECONDS).build();
         var client =
-            new ConcurrentUpdateHttp2SolrClient.Builder(
+            new ConcurrentUpdateJettySolrClient.Builder(
                     "http://"
                         + localHost.getHostAddress()
                         + ":"
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index d95ef9dfbfa..856e2103abe 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -671,7 +671,8 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
 
       try (Http2SolrClient onlyBaseUrlChangedClient =
           new Http2SolrClient.Builder(url).withHttpClient(oldClient).build()) {
-        assertEquals(oldClient.getIdleTimeout(), onlyBaseUrlChangedClient.getIdleTimeout());
+        assertEquals(
+            oldClient.getIdleTimeoutMillis(), onlyBaseUrlChangedClient.getIdleTimeoutMillis());
         assertEquals(oldClient.getHttpClient(), onlyBaseUrlChangedClient.getHttpClient());
       }
 
@@ -688,8 +689,9 @@ public class Http2SolrClientTest extends HttpSolrClientTestBase {
               .withHttpClient(oldClient)
               .withIdleTimeout(newIdleTimeoutMs, TimeUnit.MILLISECONDS)
               .build()) {
-        assertNotEquals(oldClient.getIdleTimeout(), idleTimeoutChangedClient.getIdleTimeout());
-        assertEquals(newIdleTimeoutMs, idleTimeoutChangedClient.getIdleTimeout());
+        assertNotEquals(
+            oldClient.getIdleTimeoutMillis(), idleTimeoutChangedClient.getIdleTimeoutMillis());
+        assertEquals(newIdleTimeoutMs, idleTimeoutChangedClient.getIdleTimeoutMillis());
         NamedList<Object> response = idleTimeoutChangedClient.request(req);
         try (InputStream is = (InputStream) response.get("stream")) {
           String expect =
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClient.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClient.java
index d81314c05c8..09cdc17b51c 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClient.java
@@ -41,7 +41,7 @@ import org.apache.http.entity.EntityTemplate;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.impl.ConcurrentUpdateHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.ConcurrentUpdateBaseSolrClient;
 import org.apache.solr.client.solrj.impl.StallDetection;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.util.ClientUtils;
@@ -70,7 +70,7 @@ import org.slf4j.MDC;
  * to use ConcurrentUpdateSolrClient with /update requests. The class {@link HttpSolrClient} is
  * better suited for the query interface.
  *
- * @deprecated Please use {@link ConcurrentUpdateHttp2SolrClient}
+ * @deprecated Please use {@link ConcurrentUpdateBaseSolrClient}
  */
 @Deprecated(since = "9.0")
 public class ConcurrentUpdateSolrClient extends SolrClient {


(solr) 12/12: SOLR-17996: improve documentation, etc.

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit a6be194632f7238c8fcf2acb1c170599d3c7bf1e
Author: David Smiley <ds...@apache.org>
AuthorDate: Sun Nov 30 15:32:35 2025 -0500

    SOLR-17996: improve documentation, etc.
---
 changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml       |  2 +-
 .../src/java/org/apache/solr/client/solrj/SolrRequest.java   | 12 ++++++++++--
 .../java/org/apache/solr/client/solrj/impl/LBSolrClient.java |  7 +++----
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml b/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
index 99c4c8c40e9..814187a7635 100644
--- a/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
+++ b/changelog/unreleased/SOLR-17996-requestWithBaseUrl.yml
@@ -1,5 +1,5 @@
 # See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
-title: New SolrJ SolrRequest.requestWithBaseUrl, new HttpSolrClientBase.requestWithBaseUrl
+title: New SolrJ SolrRequest.requestWithBaseUrl, new HttpSolrClientBase.processWithBaseUrl. Existing callers will require updates due to a signature change.
 type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
 authors:
   - name: David Smiley
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
index b3a4eee893e..22cd231552e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
@@ -293,13 +293,21 @@ public abstract class SolrRequest<T> implements Serializable {
   }
 
   /**
+   * Send this request to a {@link SolrClient} and return the response
+   *
+   * @param client the SolrClient to communicate with
+   * @param baseUrl the base URL, e.g. {@code Http://localhost:8983/solr}
+   * @param collection the collection to execute the request against
+   * @return the response
+   * @throws SolrServerException if there is an error on the Solr server
+   * @throws IOException if there is a communication error
    * @lucene.experimental
    */
-  public final T processWithBaseUrl(HttpSolrClientBase client, String url, String collection)
+  public final T processWithBaseUrl(HttpSolrClientBase client, String baseUrl, String collection)
       throws SolrServerException, IOException {
     // duplicative with process(), except for requestWithBaseUrl
     long startNanos = System.nanoTime();
-    var namedList = client.requestWithBaseUrl(url, this, collection);
+    var namedList = client.requestWithBaseUrl(baseUrl, this, collection);
     long endNanos = System.nanoTime();
     T typedResponse = createResponse(namedList);
     // SolrResponse is pre-V2 API
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
index 920b3c49821..2bfa5ad5f00 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
@@ -614,10 +614,9 @@ public abstract class LBSolrClient extends SolrClient {
   private NamedList<Object> doRequest(
       SolrClient solrClient, String baseUrl, String collection, SolrRequest<?> solrRequest)
       throws SolrServerException, IOException {
-    // Some implementations of LBSolrClient.getClient(...) return a HttpJettySolrClient that may not
-    // be
-    // pointed at the desired URL (or any URL for that matter).  We special case that here to ensure
-    // the appropriate URL is provided.
+    // Some implementations of LBSolrClient.getClient(...) return a HttpSolrClientBase that may not
+    // be pointed at the desired URL (or any URL for that matter).  We special-case that here to
+    // ensure the appropriate URL is provided.
     if (solrClient instanceof HttpSolrClientBase hasReqWithUrl) {
       return hasReqWithUrl.requestWithBaseUrl(baseUrl, solrRequest, collection);
     }


(solr) 02/12: Tests: move ServletFixtures to test-framework

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 83aceb9aaa5e55372e317253282f78f750beff24
Author: David Smiley <ds...@apache.org>
AuthorDate: Mon Nov 24 21:31:49 2025 -0500

    Tests: move ServletFixtures to test-framework
---
 gradle/documentation/render-javadoc.gradle                               | 1 +
 .../src/java}/org/apache/solr/client/solrj/impl/ServletFixtures.java     | 0
 2 files changed, 1 insertion(+)

diff --git a/gradle/documentation/render-javadoc.gradle b/gradle/documentation/render-javadoc.gradle
index 65ebe7b517c..6102b39a06d 100644
--- a/gradle/documentation/render-javadoc.gradle
+++ b/gradle/documentation/render-javadoc.gradle
@@ -150,6 +150,7 @@ configure(project(":solr:test-framework")) {
     javadocMissingIgnore = [
         "org.apache.solr",
         "org.apache.solr.analysis",
+        "org.apache.solr.client.solrj.impl",
         "org.apache.solr.cloud",
         "org.apache.solr.cloud.api.collections",
         "org.apache.solr.common.cloud",
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ServletFixtures.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/impl/ServletFixtures.java
similarity index 100%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ServletFixtures.java
rename to solr/test-framework/src/java/org/apache/solr/client/solrj/impl/ServletFixtures.java


(solr) 05/12: build: print broken links

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit f957a72affad82fdec112abcfc5b3a35ef8f096c
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Nov 27 09:52:54 2025 -0500

    build: print broken links
---
 gradle/validation/check-broken-links.gradle | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/gradle/validation/check-broken-links.gradle b/gradle/validation/check-broken-links.gradle
index 885e1a75758..a2e3e01f112 100644
--- a/gradle/validation/check-broken-links.gradle
+++ b/gradle/validation/check-broken-links.gradle
@@ -64,6 +64,10 @@ class CheckBrokenLinksTask extends DefaultTask {
     }
 
     if (result.getExitValue() != 0) {
+      println "Broken links check failed. First lines of the output:"
+      outputFile.get().asFile.readLines().take(20).each { line ->
+        println line
+      }
       throw new GradleException("Broken links check failed. Command output at: ${outputFile.get()}")
     }
   }


(solr) 09/12: SOLR-17161: LBHttp2SolrClient is now LBAsyncSolrClient (#3882)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 1f74749a3e98326ddae296b63e4f24cd69dee167
Author: David Smiley <ds...@apache.org>
AuthorDate: Fri Nov 28 10:55:46 2025 -0500

    SOLR-17161: LBHttp2SolrClient is now LBAsyncSolrClient (#3882)
    
    * renamed LBHttp2SolrClient to LBAsyncSolrClient because it's code is mostly for requestAsync.
    * LBAsyncSolrClient is abstract
    * new LBJettySolrClient extends LBAsyncSolrClient; only used by HttpShardHandler
    * LBSolrClient can now be used directly easily, albeit technically remains abstract; took the builder code formerly in LBHttp2SolrClient
    * CloudHttp2SolrClient internally now only needs an LBSolrClient; not LBHttp2SolrClient (no requestAsync needed).
    
    SOLR-17161 (a first part of this issue, adds a "jetty" package)
---
 .../unreleased/SOLR-17161-LBAsyncSolrClient.yml    |   8 ++
 .../solr/handler/component/HttpShardHandler.java   |   7 +-
 .../handler/component/HttpShardHandlerFactory.java |   7 +-
 .../modules/deployment-guide/pages/solrj.adoc      |   4 +-
 .../client/solrj/impl/CloudHttp2SolrClient.java    |  12 +-
 .../solr/client/solrj/impl/Http2SolrClient.java    |   6 +
 .../solr/client/solrj/impl/HttpJdkSolrClient.java  |   5 +
 .../solr/client/solrj/impl/HttpSolrClientBase.java |   5 +
 ...Http2SolrClient.java => LBAsyncSolrClient.java} | 143 +++------------------
 .../solr/client/solrj/impl/LBSolrClient.java       | 113 +++++++++++++++-
 .../solr/client/solrj/jetty/LBJettySolrClient.java |  54 ++++++++
 .../solr/client/solrj/jetty/package-info.java      |  19 +++
 .../impl/CloudHttp2SolrClientBuilderTest.java      |   2 +-
 ...IntegrationTest.java => LB2SolrClientTest.java} |  19 +--
 ...rClientTest.java => LBAsyncSolrClientTest.java} |  53 ++------
 .../solr/client/solrj/apache/LBHttpSolrClient.java |   3 +-
 16 files changed, 260 insertions(+), 200 deletions(-)

diff --git a/changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml b/changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml
new file mode 100644
index 00000000000..f722b2ffb46
--- /dev/null
+++ b/changelog/unreleased/SOLR-17161-LBAsyncSolrClient.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: LBAsyncSolrClient (renamed from LBHttp2SolrClient) & LBSolrClient (improved) & LBJettySolrClient (new, clarifying impl).
+type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+authors:
+  - name: David Smiley
+links:
+  - name: SOLR-17161
+    url: https://issues.apache.org/jira/browse/SOLR-17161
diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
index b58da55859a..788d985a79e 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
@@ -33,8 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import net.jcip.annotations.NotThreadSafe;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
-import org.apache.solr.client.solrj.impl.LBHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.LBAsyncSolrClient;
 import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.routing.NoOpReplicaListTransformer;
@@ -98,7 +97,7 @@ public class HttpShardHandler extends ShardHandler {
   private final AtomicBoolean canceled = new AtomicBoolean(false);
 
   private final Map<String, List<String>> shardToURLs;
-  protected LBHttp2SolrClient<Http2SolrClient> lbClient;
+  protected LBAsyncSolrClient lbClient;
 
   public HttpShardHandler(HttpShardHandlerFactory httpShardHandlerFactory) {
     this.httpShardHandlerFactory = httpShardHandlerFactory;
@@ -247,7 +246,7 @@ public class HttpShardHandler extends ShardHandler {
    * @param sreq the request to make
    * @param shard the shard to address
    * @param params request parameters
-   * @param lbReq the load balanced request suitable for LBHttp2SolrClient
+   * @param lbReq the load balanced request
    * @param ssr the response collector part 1
    * @param srsp the shard response collector
    * @param startTimeNS the time at which the request was initiated, likely just prior to calling
diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
index 0a78e179019..0daec7a4825 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java
@@ -32,9 +32,10 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
-import org.apache.solr.client.solrj.impl.LBHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.LBAsyncSolrClient;
 import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.client.solrj.impl.SolrHttpConstants;
+import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.routing.AffinityReplicaListTransformerFactory;
 import org.apache.solr.client.solrj.routing.ReplicaListTransformer;
@@ -82,7 +83,7 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory
 
   protected volatile Http2SolrClient defaultClient;
   protected InstrumentedHttpListenerFactory httpListenerFactory;
-  protected LBHttp2SolrClient<Http2SolrClient> loadbalancer;
+  protected LBAsyncSolrClient loadbalancer;
 
   int corePoolSize = 0;
   int maximumPoolSize = Integer.MAX_VALUE;
@@ -307,7 +308,7 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory
             .withMaxConnectionsPerHost(maxConnectionsPerHost)
             .build();
     this.defaultClient.addListenerFactory(this.httpListenerFactory);
-    this.loadbalancer = new LBHttp2SolrClient.Builder<Http2SolrClient>(defaultClient).build();
+    this.loadbalancer = new LBJettySolrClient.Builder(defaultClient).build();
 
     initReplicaListTransformers(getParameter(args, "replicaRouting", null, sb));
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
index e61f17567f7..5582621ec7b 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/solrj.adoc
@@ -97,7 +97,7 @@ Requests are sent in the form of {solr-javadocs}/solrj/org/apache/solr/client/so
 
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/Http2SolrClient.html[`Http2SolrClient`] - a general purpose client based on Jetty HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Most used & tested.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.html[`HttpJdkSolrClient`] - a general purpose client based on JDK HttpClient.  Supports HTTP/2 and HTTP/1.1, async, non-blocking.  Has no dependencies.
-- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.html[`LBHttp2SolrClient`] - an internal client that delegates to other clients pointed at different URLs for fail-over/availability.  Adjusts the list of "in-service" nodes based on node health.
+- {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/LBSolrClient.html[`LBSolrClient`] - an internal client that delegates to other clients pointed at different URLs for fail-over/availability.  Adjusts the list of "in-service" nodes based on node health.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/CloudSolrClient.html[`CloudSolrClient`] - the ideal client for SolrCloud.  Using the "cluster state", it routes requests to the optimal nodes, including splitting out the documents in an UpdateRequest to different nodes.
 - {solr-javadocs}/solrj/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClient.html[`ConcurrentUpdateHttp2SolrClient`] - geared towards indexing-centric workloads.
 Buffers documents internally before sending larger batches to Solr.
@@ -116,7 +116,7 @@ A few notable exceptions to this are described below:
 
 - *Http2SolrClient* - Users of `Http2SolrClient` may choose to skip providing a root URL to their client, in favor of specifying the URL as an argument for the `Http2SolrClient.requestWithBaseUrl` method.
 Calling any other `request` methods on a URL-less `Http2SolrClient` will result in an `IllegalArgumentException`.
-- *LBHttp2SolrClient* - Solr's "load balancing" client is frequently used to round-robin requests across a set of replicas or cores.
+- *LBSolrClient* - Solr's "load balancing" client is frequently used to round-robin requests across a set of replicas or cores.
 URLs are still expected to point to the Solr root (i.e. "/solr"), but to support this use-case the URLs are often supplemented by an additional parameter to specify the targeted core.
 Alternatively, some "load balancing" methods make use of an `Endpoint` abstraction to provide this URL and core information in a more structured way.
 - *CloudSolrClient* - Like many clients, CloudSolrClient accepts a series of URLs pointing to the Solr root path (i.e. "/solr").
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
index 4fc82b630d3..6886952c25c 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudHttp2SolrClient.java
@@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory;
 /**
  * SolrJ client class to communicate with SolrCloud using an Http/2-capable Solr Client. Instances
  * of this class communicate with Zookeeper to discover Solr endpoints for SolrCloud collections,
- * and then use the {@link LBHttp2SolrClient} to issue requests.
+ * and then use the {@link LBSolrClient} to issue requests.
  *
  * @since solr 8.0
  */
@@ -44,7 +44,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private final ClusterStateProvider stateProvider;
-  private final LBHttp2SolrClient<HttpSolrClientBase> lbClient;
+  private final LBSolrClient lbClient;
   private final HttpSolrClientBase myClient;
   private final boolean clientIsInternal;
 
@@ -90,7 +90,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
     // locks.
     this.locks = objectList(builder.parallelCacheRefreshesLocks);
 
-    this.lbClient = new LBHttp2SolrClient.Builder<>(myClient).build();
+    this.lbClient = builder.createOrGetLbClient(myClient);
   }
 
   private HttpSolrClientBase createOrGetHttpClientFromBuilder(Builder builder) {
@@ -160,7 +160,7 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
   }
 
   @Override
-  public LBHttp2SolrClient<?> getLbClient() {
+  public LBSolrClient getLbClient() {
     return lbClient;
   }
 
@@ -452,5 +452,9 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
 
       return new CloudHttp2SolrClient(this);
     }
+
+    protected LBSolrClient createOrGetLbClient(HttpSolrClientBase myClient) {
+      return myClient.createLBSolrClient();
+    }
   }
 }
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 83b24671bbe..d8005979e90 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
@@ -47,6 +47,7 @@ import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.HttpListenerFactory.RequestResponseListener;
+import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.util.ClientUtils;
@@ -638,6 +639,11 @@ public class Http2SolrClient extends HttpSolrClientBase {
     }
   }
 
+  @Override
+  protected LBSolrClient createLBSolrClient() {
+    return new LBJettySolrClient.Builder(this).build();
+  }
+
   @Override
   public HttpSolrClientBuilderBase<?, ?> builder() {
     return new Http2SolrClient.Builder().withHttpClient(this);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
index d65a8fb9681..4e0472cc2d8 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpJdkSolrClient.java
@@ -554,6 +554,11 @@ public class HttpJdkSolrClient extends HttpSolrClientBase {
     return new HttpJdkSolrClient.Builder().withHttpClient(this);
   }
 
+  @Override
+  protected LBSolrClient createLBSolrClient() {
+    return new LBSolrClient.Builder<>(this).build();
+  }
+
   public static class Builder
       extends HttpSolrClientBuilderBase<HttpJdkSolrClient.Builder, HttpJdkSolrClient> {
 
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
index 0fa2936cbfc..ffed7779fb4 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClientBase.java
@@ -101,6 +101,11 @@ public abstract class HttpSolrClientBase extends SolrClient {
 
   public abstract HttpSolrClientBuilderBase<?, ?> builder();
 
+  /**
+   * @lucene.internal
+   */
+  protected abstract LBSolrClient createLBSolrClient();
+
   protected String getRequestUrl(SolrRequest<?> solrRequest, String collection)
       throws MalformedURLException {
     return ClientUtils.buildRequestUrl(solrRequest, serverBaseUrl, collection);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBAsyncSolrClient.java
similarity index 58%
rename from solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java
rename to solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBAsyncSolrClient.java
index 89ffbd707a0..41225ffaf25 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBHttp2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBAsyncSolrClient.java
@@ -20,109 +20,32 @@ import java.io.IOException;
 import java.net.ConnectException;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
-import java.util.Arrays;
-import java.util.Set;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
-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.SolrRequest.SolrRequestType;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.request.RequestWriter;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.NamedList;
 import org.slf4j.MDC;
 
-/**
- * This "LoadBalanced Http Solr Client" is a load balancing wrapper around a Http Solr Client. This
- * is useful when you have multiple Solr endpoints and requests need to be Load Balanced among them.
- *
- * <p>Do <b>NOT</b> use this class for indexing in leader/follower scenarios since documents must be
- * sent to the correct leader; no inter-node routing is done.
- *
- * <p>In SolrCloud (leader/replica) scenarios, it is usually better to use {@link CloudSolrClient},
- * but this class may be used for updates because the server will forward them to the appropriate
- * leader.
- *
- * <p>It offers automatic failover when a server goes down, and it detects when the server comes
- * back up.
- *
- * <p>Load balancing is done using a simple round-robin on the list of endpoints. Endpoint URLs are
- * expected to point to the Solr "root" path (i.e. "/solr").
- *
- * <blockquote>
- *
- * <pre>
- * SolrClient client = new LBHttp2SolrClient.Builder(http2SolrClient,
- *         new LBSolrClient.Endpoint("http://host1:8080/solr"), new LBSolrClient.Endpoint("http://host2:8080/solr"))
- *     .build();
- * </pre>
- *
- * </blockquote>
- *
- * Users who wish to balance traffic across a specific set of replicas or cores may specify each
- * endpoint as a root-URL and core-name pair. For example:
- *
- * <blockquote>
- *
- * <pre>
- * SolrClient client = new LBHttp2SolrClient.Builder(http2SolrClient,
- *         new LBSolrClient.Endpoint("http://host1:8080/solr", "coreA"),
- *         new LBSolrClient.Endpoint("http://host2:8080/solr", "coreB"))
- *     .build();
- * </pre>
- *
- * </blockquote>
- *
- * <p>If a request to an endpoint fails by an IOException due to a connection timeout or read
- * timeout then the host is taken off the list of live endpoints and moved to a 'dead endpoint list'
- * and the request is resent to the next live endpoint. This process is continued till it tries all
- * the live endpoints. If at least one endpoint is alive, the request succeeds, and if not it fails.
- *
- * <p>Dead endpoints are periodically healthchecked on a fixed interval controlled by {@link
- * LBHttp2SolrClient.Builder#setAliveCheckInterval(int, TimeUnit)}. The default is set to one
- * minute.
- *
- * <p><b>When to use this?</b><br>
- * This can be used as a software load balancer when you do not wish to set up an external load
- * balancer. Alternatives to this code are to use a dedicated hardware load balancer or using Apache
- * httpd with mod_proxy_balancer as a load balancer. See <a
- * href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load balancing on Wikipedia</a>
- *
- * @since solr 8.0
- */
-public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClient {
+/** A {@link LBSolrClient} adding {@link #requestAsync(Req)}. */
+public abstract class LBAsyncSolrClient extends LBSolrClient {
+  // formerly known as LBHttp2SolrClient, using Http2SolrClient (jetty)
 
-  protected final C solrClient;
+  protected final HttpSolrClientBase solrClient;
 
-  @SuppressWarnings("unchecked")
-  private LBHttp2SolrClient(Builder<?> builder) {
-    super(Arrays.asList(builder.solrEndpoints));
-    this.solrClient = (C) builder.solrClient;
-    this.aliveCheckIntervalMillis = builder.aliveCheckIntervalMillis;
-    this.defaultCollection = builder.defaultCollection;
+  protected LBAsyncSolrClient(Builder<?> builder) {
+    super(builder);
+    this.solrClient = builder.getSolrClient();
   }
 
   @Override
-  protected SolrClient getClient(Endpoint endpoint) {
+  protected HttpSolrClientBase getClient(Endpoint endpoint) {
     return solrClient;
   }
 
-  @Override
-  public ResponseParser getParser() {
-    return solrClient.getParser();
-  }
-
-  @Override
-  public RequestWriter getRequestWriter() {
-    return solrClient.getRequestWriter();
-  }
-
-  public Set<String> getUrlParamNames() {
-    return solrClient.getUrlParamNames();
-  }
-
   /**
    * Execute an asynchronous request against one or more hosts for a given collection. The passed-in
    * Req object includes a List of Endpoints. This method always begins with the first Endpoint in
@@ -214,10 +137,9 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien
       RetryListener listener) {
     String baseUrl = endpoint.toString();
     rsp.server = baseUrl;
-    final var client = (Http2SolrClient) getClient(endpoint);
     try {
       CompletableFuture<NamedList<Object>> future =
-          client.requestWithBaseUrl(baseUrl, (c) -> c.requestAsync(req.getRequest()));
+          requestAsyncWithUrl(getClient(endpoint), baseUrl, req.getRequest());
       future.whenComplete(
           (result, throwable) -> {
             if (!future.isCompletedExceptionally()) {
@@ -228,11 +150,15 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien
           });
       return future;
     } catch (SolrServerException | IOException e) {
-      // Unreachable, since 'requestWithBaseUrl' above is running the request asynchronously
+      // Unreachable, since 'requestAsyncWithUrl' above is running the request asynchronously
       throw new RuntimeException(e);
     }
   }
 
+  protected abstract CompletableFuture<NamedList<Object>> requestAsyncWithUrl(
+      SolrClient client, String baseUrl, SolrRequest<?> request)
+      throws SolrServerException, IOException;
+
   private void onSuccessfulRequest(
       NamedList<Object> result,
       Endpoint endpoint,
@@ -293,43 +219,4 @@ public class LBHttp2SolrClient<C extends HttpSolrClientBase> extends LBSolrClien
       listener.onFailure(new SolrServerException(e), false);
     }
   }
-
-  public static class Builder<C extends HttpSolrClientBase> {
-
-    private final C solrClient;
-    private final LBSolrClient.Endpoint[] solrEndpoints;
-    private long aliveCheckIntervalMillis =
-        TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS); // 1 minute between checks
-    protected String defaultCollection;
-
-    public Builder(C solrClient, Endpoint... endpoints) {
-      this.solrClient = solrClient;
-      this.solrEndpoints = endpoints;
-    }
-
-    /**
-     * LBHttpSolrServer keeps pinging the dead servers at fixed interval to find if it is alive. Use
-     * this to set that interval
-     *
-     * @param aliveCheckInterval how often to ping for aliveness
-     */
-    public Builder<C> setAliveCheckInterval(int aliveCheckInterval, TimeUnit unit) {
-      if (aliveCheckInterval <= 0) {
-        throw new IllegalArgumentException(
-            "Alive check interval must be " + "positive, specified value = " + aliveCheckInterval);
-      }
-      this.aliveCheckIntervalMillis = TimeUnit.MILLISECONDS.convert(aliveCheckInterval, unit);
-      return this;
-    }
-
-    /** Sets a default for core or collection based requests. */
-    public Builder<C> withDefaultCollection(String defaultCoreOrCollection) {
-      this.defaultCollection = defaultCoreOrCollection;
-      return this;
-    }
-
-    public LBHttp2SolrClient<C> build() {
-      return new LBHttp2SolrClient<C>(this);
-    }
-  }
 }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
index c71d0210bec..1b9fbc5a3c6 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/LBSolrClient.java
@@ -61,6 +61,61 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
+/**
+ * This "LoadBalanced Http Solr Client" is a load balancing wrapper around an Http Solr Client. This
+ * is useful when you have multiple Solr endpoints and requests need to be Load Balanced among them.
+ *
+ * <p>Do <b>NOT</b> use this class for indexing in leader/follower scenarios since documents must be
+ * sent to the correct leader; no inter-node routing is done.
+ *
+ * <p>In SolrCloud (leader/replica) scenarios, it is usually better to use {@link CloudSolrClient},
+ * but this class may be used for updates because the server will forward them to the appropriate
+ * leader.
+ *
+ * <p>It offers automatic failover when a server goes down, and it detects when the server comes
+ * back up.
+ *
+ * <p>Load balancing is done using a simple round-robin on the list of endpoints. Endpoint URLs are
+ * expected to point to the Solr "root" path (i.e. "/solr").
+ *
+ * <blockquote>
+ *
+ * <pre>
+ * SolrClient client = new LBSolrClient.Builder(http2SolrClient,
+ *         new LBSolrClient.Endpoint("http://host1:8080/solr"), new LBSolrClient.Endpoint("http://host2:8080/solr"))
+ *     .build();
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * Users who wish to balance traffic across a specific set of replicas or cores may specify each
+ * endpoint as a root-URL and core-name pair. For example:
+ *
+ * <blockquote>
+ *
+ * <pre>
+ * SolrClient client = new LBSolrClient.Builder(http2SolrClient,
+ *         new LBSolrClient.Endpoint("http://host1:8080/solr", "coreA"),
+ *         new LBSolrClient.Endpoint("http://host2:8080/solr", "coreB"))
+ *     .build();
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * <p>If a request to an endpoint fails by an IOException due to a connection timeout or read
+ * timeout then the host is taken off the list of live endpoints and moved to a 'dead endpoint list'
+ * and the request is resent to the next live endpoint. This process is continued till it tries all
+ * the live endpoints. If at least one endpoint is alive, the request succeeds, and if not it fails.
+ *
+ * <p>Dead endpoints are periodically healthchecked on a fixed interval controlled by {@code
+ * LBSolrClient.Builder#setAliveCheckInterval(int, TimeUnit)}. The default is set to one minute.
+ *
+ * <p><b>When to use this?</b><br>
+ * This can be used as a software load balancer when you do not wish to set up an external load
+ * balancer. Alternatives to this code are to use a dedicated hardware load balancer or using Apache
+ * httpd with mod_proxy_balancer as a load balancer. See <a
+ * href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load balancing on Wikipedia</a>
+ */
 public abstract class LBSolrClient extends SolrClient {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -110,6 +165,54 @@ public abstract class LBSolrClient extends SolrClient {
     solrQuery.setDistrib(false);
   }
 
+  public static class Builder<C extends HttpSolrClientBase> {
+
+    private final C solrClient;
+    private final Endpoint[] solrEndpoints;
+    private long aliveCheckIntervalMillis =
+        TimeUnit.MILLISECONDS.convert(60, TimeUnit.SECONDS); // 1 minute between checks
+    protected String defaultCollection;
+
+    public Builder(C solrClient, Endpoint... endpoints) {
+      this.solrClient = solrClient;
+      this.solrEndpoints = endpoints;
+    }
+
+    /**
+     * LBHttpSolrServer keeps pinging the dead servers at fixed interval to find if it is alive. Use
+     * this to set that interval
+     *
+     * @param aliveCheckInterval how often to ping for aliveness
+     */
+    public Builder<C> setAliveCheckInterval(int aliveCheckInterval, TimeUnit unit) {
+      if (aliveCheckInterval <= 0) {
+        throw new IllegalArgumentException(
+            "Alive check interval must be " + "positive, specified value = " + aliveCheckInterval);
+      }
+      this.aliveCheckIntervalMillis = TimeUnit.MILLISECONDS.convert(aliveCheckInterval, unit);
+      return this;
+    }
+
+    /** Sets a default for core or collection based requests. */
+    public Builder<C> withDefaultCollection(String defaultCoreOrCollection) {
+      this.defaultCollection = defaultCoreOrCollection;
+      return this;
+    }
+
+    public C getSolrClient() {
+      return solrClient;
+    }
+
+    public LBSolrClient build() {
+      return new LBSolrClient(this) {
+        @Override
+        protected SolrClient getClient(Endpoint endpoint) {
+          return solrClient;
+        }
+      };
+    }
+  }
+
   /**
    * A Solr endpoint for {@link LBSolrClient} to include in its load-balancing
    *
@@ -401,7 +504,15 @@ public abstract class LBSolrClient extends SolrClient {
     }
   }
 
-  public LBSolrClient(List<Endpoint> solrEndpoints) {
+  protected LBSolrClient(Builder<?> builder) {
+    this(Arrays.asList(builder.solrEndpoints));
+    this.aliveCheckIntervalMillis = builder.aliveCheckIntervalMillis;
+    this.defaultCollection = builder.defaultCollection;
+    this.requestWriter = builder.solrClient.getRequestWriter();
+    this.parser = builder.solrClient.getParser();
+  }
+
+  protected LBSolrClient(List<Endpoint> solrEndpoints) {
     if (!solrEndpoints.isEmpty()) {
       for (Endpoint s : solrEndpoints) {
         EndpointWrapper wrapper = createServerWrapper(s);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
new file mode 100644
index 00000000000..4790fabcf3a
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/LBJettySolrClient.java
@@ -0,0 +1,54 @@
+/*
+ * 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.jetty;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.client.solrj.impl.LBAsyncSolrClient;
+import org.apache.solr.client.solrj.impl.LBSolrClient;
+import org.apache.solr.common.util.NamedList;
+
+/** An {@link LBSolrClient} based on Jetty HttpClient, supporting async. */
+public class LBJettySolrClient extends LBAsyncSolrClient {
+
+  protected LBJettySolrClient(Builder builder) {
+    super(builder);
+  }
+
+  public static class Builder extends LBSolrClient.Builder<Http2SolrClient> {
+
+    public Builder(Http2SolrClient solrClient, Endpoint... endpoints) {
+      super(solrClient, endpoints);
+    }
+
+    @Override
+    public LBJettySolrClient build() {
+      return new LBJettySolrClient(this);
+    }
+  }
+
+  @Override
+  protected CompletableFuture<NamedList<Object>> requestAsyncWithUrl(
+      SolrClient client, String baseUrl, SolrRequest<?> request)
+      throws SolrServerException, IOException {
+    return ((Http2SolrClient) client).requestWithBaseUrl(baseUrl, c -> c.requestAsync(request));
+  }
+}
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/package-info.java b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/package-info.java
new file mode 100644
index 00000000000..cd7607f2790
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/jetty/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+/** SolrJ code that depends on Jetty client / HttpClient. */
+package org.apache.solr.client.solrj.jetty;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
index e4292ee3185..6f8f8587b51 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
@@ -214,7 +214,7 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
                 Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .build()) {
       assertTrue(createdClient.getHttpClient() instanceof Http2SolrClient);
-      assertTrue(createdClient.getLbClient().solrClient instanceof Http2SolrClient);
+      assertTrue(createdClient.getLbClient().getClient(null) instanceof Http2SolrClient);
     }
   }
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
similarity index 94%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
index 3f24984f90e..ed0975b5181 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientIntegrationTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LB2SolrClientTest.java
@@ -18,7 +18,6 @@ package org.apache.solr.client.solrj.impl;
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.lang.invoke.MethodHandles;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -41,18 +40,14 @@ import org.apache.solr.util.LogLevel;
 import org.apache.solr.util.LogListener;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
- * Test for LBHttp2SolrClient
+ * Integration test for {@link LBSolrClient}
  *
  * @since solr 1.4
  */
 @LogLevel("org.apache.solr.client.solrj.impl=DEBUG")
-public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 {
-
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+public class LB2SolrClientTest extends SolrTestCaseJ4 {
 
   SolrInstance[] solr = new SolrInstance[3];
 
@@ -124,7 +119,7 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 {
               .withIdleTimeout(2000, TimeUnit.MILLISECONDS)
               .build();
       var lbClient =
-          new LBHttp2SolrClient.Builder<>(delegateClient, baseSolrEndpoints)
+          new LBSolrClient.Builder<>(delegateClient, baseSolrEndpoints)
               .withDefaultCollection(solr[0].getDefaultCollection())
               .setAliveCheckInterval(500, TimeUnit.MILLISECONDS)
               .build();
@@ -137,7 +132,7 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 {
               .withSSLContext(MockTrustManager.ALL_TRUSTING_SSL_CONTEXT)
               .build();
       var lbClient =
-          new LBHttp2SolrClient.Builder<>(delegateClient, baseSolrEndpoints)
+          new LBSolrClient.Builder<>(delegateClient, baseSolrEndpoints)
               .withDefaultCollection(solr[0].getDefaultCollection())
               .setAliveCheckInterval(500, TimeUnit.MILLISECONDS)
               .build();
@@ -316,10 +311,10 @@ public class LBHttp2SolrClientIntegrationTest extends SolrTestCaseJ4 {
 
   private static class LBClientHolder implements AutoCloseable {
 
-    final LBHttp2SolrClient<?> lbClient;
-    final HttpSolrClientBase delegate;
+    final LBSolrClient lbClient;
+    final SolrClient delegate; // http
 
-    LBClientHolder(LBHttp2SolrClient<?> lbClient, HttpSolrClientBase delegate) {
+    LBClientHolder(LBSolrClient lbClient, SolrClient delegate) {
       this.lbClient = lbClient;
       this.delegate = delegate;
     }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
similarity index 87%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java
rename to solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
index 9d2019309b0..a83bd793b49 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/LBAsyncSolrClientTest.java
@@ -19,10 +19,8 @@ package org.apache.solr.client.solrj.impl;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -31,42 +29,15 @@ import org.apache.solr.SolrTestCase;
 import org.apache.solr.client.solrj.SolrClientFunction;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.jetty.LBJettySolrClient;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.junit.Test;
 
-/** Test the LBHttp2SolrClient. */
-public class LBHttp2SolrClientTest extends SolrTestCase {
-
-  /**
-   * Test method for {@link LBHttp2SolrClient.Builder} that validates that the query param keys
-   * passed in by the base <code>Http2SolrClient
-   * </code> instance are used by the LBHttp2SolrClient.
-   */
-  @Test
-  public void testLBHttp2SolrClientWithTheseParamNamesInTheUrl() {
-    String url = "http://127.0.0.1:8080";
-    Set<String> urlParamNames = new HashSet<>(2);
-    urlParamNames.add("param1");
-
-    try (Http2SolrClient http2SolrClient =
-            new Http2SolrClient.Builder(url).withTheseParamNamesInTheUrl(urlParamNames).build();
-        LBHttp2SolrClient<Http2SolrClient> testClient =
-            new LBHttp2SolrClient.Builder<>(http2SolrClient, new LBSolrClient.Endpoint(url))
-                .build()) {
-
-      assertArrayEquals(
-          "Wrong urlParamNames found in lb client.",
-          urlParamNames.toArray(),
-          testClient.getUrlParamNames().toArray());
-      assertArrayEquals(
-          "Wrong urlParamNames found in base client.",
-          urlParamNames.toArray(),
-          http2SolrClient.getUrlParamNames().toArray());
-    }
-  }
+/** Test the LBAsyncSolrClient. */
+public class LBAsyncSolrClientTest extends SolrTestCase {
 
   @Test
   public void testSynchronous() throws Exception {
@@ -76,10 +47,9 @@ public class LBHttp2SolrClientTest extends SolrTestCase {
 
     Http2SolrClient.Builder b =
         new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
-    ;
+
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
-        LBHttp2SolrClient<MockHttpSolrClient> testClient =
-            new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) {
+        var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
 
       String lastEndpoint = null;
       for (int i = 0; i < 10; i++) {
@@ -105,10 +75,9 @@ public class LBHttp2SolrClientTest extends SolrTestCase {
 
     Http2SolrClient.Builder b =
         new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
-    ;
+
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
-        LBHttp2SolrClient<MockHttpSolrClient> testClient =
-            new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) {
+        var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
 
       client.basePathToFail = ep1.getBaseUrl();
       String basePathToSucceed = ep2.getBaseUrl();
@@ -164,10 +133,9 @@ public class LBHttp2SolrClientTest extends SolrTestCase {
 
     Http2SolrClient.Builder b =
         new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
-    ;
+
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
-        LBHttp2SolrClient<MockHttpSolrClient> testClient =
-            new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) {
+        var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
 
       for (int j = 0; j < 2; j++) {
         // first time Endpoint One will return error code 500.
@@ -230,8 +198,7 @@ public class LBHttp2SolrClientTest extends SolrTestCase {
     Http2SolrClient.Builder b =
         new Http2SolrClient.Builder("http://base.url").withConnectionTimeout(10, TimeUnit.SECONDS);
     try (MockHttpSolrClient client = new MockHttpSolrClient("http://base.url", b);
-        LBHttp2SolrClient<MockHttpSolrClient> testClient =
-            new LBHttp2SolrClient.Builder<>(client, ep1, ep2).build()) {
+        var testClient = new LBJettySolrClient.Builder(client, ep1, ep2).build()) {
 
       int limit = 10; // For simplicity use an even limit
       List<CompletableFuture<LBSolrClient.Rsp>> responses = new ArrayList<>();
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/LBHttpSolrClient.java b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/LBHttpSolrClient.java
index b926808ddb9..bac755dc692 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/LBHttpSolrClient.java
+++ b/solr/test-framework/src/java/org/apache/solr/client/solrj/apache/LBHttpSolrClient.java
@@ -26,7 +26,6 @@ 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.JavaBinResponseParser;
-import org.apache.solr.client.solrj.impl.LBHttp2SolrClient;
 import org.apache.solr.client.solrj.impl.LBSolrClient;
 import org.apache.solr.common.params.ModifiableSolrParams;
 
@@ -95,7 +94,7 @@ import org.apache.solr.common.params.ModifiableSolrParams;
  * href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load balancing on Wikipedia</a>
  *
  * @since solr 1.4
- * @deprecated Please use {@link LBHttp2SolrClient}
+ * @deprecated Not supporting Apache HttpClient based implementations anymore
  */
 @Deprecated(since = "9.0")
 public class LBHttpSolrClient extends LBSolrClient {


(solr) 06/12: Tests: move ServletFixtures to a util package (#3896)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit ce593cb5ee3ff8818f043daff43468f5e5ad85c8
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Nov 27 20:40:08 2025 -0500

    Tests: move ServletFixtures to a util package (#3896)
    
    -- of the test framework, avoiding split-package of where it's at now
---
 .../apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java    | 8 ++++----
 .../solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java  | 2 +-
 .../org/apache/solr/client/solrj/impl/Http2SolrClientTest.java    | 3 ++-
 .../org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java  | 2 +-
 .../org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java | 8 ++++----
 .../apache/solr/{client/solrj/impl => util}/ServletFixtures.java  | 2 +-
 6 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
index ef372e93e50..1542eb6e45f 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
@@ -55,10 +55,6 @@ import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.apache.HttpClientUtil;
 import org.apache.solr.client.solrj.apache.HttpSolrClient;
 import org.apache.solr.client.solrj.apache.SolrPortAwareCookieSpecFactory;
-import org.apache.solr.client.solrj.impl.ServletFixtures.DebugServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.RedirectServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.SlowServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.SlowStreamServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -69,6 +65,10 @@ import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.embedded.JettyConfig;
+import org.apache.solr.util.ServletFixtures.DebugServlet;
+import org.apache.solr.util.ServletFixtures.RedirectServlet;
+import org.apache.solr.util.ServletFixtures.SlowServlet;
+import org.apache.solr.util.ServletFixtures.SlowStreamServlet;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 import org.junit.BeforeClass;
 import org.junit.Test;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
index e58a016f9b6..fbc3cb394cf 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientCompatibilityTest.java
@@ -23,9 +23,9 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.embedded.JettyConfig;
 import org.apache.solr.util.LogLevel;
+import org.apache.solr.util.ServletFixtures.DebugServlet;
 import org.eclipse.jetty.client.transport.HttpClientTransportOverHTTP;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index a56f1bab1b0..d95ef9dfbfa 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -35,7 +35,6 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.SolrPing;
 import org.apache.solr.common.SolrException;
@@ -44,6 +43,8 @@ import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
+import org.apache.solr.util.ServletFixtures;
+import org.apache.solr.util.ServletFixtures.DebugServlet;
 import org.eclipse.jetty.client.WWWAuthenticationProtocolHandler;
 import org.eclipse.jetty.http.HttpStatus;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
index b29be52e5f2..875c5f39031 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpJdkSolrClientTest.java
@@ -37,12 +37,12 @@ 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.ServletFixtures.DebugServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.SolrPingResponse;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.util.ServletFixtures.DebugServlet;
 import org.junit.After;
 import org.junit.Test;
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
index 6a870b2b58e..b4432d56612 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/HttpSolrClientTestBase.java
@@ -42,10 +42,6 @@ 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.apache.HttpClientUtil;
-import org.apache.solr.client.solrj.impl.ServletFixtures.DebugServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.RedirectServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.SlowServlet;
-import org.apache.solr.client.solrj.impl.ServletFixtures.SlowStreamServlet;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -56,6 +52,10 @@ import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.embedded.JettyConfig;
+import org.apache.solr.util.ServletFixtures.DebugServlet;
+import org.apache.solr.util.ServletFixtures.RedirectServlet;
+import org.apache.solr.util.ServletFixtures.SlowServlet;
+import org.apache.solr.util.ServletFixtures.SlowStreamServlet;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 import org.junit.BeforeClass;
 
diff --git a/solr/test-framework/src/java/org/apache/solr/client/solrj/impl/ServletFixtures.java b/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java
similarity index 99%
rename from solr/test-framework/src/java/org/apache/solr/client/solrj/impl/ServletFixtures.java
rename to solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java
index c093cd673ca..3405f34b6a2 100644
--- a/solr/test-framework/src/java/org/apache/solr/client/solrj/impl/ServletFixtures.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/ServletFixtures.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.util;
 
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.Cookie;


(solr) 08/12: SOLR-17962: Move tests relating to Apache HttpClient (#3888)

Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/solr.git

commit ab330d40d600dd1672403ff230c28e76101b12ca
Author: David Smiley <ds...@apache.org>
AuthorDate: Fri Nov 28 09:45:23 2025 -0500

    SOLR-17962: Move tests relating to Apache HttpClient (#3888)
    
    to test-framework, org.apache.solr.client.solrj.apache
    And copied/ported some individual tests that were not yet present in the surviving clones of these test suites.
    
    Move SolrJMetricTestUtils to test-framework.
    
    @Ignore Apache HttpClient tests relying on SolrJ fixtures/resources since they are not available in the test framework (the new home), and these tests will be removed soon anyway.
---
 .../impl/CloudHttp2SolrClientBuilderTest.java      |  13 ++
 .../solrj/impl/CloudHttp2SolrClientRetryTest.java  |   2 +-
 .../solrj/impl/CloudHttp2SolrClientTest.java       |  75 ++++++++-
 .../impl/ConcurrentUpdateHttp2SolrClientTest.java  | 187 +++++++++++++++++++--
 .../solr/client/solrj/request/TestCoreAdmin.java   |   2 +-
 .../apache/solr/util}/SolrJMetricTestUtils.java    |   5 +-
 .../solrj/apache}/BasicHttpSolrClientTest.java     |   9 +-
 .../solrj/apache}/CloudSolrClientBadInputTest.java |   2 +-
 .../solrj/apache}/CloudSolrClientBuilderTest.java  |   8 +-
 .../CloudSolrClientMultiConstructorTest.java       |   8 +-
 .../solrj/apache}/CloudSolrClientRetryTest.java    |  14 +-
 .../client/solrj/apache}/CloudSolrClientTest.java  |  11 +-
 .../ConcurrentUpdateSolrClientBadInputTest.java    |   3 +-
 .../ConcurrentUpdateSolrClientBuilderTest.java     |   3 +-
 ...currentUpdateSolrClientMultiCollectionTest.java |   3 +-
 .../apache}/ConcurrentUpdateSolrClientTest.java    |   3 +-
 16 files changed, 299 insertions(+), 49 deletions(-)

diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
index 6a59a6d7c72..e4292ee3185 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientBuilderTest.java
@@ -28,6 +28,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
+import java.util.concurrent.TimeUnit;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -280,4 +281,16 @@ public class CloudHttp2SolrClientBuilderTest extends SolrCloudTestCase {
     }
     verify(stateProvider, times(1)).close();
   }
+
+  @Test
+  @SuppressWarnings({"try"})
+  public void test0Timeouts() throws IOException {
+    try (CloudSolrClient createdClient =
+        new CloudHttp2SolrClient.Builder(Collections.singletonList(ANY_ZK_HOST), Optional.empty())
+            .withZkConnectTimeout(0, TimeUnit.MILLISECONDS)
+            .withZkClientTimeout(0, TimeUnit.MILLISECONDS)
+            .build()) {
+      assertNotNull(createdClient);
+    }
+  }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
index 8c703f7d5ce..58b1b2db48c 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
@@ -17,7 +17,7 @@
 
 package org.apache.solr.client.solrj.impl;
 
-import static org.apache.solr.client.solrj.SolrJMetricTestUtils.getPrometheusMetricValue;
+import static org.apache.solr.util.SolrJMetricTestUtils.getPrometheusMetricValue;
 
 import java.util.Collections;
 import java.util.Optional;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
index cf490ce042e..8697baf87a8 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.solr.client.solrj.impl;
 
+import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
 import static org.apache.solr.client.solrj.impl.BaseHttpClusterStateProvider.SYS_PROP_CACHE_TIMEOUT_SECONDS;
 import static org.apache.solr.client.solrj.impl.CloudSolrClient.RouteResponse;
 
@@ -38,7 +39,6 @@ import java.util.concurrent.TimeoutException;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrJMetricTestUtils;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrServerException;
@@ -49,8 +49,10 @@ import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 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.RequestStatusState;
+import org.apache.solr.client.solrj.response.SolrPingResponse;
 import org.apache.solr.client.solrj.response.UpdateResponse;
 import org.apache.solr.cloud.AbstractDistribZkTestBase;
 import org.apache.solr.cloud.SolrCloudTestCase;
@@ -61,6 +63,8 @@ import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
 import org.apache.solr.common.cloud.DocRouter;
+import org.apache.solr.common.cloud.PerReplicaStates;
+import org.apache.solr.common.cloud.PerReplicaStatesOps;
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.Slice;
 import org.apache.solr.common.cloud.ZkStateReader;
@@ -79,6 +83,7 @@ import org.apache.solr.handler.admin.CoreAdminHandler;
 import org.apache.solr.servlet.HttpSolrCall;
 import org.apache.solr.util.LogLevel;
 import org.apache.solr.util.LogListener;
+import org.apache.solr.util.SolrJMetricTestUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -1197,4 +1202,72 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
       log.info("Shards giving the response: {}", Arrays.toString(shardAddresses.toArray()));
     }
   }
+
+  @Test
+  public void testPing() throws Exception {
+    final String testCollection = "ping_test";
+    CollectionAdminRequest.createCollection(testCollection, "conf", 2, 1)
+        .process(cluster.getSolrClient());
+    cluster.waitForActiveCollection(testCollection, 2, 2);
+    final SolrClient clientUnderTest = getRandomClient();
+
+    final SolrPingResponse response = clientUnderTest.ping(testCollection);
+
+    assertEquals("This should be OK", 0, response.getStatus());
+  }
+
+  public void testPerReplicaStateCollection() throws Exception {
+    String collection = getSaferTestName();
+
+    CollectionAdminRequest.createCollection(collection, "conf", 2, 1)
+        .process(cluster.getSolrClient());
+
+    String testCollection = "perReplicaState_test";
+    String collectionPath = DocCollection.getCollectionPath(testCollection);
+
+    int liveNodes = cluster.getJettySolrRunners().size();
+    CollectionAdminRequest.createCollection(testCollection, "conf", 2, 2)
+        .setPerReplicaState(Boolean.TRUE)
+        .process(cluster.getSolrClient());
+    cluster.waitForActiveCollection(testCollection, 2, 4);
+    final SolrClient clientUnderTest = getRandomClient();
+    final SolrPingResponse response = clientUnderTest.ping(testCollection);
+    assertEquals("This should be OK", 0, response.getStatus());
+
+    DocCollection c = cluster.getZkStateReader().getCollection(testCollection);
+    c.forEachReplica((s, replica) -> assertNotNull(replica.getReplicaState()));
+    PerReplicaStates prs = PerReplicaStatesOps.fetch(collectionPath, cluster.getZkClient(), null);
+    assertEquals(4, prs.states.size());
+
+    JettySolrRunner jsr = null;
+    try {
+      jsr = cluster.startJettySolrRunner();
+
+      // Now let's do an add replica
+      CollectionAdminRequest.addReplicaToShard(testCollection, "shard1")
+          .process(cluster.getSolrClient());
+      prs = PerReplicaStatesOps.fetch(collectionPath, cluster.getZkClient(), null);
+      assertEquals(5, prs.states.size());
+
+      // create a collection with PRS and v2 API
+      testCollection = "perReplicaState_testv2";
+      collectionPath = DocCollection.getCollectionPath(testCollection);
+
+      new V2Request.Builder("/collections")
+          .withMethod(POST)
+          .withPayload(
+              "{\"name\": \"perReplicaState_testv2\", \"config\" : \"conf\", \"numShards\" : 2, \"nrtReplicas\" : 2, \"perReplicaState\" : true}")
+          .build()
+          .process(cluster.getSolrClient());
+      cluster.waitForActiveCollection(testCollection, 2, 4);
+      c = cluster.getZkStateReader().getCollection(testCollection);
+      c.forEachReplica((s, replica) -> assertNotNull(replica.getReplicaState()));
+      prs = PerReplicaStatesOps.fetch(collectionPath, cluster.getZkClient(), null);
+      assertEquals(4, prs.states.size());
+    } finally {
+      if (jsr != null) {
+        cluster.stopJettySolrRunner(jsr);
+      }
+    }
+  }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java
index 470aa838a4f..d202834dd83 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateHttp2SolrClientTest.java
@@ -17,14 +17,30 @@
 
 package org.apache.solr.client.solrj.impl;
 
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.SocketTimeoutException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.solr.SolrJettyTestBase;
+import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec;
+import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.SolrNamedThreadFactory;
@@ -33,22 +49,134 @@ import org.eclipse.jetty.client.Response;
 import org.eclipse.jetty.ee10.servlet.ServletHolder;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  /** Mock endpoint where the CUSS being tested in this class sends requests. */
+  public static class TestServlet extends HttpServlet
+      implements JavaBinUpdateRequestCodec.StreamingUpdateHandler {
+    private static final long serialVersionUID = 1L;
+
+    public static void clear() {
+      lastMethod = null;
+      headers = null;
+      parameters = null;
+      errorCode = null;
+      numReqsRcvd.set(0);
+      numDocsRcvd.set(0);
+    }
+
+    public static Integer errorCode = null;
+    public static String lastMethod = null;
+    public static HashMap<String, String> headers = null;
+    public static Map<String, String[]> parameters = null;
+    public static AtomicInteger numReqsRcvd = new AtomicInteger(0);
+    public static AtomicInteger numDocsRcvd = new AtomicInteger(0);
+
+    public static void setErrorCode(Integer code) {
+      errorCode = code;
+    }
+
+    private void setHeaders(HttpServletRequest req) {
+      Enumeration<String> headerNames = req.getHeaderNames();
+      headers = new HashMap<>();
+      while (headerNames.hasMoreElements()) {
+        final String name = headerNames.nextElement();
+        headers.put(name, req.getHeader(name));
+      }
+    }
+
+    private void setParameters(HttpServletRequest req) {
+      // parameters = req.getParameterMap();
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+        throws ServletException, IOException {
+
+      numReqsRcvd.incrementAndGet();
+      lastMethod = "post";
+      recordRequest(req, resp);
+
+      InputStream reqIn = req.getInputStream();
+      JavaBinUpdateRequestCodec javabin = new JavaBinUpdateRequestCodec();
+      for (; ; ) {
+        try {
+          javabin.unmarshal(reqIn, this);
+        } catch (EOFException e) {
+          break; // this is expected
+        }
+      }
+    }
+
+    private void recordRequest(HttpServletRequest req, HttpServletResponse resp) {
+      setHeaders(req);
+      setParameters(req);
+      if (null != errorCode) {
+        try {
+          resp.sendError(errorCode);
+        } catch (IOException e) {
+          throw new RuntimeException("sendError IO fail in TestServlet", e);
+        }
+      }
+    }
+
+    @Override
+    public void update(
+        SolrInputDocument document, UpdateRequest req, Integer commitWithin, Boolean override) {
+      numDocsRcvd.incrementAndGet();
+    }
+  } // end TestServlet
+
+  static class SendDocsRunnable implements Runnable {
+
+    private String id;
+    private int numDocs;
+    private SolrClient cuss;
+    private String collection;
+
+    SendDocsRunnable(String id, int numDocs, SolrClient cuss) {
+      this(id, numDocs, cuss, null);
+    }
+
+    SendDocsRunnable(String id, int numDocs, SolrClient cuss, String collection) {
+      this.id = id;
+      this.numDocs = numDocs;
+      this.cuss = cuss;
+      this.collection = collection;
+    }
+
+    @Override
+    public void run() {
+      for (int d = 0; d < numDocs; d++) {
+        SolrInputDocument doc = new SolrInputDocument();
+        String docId = id + "_" + d;
+        doc.setField("id", docId);
+        UpdateRequest req = new UpdateRequest();
+        req.add(doc);
+        try {
+          if (this.collection == null) cuss.request(req);
+          else cuss.request(req, this.collection);
+        } catch (Throwable t) {
+          log.error("error making request", t);
+        }
+      }
+    }
+  }
 
   @BeforeClass
   public static void beforeTest() throws Exception {
     JettyConfig jettyConfig =
-        JettyConfig.builder()
-            .withServlet(
-                new ServletHolder(ConcurrentUpdateSolrClientTest.TestServlet.class), "/cuss/*")
-            .build();
+        JettyConfig.builder().withServlet(new ServletHolder(TestServlet.class), "/cuss/*").build();
     createAndStartJetty(legacyExampleCollection1SolrHome(), jettyConfig);
   }
 
   @Test
   public void testConcurrentUpdate() throws Exception {
-    ConcurrentUpdateSolrClientTest.TestServlet.clear();
+    TestServlet.clear();
 
     String serverUrl = getBaseUrl() + "/cuss/foo";
 
@@ -79,9 +207,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
       int numDocs = 100;
       int numRunnables = 5;
       for (int r = 0; r < numRunnables; r++)
-        threadPool.execute(
-            new ConcurrentUpdateSolrClientTest.SendDocsRunnable(
-                String.valueOf(r), numDocs, concurrentClient));
+        threadPool.execute(new SendDocsRunnable(String.valueOf(r), numDocs, concurrentClient));
 
       // ensure all docs are sent
       threadPool.awaitTermination(5, TimeUnit.SECONDS);
@@ -91,10 +217,10 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
       concurrentClient.blockUntilFinished();
       concurrentClient.shutdownNow();
 
-      assertEquals("post", ConcurrentUpdateSolrClientTest.TestServlet.lastMethod);
+      assertEquals("post", TestServlet.lastMethod);
 
       // expect all requests to be successful
-      int expectedSuccesses = ConcurrentUpdateSolrClientTest.TestServlet.numReqsRcvd.get();
+      int expectedSuccesses = TestServlet.numReqsRcvd.get();
       assertTrue(expectedSuccesses > 0); // at least one request must have been sent
 
       assertEquals(
@@ -108,11 +234,8 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
 
       int expectedDocs = numDocs * numRunnables;
       assertEquals(
-          "Expected CUSS to send "
-              + expectedDocs
-              + " but got "
-              + ConcurrentUpdateSolrClientTest.TestServlet.numDocsRcvd.get(),
-          ConcurrentUpdateSolrClientTest.TestServlet.numDocsRcvd.get(),
+          "Expected CUSS to send " + expectedDocs + " but got " + TestServlet.numDocsRcvd.get(),
+          TestServlet.numDocsRcvd.get(),
           expectedDocs);
     }
   }
@@ -185,8 +308,7 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
 
       for (int r = 0; r < numRunnables; r++)
         threadPool.execute(
-            new ConcurrentUpdateSolrClientTest.SendDocsRunnable(
-                String.valueOf(r), numDocs, concurrentClient, "collection1"));
+            new SendDocsRunnable(String.valueOf(r), numDocs, concurrentClient, "collection1"));
 
       // ensure all docs are sent
       threadPool.awaitTermination(5, TimeUnit.SECONDS);
@@ -263,4 +385,35 @@ public class ConcurrentUpdateHttp2SolrClientTest extends SolrJettyTestBase {
       }
     }
   }
+
+  /**
+   * Test that connection timeout information is passed to the HttpSolrClient that handles non add
+   * operations.
+   */
+  @Test(timeout = 10000)
+  public void testSocketTimeoutOnCommit() throws IOException, SolrServerException {
+    InetAddress localHost = InetAddress.getLocalHost();
+    try (ServerSocket server = new ServerSocket(0, 1, localHost);
+        var http2Client =
+            new Http2SolrClient.Builder().withIdleTimeout(1, TimeUnit.MILLISECONDS).build();
+        var client =
+            new ConcurrentUpdateHttp2SolrClient.Builder(
+                    "http://"
+                        + localHost.getHostAddress()
+                        + ":"
+                        + server.getLocalPort()
+                        + "/noOneThere",
+                    http2Client)
+                .build()) {
+      // Expecting an exception
+      client.commit();
+      fail();
+    } catch (SolrServerException e) {
+      if (!(e.getCause() instanceof SocketTimeoutException // not sure if Jetty throws this
+          || e.getCause() instanceof TimeoutException)) { // Jetty throws this
+        throw e;
+      }
+      // else test passes
+    }
+  }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
index 90b5687f402..16f96052826 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
@@ -16,7 +16,7 @@
  */
 package org.apache.solr.client.solrj.request;
 
-import static org.apache.solr.client.solrj.SolrJMetricTestUtils.getPrometheusMetricValue;
+import static org.apache.solr.util.SolrJMetricTestUtils.getPrometheusMetricValue;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.core.Is.is;
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrJMetricTestUtils.java b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
similarity index 96%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/SolrJMetricTestUtils.java
rename to solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
index f6f2d135920..ccff28c55c9 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrJMetricTestUtils.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/SolrJMetricTestUtils.java
@@ -15,11 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj;
+package org.apache.solr.util;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrServerException;
 import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/BasicHttpSolrClientTest.java
similarity index 99%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/BasicHttpSolrClientTest.java
index 1542eb6e45f..f1f90a1a817 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/BasicHttpSolrClientTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/BasicHttpSolrClientTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -52,9 +52,10 @@ import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrRequest.METHOD;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.HttpClientUtil;
-import org.apache.solr.client.solrj.apache.HttpSolrClient;
-import org.apache.solr.client.solrj.apache.SolrPortAwareCookieSpecFactory;
+import org.apache.solr.client.solrj.impl.JavaBinRequestWriter;
+import org.apache.solr.client.solrj.impl.JavaBinResponseParser;
+import org.apache.solr.client.solrj.impl.XMLRequestWriter;
+import org.apache.solr.client.solrj.impl.XMLResponseParser;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBadInputTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBadInputTest.java
similarity index 98%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBadInputTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBadInputTest.java
index 82614a98398..ad591b38681 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBadInputTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBadInputTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBuilderTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
similarity index 94%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBuilderTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
index 39d728394a1..280add47de7 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientBuilderTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientBuilderTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -24,7 +24,9 @@ import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCase;
-import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.junit.Test;
 
 public class CloudSolrClientBuilderTest extends SolrTestCase {
@@ -113,7 +115,7 @@ public class CloudSolrClientBuilderTest extends SolrTestCase {
   @Test
   public void testDefaultCollectionPassedFromBuilderToClient() throws IOException {
     try (CloudSolrClient createdClient =
-        new CloudHttp2SolrClient.Builder(
+        new CloudLegacySolrClient.Builder(
                 Collections.singletonList(ANY_ZK_HOST), Optional.of(ANY_CHROOT))
             .withDefaultCollection("aCollection")
             .build()) {
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientMultiConstructorTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
similarity index 92%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientMultiConstructorTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
index ff50829ef24..6d8f3ef8a61 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientMultiConstructorTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientMultiConstructorTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -24,7 +24,9 @@ import java.util.List;
 import java.util.Optional;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.SolrTestCase;
-import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
+import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.junit.Test;
 
 public class CloudSolrClientMultiConstructorTest extends SolrTestCase {
@@ -37,7 +39,7 @@ public class CloudSolrClientMultiConstructorTest extends SolrTestCase {
   Collection<String> hosts;
 
   @Test
-  public void testZkConnectionStringSetterWithValidChroot() throws IOException {
+  public void testZkConnecstionStringSetterWithValidChroot() throws IOException {
     boolean setOrList = random().nextBoolean();
     int numOfZKServers = TestUtil.nextInt(random(), 1, 5);
     boolean withChroot = random().nextBoolean();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientRetryTest.java
similarity index 84%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientRetryTest.java
index 19c182485ab..2ae18bce100 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientRetryTest.java
@@ -15,17 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
-
-import static org.apache.solr.client.solrj.SolrJMetricTestUtils.getPrometheusMetricValue;
+package org.apache.solr.client.solrj.apache;
 
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.util.SolrJMetricTestUtils;
 import org.apache.solr.util.TestInjection;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
+@Ignore("Apache HttpClient stuff will get deleted SOLR-16367")
 public class CloudSolrClientRetryTest extends SolrCloudTestCase {
   private static final int NODE_COUNT = 1;
 
@@ -52,7 +54,8 @@ public class CloudSolrClientRetryTest extends SolrCloudTestCase {
         "solr_core_requests_total{category=\"UPDATE\",collection=\"testRetry\",core=\"testRetry_shard1_replica_n1\",handler=\"/update\",otel_scope_name=\"org.apache.solr\",replica_type=\"NRT\",shard=\"shard1\"}";
     solrClient.add(collectionName, new SolrInputDocument("id", "1"));
 
-    assertEquals(1.0, getPrometheusMetricValue(solrClient, prometheusMetric), 0.0);
+    assertEquals(
+        1.0, SolrJMetricTestUtils.getPrometheusMetricValue(solrClient, prometheusMetric), 0.0);
 
     TestInjection.failUpdateRequests = "true:100";
     try {
@@ -66,6 +69,7 @@ public class CloudSolrClientRetryTest extends SolrCloudTestCase {
       TestInjection.reset();
     }
 
-    assertEquals(2.0, getPrometheusMetricValue(solrClient, prometheusMetric), 0.0);
+    assertEquals(
+        2.0, SolrJMetricTestUtils.getPrometheusMetricValue(solrClient, prometheusMetric), 0.0);
   }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientTest.java
similarity index 99%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientTest.java
index 5a7ae3963b0..a989ba1ebe6 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/CloudSolrClientTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import static org.apache.solr.client.solrj.SolrRequest.METHOD.POST;
 
@@ -37,13 +37,13 @@ import java.util.concurrent.TimeoutException;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrJMetricTestUtils;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrRequest.METHOD;
 import org.apache.solr.client.solrj.SolrRequest.SolrRequestType;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.CloudLegacySolrClient;
-import org.apache.solr.client.solrj.apache.HttpClientUtil;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.LBSolrClient;
+import org.apache.solr.client.solrj.impl.ZkClientClusterStateProvider;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.GenericSolrRequest;
@@ -81,9 +81,11 @@ import org.apache.solr.handler.admin.CollectionsHandler;
 import org.apache.solr.handler.admin.ConfigSetsHandler;
 import org.apache.solr.handler.admin.CoreAdminHandler;
 import org.apache.solr.util.LogLevel;
+import org.apache.solr.util.SolrJMetricTestUtils;
 import org.hamcrest.Matchers;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -91,6 +93,7 @@ import org.slf4j.LoggerFactory;
 /** This test would be faster if we simulated the zk state instead. */
 @LogLevel(
     "org.apache.solr.cloud.Overseer=INFO;org.apache.solr.common.cloud=INFO;org.apache.solr.cloud.api.collections=INFO;org.apache.solr.cloud.overseer=INFO")
+@Ignore("Apache HttpClient stuff will get deleted SOLR-16367")
 public class CloudSolrClientTest extends SolrCloudTestCase {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBadInputTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBadInputTest.java
similarity index 97%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBadInputTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBadInputTest.java
index eb8cfa71d8a..55bc43e598d 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBadInputTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBadInputTest.java
@@ -15,13 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.solr.SolrJettyTestBase;
 import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient;
 import org.apache.solr.embedded.JettyConfig;
 import org.junit.BeforeClass;
 import org.junit.Test;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBuilderTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBuilderTest.java
similarity index 96%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBuilderTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBuilderTest.java
index f20c2bc198b..7291c30ceba 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientBuilderTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientBuilderTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.io.IOException;
 import java.net.InetAddress;
@@ -25,7 +25,6 @@ import java.util.concurrent.TimeUnit;
 import org.apache.solr.SolrTestCase;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient;
 import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient.Builder;
 import org.junit.Test;
 
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientMultiCollectionTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientMultiCollectionTest.java
similarity index 96%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientMultiCollectionTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientMultiCollectionTest.java
index 90a4a512d62..4110949c9a5 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientMultiCollectionTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientMultiCollectionTest.java
@@ -15,13 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import java.io.IOException;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.SolrInputDocument;
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTest.java b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientTest.java
similarity index 99%
rename from solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTest.java
rename to solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientTest.java
index 0ab4a03da75..99f80e892e5 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ConcurrentUpdateSolrClientTest.java
+++ b/solr/test-framework/src/test/org/apache/solr/client/solrj/apache/ConcurrentUpdateSolrClientTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.client.solrj.impl;
+package org.apache.solr.client.solrj.apache;
 
 import jakarta.servlet.ServletException;
 import jakarta.servlet.http.HttpServlet;
@@ -35,7 +35,6 @@ import org.apache.solr.SolrJettyTestBase;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.apache.ConcurrentUpdateSolrClient;
 import org.apache.solr.client.solrj.request.JavaBinUpdateRequestCodec;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.common.SolrInputDocument;