Skip to content

Commit 156b350

Browse files
duncdrumcursoragent
andcommitted
[refactor] Migrate integration tests to Apache HttpClient 5.x
Add HC5 BOM (httpclient5/httpclient5-fluent 5.6.1, httpcore5 5.4) alongside HC4 for hybrid coexistence. Migrate exist-core and extension HTTP tests to org.apache.hc.*; keep extensions/webdav on HC4 for milton-client JUnit tests. HC5 fluent Executor auth does not reliably attach Basic credentials to /exist/... URLs, so AbstractHttpTest adds a preemptive Authorization interceptor (same pattern in restxq and file module tests). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent bf86241 commit 156b350

31 files changed

Lines changed: 527 additions & 401 deletions

File tree

exist-core/pom.xml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,8 @@
589589
<scope>test</scope>
590590
</dependency>
591591
<dependency>
592-
<groupId>org.apache.httpcomponents</groupId>
593-
<artifactId>httpclient</artifactId>
592+
<groupId>org.apache.httpcomponents.client5</groupId>
593+
<artifactId>httpclient5</artifactId>
594594
<scope>test</scope>
595595
</dependency>
596596

@@ -655,18 +655,13 @@
655655
<scope>test</scope>
656656
</dependency>
657657
<dependency>
658-
<groupId>org.apache.httpcomponents</groupId>
659-
<artifactId>httpcore</artifactId>
658+
<groupId>org.apache.httpcomponents.core5</groupId>
659+
<artifactId>httpcore5</artifactId>
660660
<scope>test</scope>
661661
</dependency>
662662
<dependency>
663-
<groupId>org.apache.httpcomponents</groupId>
664-
<artifactId>httpmime</artifactId>
665-
<scope>test</scope>
666-
</dependency>
667-
<dependency>
668-
<groupId>org.apache.httpcomponents</groupId>
669-
<artifactId>fluent-hc</artifactId>
663+
<groupId>org.apache.httpcomponents.client5</groupId>
664+
<artifactId>httpclient5-fluent</artifactId>
670665
<scope>test</scope>
671666
</dependency>
672667

exist-core/src/test/java/org/exist/dom/persistent/CDataIntergationTest.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222

2323
package org.exist.dom.persistent;
2424

25-
import org.apache.http.HttpHost;
26-
import org.apache.http.HttpResponse;
27-
import org.apache.http.client.fluent.Executor;
28-
import org.apache.http.client.fluent.Request;
25+
import org.apache.hc.client5.http.fluent.Executor;
26+
import org.apache.hc.client5.http.fluent.Request;
27+
import org.exist.http.AbstractHttpTest;
28+
import org.apache.hc.core5.http.ClassicHttpResponse;
2929
import org.exist.TestUtils;
3030
import org.exist.test.ExistWebServer;
3131
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
@@ -44,7 +44,7 @@
4444
import java.io.IOException;
4545

4646
import static java.nio.charset.StandardCharsets.UTF_8;
47-
import static org.apache.http.HttpStatus.SC_CREATED;
47+
import static org.apache.hc.core5.http.HttpStatus.SC_CREATED;
4848
import static org.junit.Assert.*;
4949

5050
/**
@@ -67,24 +67,22 @@ public void cdataRestApi() throws IOException {
6767
final String uri = "http://localhost:" + existWebServer.getPort() + "/exist/rest/db";
6868
final String docUri = uri + "/rest-cdata-test.xml";
6969

70-
final Executor executor = Executor
71-
.newInstance()
72-
.auth(TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD)
73-
.authPreemptive(new HttpHost("localhost", existWebServer.getPort()));
70+
final Executor executor = AbstractHttpTest.createAuthenticatedExecutor(
71+
existWebServer, TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD);
7472

7573
// store document
76-
final HttpResponse storeResponse = executor.execute(
74+
final ClassicHttpResponse storeResponse = (ClassicHttpResponse) executor.execute(
7775
Request
78-
.Put(docUri)
76+
.put(docUri)
7977
.addHeader("Content-Type", "application/xml")
8078
.bodyByteArray(cdata_xml.getBytes(UTF_8))
8179
).returnResponse();
82-
assertEquals(SC_CREATED, storeResponse.getStatusLine().getStatusCode());
80+
assertEquals(SC_CREATED, storeResponse.getCode());
8381

8482
// retrieve document
85-
final HttpResponse retrieveResponse = executor.execute(
83+
final ClassicHttpResponse retrieveResponse = (ClassicHttpResponse) executor.execute(
8684
Request
87-
.Get(docUri)
85+
.get(docUri)
8886
).returnResponse();
8987
try (final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
9088
retrieveResponse.getEntity().writeTo(baos);

exist-core/src/test/java/org/exist/http/AbstractHttpTest.java

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,20 @@
2323
package org.exist.http;
2424

2525
import com.evolvedbinary.j8fu.function.FunctionE;
26-
import org.apache.http.HttpHost;
27-
import org.apache.http.client.HttpClient;
28-
import org.apache.http.client.config.CookieSpecs;
29-
import org.apache.http.client.config.RequestConfig;
30-
import org.apache.http.client.fluent.Executor;
31-
import org.apache.http.impl.client.CloseableHttpClient;
32-
import org.apache.http.impl.client.HttpClientBuilder;
26+
import org.apache.hc.client5.http.auth.AuthScope;
27+
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
28+
import org.apache.hc.client5.http.fluent.Executor;
29+
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
30+
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
31+
import org.apache.hc.client5.http.impl.classic.HttpClients;
32+
import org.apache.hc.core5.http.HttpHeaders;
33+
import org.apache.hc.core5.http.HttpHost;
3334
import org.exist.TestUtils;
3435
import org.exist.test.ExistWebServer;
3536

3637
import java.io.IOException;
38+
import java.nio.charset.StandardCharsets;
39+
import java.util.Base64;
3740

3841
/**
3942
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
@@ -73,6 +76,73 @@ protected static String getAppsUri(final ExistWebServer existWebServer) {
7376
return getServerUri(existWebServer) + "/apps";
7477
}
7578

79+
/**
80+
* Create an {@link HttpHost} for the given eXist-db Web Server.
81+
*
82+
* @param existWebServer the eXist-db Web Server.
83+
*
84+
* @return the HTTP host.
85+
*/
86+
public static HttpHost getHttpHost(final ExistWebServer existWebServer) {
87+
return new HttpHost("http", "localhost", existWebServer.getPort());
88+
}
89+
90+
/**
91+
* Create an HTTP client that sends preemptive HTTP Basic authentication.
92+
*
93+
* <p>HC5's fluent {@link Executor} auth helpers do not always attach credentials to requests
94+
* under the {@code /exist/...} context path; the request interceptor ensures the
95+
* {@code Authorization} header is present on the first request.</p>
96+
*
97+
* @param existWebServer the eXist-db Web Server.
98+
* @param user the user name.
99+
* @param password the password.
100+
*
101+
* @return a closable HTTP client.
102+
*/
103+
public static CloseableHttpClient createAuthenticatedClient(
104+
final ExistWebServer existWebServer,
105+
final String user,
106+
final String password) {
107+
final HttpHost host = getHttpHost(existWebServer);
108+
final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user, password.toCharArray());
109+
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
110+
credentialsProvider.setCredentials(new AuthScope(host), credentials);
111+
final String authorizationHeader = "Basic " + Base64.getEncoder().encodeToString(
112+
(user + ":" + password).getBytes(StandardCharsets.UTF_8));
113+
114+
return HttpClients.custom()
115+
.setDefaultCredentialsProvider(credentialsProvider)
116+
.addRequestInterceptorFirst((request, entity, context) -> {
117+
if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) {
118+
request.addHeader(HttpHeaders.AUTHORIZATION, authorizationHeader);
119+
}
120+
})
121+
.disableAutomaticRetries()
122+
.build();
123+
}
124+
125+
/**
126+
* Create an HTTP executor that sends preemptive HTTP Basic authentication.
127+
*
128+
* @param existWebServer the eXist-db Web Server.
129+
* @param user the user name.
130+
* @param password the password.
131+
*
132+
* @return an executor backed by {@link #createAuthenticatedClient(ExistWebServer, String, String)}.
133+
*/
134+
public static Executor createAuthenticatedExecutor(
135+
final ExistWebServer existWebServer,
136+
final String user,
137+
final String password) {
138+
final HttpHost host = getHttpHost(existWebServer);
139+
final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(user, password.toCharArray());
140+
return Executor
141+
.newInstance(createAuthenticatedClient(existWebServer, user, password))
142+
.authPreemptive(host)
143+
.auth(host, credentials);
144+
}
145+
76146
/**
77147
* Execute a function with a HTTP Client.
78148
*
@@ -83,13 +153,9 @@ protected static String getAppsUri(final ExistWebServer existWebServer) {
83153
*
84154
* @throws IOException if an I/O error occurs
85155
*/
86-
protected static <T> T withHttpClient(final FunctionE<HttpClient, T, IOException> fn) throws IOException {
87-
try (final CloseableHttpClient client = HttpClientBuilder
88-
.create()
156+
protected static <T> T withHttpClient(final FunctionE<CloseableHttpClient, T, IOException> fn) throws IOException {
157+
try (final CloseableHttpClient client = HttpClients.custom()
89158
.disableAutomaticRetries()
90-
.setDefaultRequestConfig(RequestConfig.custom()
91-
.setCookieSpec(CookieSpecs.STANDARD)
92-
.build())
93159
.build()) {
94160
return fn.apply(client);
95161
}
@@ -107,12 +173,16 @@ protected static <T> T withHttpClient(final FunctionE<HttpClient, T, IOException
107173
* @throws IOException if an I/O error occurs
108174
*/
109175
protected static <T> T withHttpExecutor(final ExistWebServer existWebServer, final FunctionE<Executor, T, IOException> fn) throws IOException {
110-
return withHttpClient(client -> {
176+
try (final CloseableHttpClient client = createAuthenticatedClient(
177+
existWebServer, TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD)) {
178+
final HttpHost host = getHttpHost(existWebServer);
179+
final UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
180+
TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD.toCharArray());
111181
final Executor executor = Executor
112182
.newInstance(client)
113-
.auth(TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD)
114-
.authPreemptive(new HttpHost("localhost", existWebServer.getPort()));
183+
.authPreemptive(host)
184+
.auth(host, credentials);
115185
return fn.apply(executor);
116-
});
186+
}
117187
}
118188
}

exist-core/src/test/java/org/exist/http/urlrewrite/ControllerTest.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424

2525
import com.evolvedbinary.j8fu.tuple.Tuple2;
2626
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
27-
import org.apache.http.HttpResponse;
28-
import org.apache.http.HttpStatus;
29-
import org.apache.http.client.fluent.Request;
30-
import org.apache.http.entity.ContentType;
27+
import org.apache.hc.client5.http.fluent.Request;
28+
import org.apache.hc.core5.http.ClassicHttpResponse;
29+
import org.apache.hc.core5.http.ContentType;
30+
import org.apache.hc.core5.http.HttpStatus;
3131
import org.exist.http.AbstractHttpTest;
3232
import org.exist.test.ExistWebServer;
3333
import org.junit.ClassRule;
@@ -95,20 +95,20 @@ public void prefersNonLegacyController() throws IOException {
9595

9696
private void store(final String testCollectionName, final String documentMediaType, final String documentName, final String documentContent) throws IOException {
9797
final Request request = Request
98-
.Put(getRestUri(existWebServer) + "/db/apps/" + testCollectionName + "/" + documentName)
98+
.put(getRestUri(existWebServer) + "/db/apps/" + testCollectionName + "/" + documentName)
9999
.bodyString(documentContent, ContentType.create(documentMediaType));
100100
int statusCode = withHttpExecutor(existWebServer, executor ->
101-
executor.execute(request).returnResponse().getStatusLine().getStatusCode()
101+
executor.execute(request).returnResponse().getCode()
102102
);
103103
assertEquals(HttpStatus.SC_CREATED, statusCode);
104104
}
105105

106106
private Tuple2<Integer, String> get(final String testCollectionName, final String documentName) throws IOException {
107107
final Request request = Request
108-
.Get(getAppsUri(existWebServer) + "/" + testCollectionName + "/" + documentName);
108+
.get(getAppsUri(existWebServer) + "/" + testCollectionName + "/" + documentName);
109109
final Tuple2<Integer, String> responseCodeAndBody = withHttpExecutor(existWebServer, executor -> {
110-
final HttpResponse response = executor.execute(request).returnResponse();
111-
final int sc = response.getStatusLine().getStatusCode();
110+
final ClassicHttpResponse response = (ClassicHttpResponse) executor.execute(request).returnResponse();
111+
final int sc = response.getCode();
112112
try (final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
113113
response.getEntity().writeTo(baos);
114114
return Tuple(sc, baos.toString(UTF_8));

exist-core/src/test/java/org/exist/http/urlrewrite/URLRewritingTest.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
package org.exist.http.urlrewrite;
2424

2525
import com.evolvedbinary.j8fu.tuple.Tuple2;
26-
import org.apache.http.HttpResponse;
27-
import org.apache.http.HttpStatus;
28-
import org.apache.http.client.fluent.Request;
29-
import org.apache.http.entity.ContentType;
26+
import org.apache.hc.client5.http.fluent.Request;
27+
import org.apache.hc.core5.http.ClassicHttpResponse;
28+
import org.apache.hc.core5.http.ContentType;
29+
import org.apache.hc.core5.http.HttpStatus;
3030
import org.exist.http.AbstractHttpTest;
3131
import org.exist.test.ExistWebServer;
3232
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
@@ -65,22 +65,22 @@ public void findsParentController() throws IOException {
6565

6666
final String storeDocUri = getRestUri(existWebServer) + TEST_COLLECTION.append(nestedCollectionName).append(docName);
6767
final Request storeRequest = Request
68-
.Put(storeDocUri)
68+
.put(storeDocUri)
6969
.bodyString(testDocument, ContentType.APPLICATION_XML);
70-
final int storeResponseStatusCode = withHttpExecutor(existWebServer, executor -> executor.execute(storeRequest).returnResponse().getStatusLine().getStatusCode());
70+
final int storeResponseStatusCode = withHttpExecutor(existWebServer, executor -> executor.execute(storeRequest).returnResponse().getCode());
7171
assertEquals(HttpStatus.SC_CREATED, storeResponseStatusCode);
7272

7373
final String retrieveDocUri = getAppsUri(existWebServer) + "/" + TEST_COLLECTION_NAME.append(nestedCollectionName).append(docName);
7474
final Request retrieveRequest = Request
75-
.Get(retrieveDocUri);
75+
.get(retrieveDocUri);
7676
final Tuple2<Integer, String> retrieveResponseStatusCodeAndBody = withHttpExecutor(existWebServer, executor -> {
77-
final HttpResponse response = executor.execute(retrieveRequest).returnResponse();
77+
final ClassicHttpResponse response = (ClassicHttpResponse) executor.execute(retrieveRequest).returnResponse();
7878
final String responseBody;
7979
try (final UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream((int)response.getEntity().getContentLength())) {
8080
response.getEntity().writeTo(baos);
8181
responseBody = baos.toString(UTF_8);
8282
}
83-
return Tuple(response.getStatusLine().getStatusCode(), responseBody);
83+
return Tuple(response.getCode(), responseBody);
8484
});
8585
assertEquals(HttpStatus.SC_OK, retrieveResponseStatusCodeAndBody._1.intValue());
8686
assertTrue(retrieveResponseStatusCodeAndBody._2.matches("<controller>.+</controller>"));
@@ -89,11 +89,11 @@ public void findsParentController() throws IOException {
8989
@BeforeClass
9090
public static void setup() throws IOException {
9191
final Request request = Request
92-
.Put(getRestUri(existWebServer) + TEST_COLLECTION + "/" + XQUERY_CONTROLLER_FILENAME)
92+
.put(getRestUri(existWebServer) + TEST_COLLECTION + "/" + XQUERY_CONTROLLER_FILENAME)
9393
.bodyString(TEST_CONTROLLER, ContentType.create("application/xquery"));
9494

9595
final int statusCode = withHttpExecutor(existWebServer, executor ->
96-
executor.execute(request).returnResponse().getStatusLine().getStatusCode()
96+
executor.execute(request).returnResponse().getCode()
9797
);
9898

9999
assertEquals(HttpStatus.SC_CREATED, statusCode);
@@ -102,10 +102,10 @@ public static void setup() throws IOException {
102102
@AfterClass
103103
public static void cleanup() throws IOException {
104104
final Request request = Request
105-
.Delete(getRestUri(existWebServer) + TEST_COLLECTION);
105+
.delete(getRestUri(existWebServer) + TEST_COLLECTION);
106106

107107
final int statusCode = withHttpExecutor(existWebServer, executor ->
108-
executor.execute(request).returnResponse().getStatusLine().getStatusCode()
108+
executor.execute(request).returnResponse().getCode()
109109
);
110110

111111
assertEquals(HttpStatus.SC_OK, statusCode);

0 commit comments

Comments
 (0)