diff --git a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java index 1b32a3312f4..ae7dc400c72 100644 --- a/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java +++ b/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/authentication/token/TokenProvider.java @@ -31,6 +31,8 @@ import java.util.Map; import java.util.Set; import javax.jcr.AccessDeniedException; +import javax.jcr.InvalidItemStateException; +import javax.jcr.ItemNotFoundException; import javax.jcr.NamespaceRegistry; import javax.jcr.Node; import javax.jcr.Property; @@ -76,6 +78,13 @@ public class TokenProvider extends ProtectedItemModifier { private static final char DELIM = '_'; + /** + * Number of attempts to persist a new token node before giving up. Concurrent logins + * of the same user write below a shared token parent and may invalidate each other's + * pending changes (JCR-5095). + */ + private static final int CREATE_TOKEN_MAX_ATTEMPTS = 3; + private static final Set RESERVED_ATTRIBUTES = new HashSet(3); static { RESERVED_ATTRIBUTES.add(TOKEN_ATTRIBUTE); @@ -146,6 +155,39 @@ public TokenInfo createToken(User user, SimpleCredentials sc) throws RepositoryE */ private TokenInfo createToken(User user, Map attributes) throws RepositoryException { String error = "Failed to create login token. "; + // Concurrent logins of the same user add token nodes below the very same token + // parent. A concurrent commit below that parent may invalidate the pending changes + // of this session, so that the token node can neither be saved + // (InvalidItemStateException) nor resolved afterwards (ItemNotFoundException while + // building its path). Both are transient, so retry with a refreshed session, + // analogous to the conflict handling in getTokenParent (JCR-5095). + for (int attempt = 1; ; attempt++) { + try { + return createTokenNode(user, attributes, error); + } catch (InvalidItemStateException | ItemNotFoundException e) { + if (attempt >= CREATE_TOKEN_MAX_ATTEMPTS) { + throw e; + } + log.debug("Conflict while creating login token (attempt {}) -> retrying", attempt, e); + // discard the token node that could not be persisted before retrying + session.refresh(false); + } + } + } + + /** + * Creates and persists a single token node below the token parent of the given user. + * + * @param user The user for which a new token should be created. + * @param attributes The attributes associated with the new token. + * @param error Prefix used for log messages. + * @return A new {@code TokenInfo} or {@code null} if the token could not be created. + * @throws InvalidItemStateException If the token node could not be persisted because + * the token parent was modified concurrently. + * @throws ItemNotFoundException If the token node could not be resolved after saving + * because the token parent was modified concurrently. + */ + private TokenInfo createTokenNode(User user, Map attributes, String error) throws RepositoryException { NodeImpl tokenParent = getTokenParent(user); if (tokenParent != null) { try { diff --git a/jackrabbit-it-osgi/pom.xml b/jackrabbit-it-osgi/pom.xml index 6503f5edd6a..4babd4fcbe4 100644 --- a/jackrabbit-it-osgi/pom.xml +++ b/jackrabbit-it-osgi/pom.xml @@ -71,15 +71,13 @@ test - org.apache.httpcomponents - httpcore-osgi - 4.4.16 + org.apache.httpcomponents.core5 + httpcore5 test - org.apache.httpcomponents - httpclient-osgi - 4.5.14 + org.apache.httpcomponents.client5 + httpclient5 test diff --git a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/OSGiIT.java b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/OSGiIT.java index d30ac442e69..c96edad9552 100644 --- a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/OSGiIT.java +++ b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/OSGiIT.java @@ -17,7 +17,6 @@ package org.apache.jackrabbit.osgi; import static org.junit.Assert.assertEquals; -import static org.ops4j.pax.exam.CoreOptions.bundle; import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; import static org.ops4j.pax.exam.CoreOptions.junitBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; @@ -28,7 +27,6 @@ import java.io.File; import java.io.IOException; import java.net.URI; -import java.net.MalformedURLException; import java.net.URISyntaxException; import javax.inject.Inject; @@ -103,14 +101,8 @@ private String getConfigDir(){ return new File(new File("src", "test"), "config").getAbsolutePath(); } - private Option jarBundles() throws MalformedURLException { - DefaultCompositeOption composite = new DefaultCompositeOption(); - for (File bundle : new File("target", "test-bundles").listFiles()) { - if (bundle.getName().endsWith(".jar") && bundle.isFile()) { - composite.add(bundle(bundle.toURI().toURL().toString())); - } - } - return composite; + private Option jarBundles() throws IOException { + return TestBundles.jarBundles(); } @Inject diff --git a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/TestBundles.java b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/TestBundles.java new file mode 100644 index 00000000000..8e38d0cf24d --- /dev/null +++ b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/TestBundles.java @@ -0,0 +1,149 @@ +/* + * 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.jackrabbit.osgi; + +import static org.ops4j.pax.exam.CoreOptions.bundle; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.options.DefaultCompositeOption; + +/** + * Provisions the JARs assembled into {@code target/test-bundles} for the OSGi tests. + *

+ * Anything that is not already a bundle is repacked as one. HttpComponents publishes no + * OSGi bundles for the 5.x line: {@code httpclient5-osgi} and {@code httpcore5-osgi} stop + * at 5.0-beta, and the plain 5.x JARs carry no {@code Bundle-SymbolicName}, so without + * this the bundles depending on them cannot resolve. This is what the {@code wrap:} URL + * handler does; provisioning {@code wrap:} URLs was tried first and left the Pax Exam + * native container unable to start, so the repack is done here instead. + */ +public final class TestBundles { + + private TestBundles() { + } + + public static Option jarBundles() throws IOException { + DefaultCompositeOption composite = new DefaultCompositeOption(); + for (File jar : new File("target", "test-bundles").listFiles()) { + if (jar.getName().endsWith(".jar") && jar.isFile()) { + File installable = isBundle(jar) ? jar : asBundle(jar); + composite.add(bundle(installable.toURI().toURL().toString())); + } + } + return composite; + } + + private static boolean isBundle(File jar) throws IOException { + try (JarFile jarFile = new JarFile(jar)) { + Manifest manifest = jarFile.getManifest(); + return manifest != null + && manifest.getMainAttributes().getValue("Bundle-SymbolicName") != null; + } + } + + /** + * Repacks a plain JAR as an OSGi bundle exporting every package it contains and + * resolving its own dependencies dynamically. + */ + private static File asBundle(File jar) throws IOException { + File dir = new File("target", "wrapped-bundles"); + dir.mkdirs(); + File wrapped = new File(dir, jar.getName()); + + try (JarFile in = new JarFile(jar)) { + Set packages = new TreeSet(); + for (Enumeration e = in.entries(); e.hasMoreElements();) { + String name = e.nextElement().getName(); + int slash = name.lastIndexOf('/'); + if (name.endsWith(".class") && slash > 0) { + packages.add(name.substring(0, slash).replace('/', '.')); + } + } + + String symbolicName = jar.getName().substring(0, jar.getName().length() - ".jar".length()); + Manifest manifest = new Manifest(); + Attributes main = manifest.getMainAttributes(); + main.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + main.putValue("Bundle-ManifestVersion", "2"); + main.putValue("Bundle-SymbolicName", symbolicName); + main.putValue("Bundle-Version", bundleVersion(in)); + main.putValue("Export-Package", String.join(",", packages)); + main.putValue("DynamicImport-Package", "*"); + + byte[] buffer = new byte[8192]; + try (JarOutputStream out = new JarOutputStream(new FileOutputStream(wrapped), manifest)) { + for (Enumeration e = in.entries(); e.hasMoreElements();) { + JarEntry entry = e.nextElement(); + if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) { + continue; + } + out.putNextEntry(new JarEntry(entry.getName())); + try (InputStream content = in.getInputStream(entry)) { + copy(content, out, buffer); + } + out.closeEntry(); + } + } + } + return wrapped; + } + + /** + * Derives an OSGi-legal Bundle-Version from the JAR's Implementation-Version. The + * assembly renames JARs to {@code .jar}, so the file name carries no + * version. + */ + private static String bundleVersion(JarFile jar) throws IOException { + Manifest manifest = jar.getManifest(); + String version = manifest == null + ? null : manifest.getMainAttributes().getValue("Implementation-Version"); + if (version == null) { + return "0.0.0"; + } + // OSGi wants major.minor.micro; anything after the third segment is a qualifier + String[] parts = version.split("[.-]"); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < 3; i++) { + result.append(i < parts.length && parts[i].matches("\\d+") ? parts[i] : "0"); + if (i < 2) { + result.append('.'); + } + } + return result.toString(); + } + + private static void copy(InputStream in, OutputStream out, byte[] buffer) throws IOException { + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + } +} diff --git a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_4_OSGiIT.java b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_4_OSGiIT.java index b3371f8e328..8c284aa1df7 100644 --- a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_4_OSGiIT.java +++ b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_4_OSGiIT.java @@ -17,7 +17,6 @@ package org.apache.jackrabbit.osgi.slf4j2; import static org.junit.Assert.assertEquals; -import static org.ops4j.pax.exam.CoreOptions.bundle; import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; import static org.ops4j.pax.exam.CoreOptions.junitBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; @@ -115,14 +114,9 @@ private String getConfigDir(){ return new File(new File("src", "test"), "config").getAbsolutePath(); } - private Option jarBundles() throws MalformedURLException { - DefaultCompositeOption composite = new DefaultCompositeOption(); - for (File bundle : new File("target", "test-bundles").listFiles()) { - if (bundle.getName().endsWith(".jar") && bundle.isFile()) { - composite.add(bundle(bundle.toURI().toURL().toString())); - } - } - return composite; + private Option jarBundles() throws IOException { + // shared with OSGiIT: repacks the HttpClient 5 JARs, which are not bundles + return org.apache.jackrabbit.osgi.TestBundles.jarBundles(); } @Inject diff --git a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_9_OSGiIT.java b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_9_OSGiIT.java index a814892f618..043c43aa568 100644 --- a/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_9_OSGiIT.java +++ b/jackrabbit-it-osgi/src/test/java/org/apache/jackrabbit/osgi/slf4j2/Slf4j_v2_Tika_v2_9_OSGiIT.java @@ -17,7 +17,6 @@ package org.apache.jackrabbit.osgi.slf4j2; import static org.junit.Assert.assertEquals; -import static org.ops4j.pax.exam.CoreOptions.bundle; import static org.ops4j.pax.exam.CoreOptions.frameworkProperty; import static org.ops4j.pax.exam.CoreOptions.junitBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; @@ -115,14 +114,9 @@ private String getConfigDir(){ return new File(new File("src", "test"), "config").getAbsolutePath(); } - private Option jarBundles() throws MalformedURLException { - DefaultCompositeOption composite = new DefaultCompositeOption(); - for (File bundle : new File("target", "test-bundles").listFiles()) { - if (bundle.getName().endsWith(".jar") && bundle.isFile()) { - composite.add(bundle(bundle.toURI().toURL().toString())); - } - } - return composite; + private Option jarBundles() throws IOException { + // shared with OSGiIT: repacks the HttpClient 5 JARs, which are not bundles + return org.apache.jackrabbit.osgi.TestBundles.jarBundles(); } @Inject diff --git a/jackrabbit-it-osgi/test-bundles.xml b/jackrabbit-it-osgi/test-bundles.xml index 367af71f31a..94b2a09592d 100644 --- a/jackrabbit-it-osgi/test-bundles.xml +++ b/jackrabbit-it-osgi/test-bundles.xml @@ -36,8 +36,8 @@ org.apache.jackrabbit:jackrabbit-jcr-commons org.apache.jackrabbit:jackrabbit-spi org.apache.jackrabbit:jackrabbit-spi-commons - org.apache.httpcomponents:httpclient-osgi - org.apache.httpcomponents:httpcore-osgi + org.apache.httpcomponents.client5:httpclient5 + org.apache.httpcomponents.core5:httpcore5 org.apache.jackrabbit:jackrabbit-webdav org.apache.jackrabbit:jackrabbit-jcr-server diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java index e009efff65e..b2fa2b82f1d 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java @@ -24,13 +24,13 @@ import java.util.List; import java.util.Set; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatus; import org.apache.jackrabbit.webdav.MultiStatusResponse; @@ -60,8 +60,8 @@ public class BindTest extends WebDAVTestBase { // http://greenbytes.de/tech/webdav/rfc5842.html#rfc.section.8.1 public void testOptions() throws IOException { HttpOptions options = new HttpOptions(this.uri); - HttpResponse response = this.client.execute(options, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, options, this.context); + int status = response.getCode(); assertEquals(200, status); Set allow = options.getAllowedMethods(response); Set complianceClasses = options.getDavComplianceClasses(response); @@ -80,23 +80,23 @@ public void testResourceId() throws IOException, DavException, URISyntaxExceptio int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); HttpPut put = new HttpPut(testuri1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); // enabling version control always makes the resource referenceable HttpVersionControl versioncontrol = new HttpVersionControl(testuri1); - status = this.client.execute(versioncontrol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, versioncontrol, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201); URI resourceId = getResourceId(testuri1); HttpMove move = new HttpMove(testuri1, testuri2, true); - status = this.client.execute(move, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, move, this.context).getCode(); assertEquals(201, status); URI resourceId2 = getResourceId(testuri2); @@ -113,8 +113,8 @@ private URI getResourceId(String uri) throws IOException, DavException, URISynta DavPropertyNameSet names = new DavPropertyNameSet(); names.add(BindConstants.RESOURCEID); HttpPropfind propfind = new HttpPropfind(uri, names, 0); - HttpResponse response = this.client.execute(propfind, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, propfind, this.context); + int status = response.getCode(); assertEquals(207, status); MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus(response); MultiStatusResponse[] responses = multistatus.getResponses(); @@ -133,8 +133,8 @@ private DavProperty getParentSet(String uri) throws IOException, DavException, U DavPropertyNameSet names = new DavPropertyNameSet(); names.add(BindConstants.PARENTSET); HttpPropfind propfind = new HttpPropfind(uri, names, 0); - HttpResponse response = this.client.execute(propfind, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, propfind, this.context); + int status = response.getCode(); assertEquals(207, status); MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus(response); MultiStatusResponse[] responses = multistatus.getResponses(); @@ -153,54 +153,54 @@ public void testSimpleBind() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //create new binding of R with path bindtest2/res2 HttpBind bind = new HttpBind(subcol2, new BindInfo(testres1, "res2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(201, status); //check if both bindings report the same DAV:resource-id assertEquals(this.getResourceId(testres1), this.getResourceId(testres2)); //compare representations retrieved with both paths HttpGet get = new HttpGet(testres1); - HttpResponse resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); //modify R using the new path put = new HttpPut(testres2); put.setEntity(new StringEntity("bar", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); //compare representations retrieved with both paths get = new HttpGet(testres1); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("bar", EntityUtils.toString(resp.getEntity())); get = new HttpGet(testres2); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("bar", EntityUtils.toString(resp.getEntity())); } finally { @@ -217,44 +217,44 @@ public void testRebind() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); // enabling version control always makes the resource referenceable HttpVersionControl versioncontrol = new HttpVersionControl(testres1); - status = this.client.execute(versioncontrol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, versioncontrol, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201); URI r1 = this.getResourceId(testres1); HttpGet get = new HttpGet(testres1); - HttpResponse resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); //rebind R with path bindtest2/res2 HttpRebind rebind = new HttpRebind(subcol2, new RebindInfo(testres1, "res2")); - status = this.client.execute(rebind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, rebind, this.context).getCode(); assertEquals(201, status); URI r2 = this.getResourceId(testres2); get = new HttpGet(testres2); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); @@ -263,7 +263,7 @@ public void testRebind() throws Exception { //verify that the initial binding is gone HttpHead head = new HttpHead(testres1); - status = this.client.execute(head, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, head, this.context).getCode(); assertEquals(404, status); } finally { delete(testcol); @@ -279,55 +279,55 @@ public void testBindOverwrite() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //create new resource R' with path bindtest2/res2 put = new HttpPut(testres2); put.setEntity(new StringEntity("bar", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //try to create new binding of R with path bindtest2/res2 and Overwrite:F HttpBind bind = new HttpBind(subcol2, new BindInfo(testres1, "res2")); bind.addHeader("Overwrite", "F"); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(412, status); //verify that bindtest2/res2 still points to R' HttpGet get = new HttpGet(testres2); - HttpResponse resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("bar", EntityUtils.toString(resp.getEntity())); //create new binding of R with path bindtest2/res2 bind = new HttpBind(subcol2, new BindInfo(testres1, "res2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); //verify that bindtest2/res2 now points to R get = new HttpGet(testres2); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); //verify that the initial binding is still there HttpHead head = new HttpHead(testres1); - status = this.client.execute(head, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, head, this.context).getCode(); assertEquals(200, status); } finally { delete(testcol); @@ -343,60 +343,60 @@ public void testRebindOverwrite() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path testSimpleBind/bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); // enabling version control always makes the resource referenceable HttpVersionControl versioncontrol = new HttpVersionControl(testres1); - status = this.client.execute(versioncontrol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, versioncontrol, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201); //create new resource R' with path testSimpleBind/bindtest2/res2 put = new HttpPut(testres2); put.setEntity(new StringEntity("bar", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //try rebind R with path testSimpleBind/bindtest2/res2 and Overwrite:F HttpRebind rebind = new HttpRebind(subcol2, new RebindInfo(testres1, "res2")); rebind.addHeader("Overwrite", "F"); - status = this.client.execute(rebind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, rebind, this.context).getCode(); assertEquals(412, status); //verify that testSimpleBind/bindtest2/res2 still points to R' HttpGet get = new HttpGet(testres2); - HttpResponse resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("bar", EntityUtils.toString(resp.getEntity())); //rebind R with path testSimpleBind/bindtest2/res2 rebind = new HttpRebind(subcol2, new RebindInfo(testres1, "res2")); - status = this.client.execute(rebind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, rebind, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); //verify that testSimpleBind/bindtest2/res2 now points to R get = new HttpGet(testres2); - resp = this.client.execute(get, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, get, this.context); + status = resp.getCode(); assertEquals(200, status); assertEquals("foo", EntityUtils.toString(resp.getEntity())); //verify that the initial binding is gone HttpHead head = new HttpHead(testres1); - status = this.client.execute(head, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, head, this.context).getCode(); assertEquals(404, status); } finally { delete(testcol); @@ -412,24 +412,24 @@ public void testParentSet() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path testSimpleBind/bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //create new binding of R with path testSimpleBind/bindtest2/res2 HttpBind bind = new HttpBind(subcol2, new BindInfo(testres1, "res2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(201, status); //check if both bindings report the same DAV:resource-id assertEquals(this.getResourceId(testres1), this.getResourceId(testres2)); @@ -483,43 +483,43 @@ public void testBindCollections() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(a1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(a2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create collection resource C mkcol = new HttpMkcol(b1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(c1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create plain resource R HttpPut put = new HttpPut(x1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //create new binding of C with path a2/b2 HttpBind bind = new HttpBind(a2, new BindInfo(b1, "b2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(201, status); //check if both bindings report the same DAV:resource-id assertEquals(this.getResourceId(b1), this.getResourceId(b2)); mkcol = new HttpMkcol(c2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new binding of R with path a2/b2/c2/r2 bind = new HttpBind(c2, new BindInfo(x1, "x2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(201, status); //check if both bindings report the same DAV:resource-id assertEquals(this.getResourceId(x1), this.getResourceId(x2)); @@ -550,41 +550,41 @@ public void testUnbind() throws Exception { int status; try { HttpMkcol mkcol = new HttpMkcol(testcol); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol1); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); mkcol = new HttpMkcol(subcol2); - status = this.client.execute(mkcol, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, mkcol, this.context).getCode(); assertEquals(201, status); //create new resource R with path testSimpleBind/bindtest1/res1 HttpPut put = new HttpPut(testres1); put.setEntity(new StringEntity("foo", ContentType.create("text/plain", "UTF-8"))); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); //create new binding of R with path testSimpleBind/bindtest2/res2 HttpBind bind = new HttpBind(subcol2, new BindInfo(testres1, "res2")); - status = this.client.execute(bind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, bind, this.context).getCode(); assertEquals(201, status); //check if both bindings report the same DAV:resource-id assertEquals(this.getResourceId(testres1), this.getResourceId(testres2)); //remove new path HttpUnbind unbind = new HttpUnbind(subcol2, new UnbindInfo("res2")); - status = this.client.execute(unbind, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, unbind, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); //verify that the new binding is gone HttpHead head = new HttpHead(testres2); - status = this.client.execute(head, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, head, this.context).getCode(); assertEquals(404, status); //verify that the initial binding is still there head = new HttpHead(testres1); - status = this.client.execute(head, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, head, this.context).getCode(); assertEquals(200, status); } finally { delete(testcol); diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ConditionalsTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ConditionalsTest.java index cfb0af7bd40..1c9e8675a2a 100755 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ConditionalsTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ConditionalsTest.java @@ -19,13 +19,13 @@ import java.io.IOException; import java.text.ParseException; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; public class ConditionalsTest extends WebDAVTestBase { @@ -39,8 +39,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { { HttpPut put = new HttpPut(testUri); put.setEntity(new StringEntity("foobar")); - HttpResponse response = this.client.execute(put, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); + int status = response.getCode(); assertEquals(201, status); } @@ -51,8 +51,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { Header lm = null; { HttpHead head = new HttpHead(testUri); - HttpResponse response = this.client.execute(head, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, head, this.context); + int status = response.getCode(); assertEquals(200, status); lm = response.getFirstHeader("last-modified"); assertNotNull(lm); @@ -65,8 +65,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { { HttpGet get = new HttpGet(testUri); get.setHeader("If-Modified-Since", lm.getValue()); - HttpResponse response = this.client.execute(get, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, get, this.context); + int status = response.getCode(); assertEquals(304, status); if (etag != null) { Header newetag = response.getFirstHeader("etag"); @@ -79,8 +79,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { { HttpHead head = new HttpHead(testUri); head.setHeader("If-Modified-Since", lm.getValue()); - HttpResponse response = this.client.execute(head, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, head, this.context); + int status = response.getCode(); assertEquals(304, status); if (etag != null) { Header newetag = response.getFirstHeader("etag"); @@ -93,8 +93,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { { HttpHead head = new HttpHead(testUri); head.setHeader("If-Modified-Since", "broken"); - HttpResponse response = this.client.execute(head, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, head, this.context); + int status = response.getCode(); assertEquals(200, status); } @@ -103,8 +103,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { HttpGet req = new HttpGet(testUri); req.addHeader("If-Modified-Since", lm.getValue()); req.addHeader("If-Modified-Since", "foo"); - HttpResponse response = this.client.execute(req, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, req, this.context); + int status = response.getCode(); assertEquals(200, status); EntityUtils.consume(response.getEntity()); } @@ -120,8 +120,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { // verify last modified did not change { HttpHead head = new HttpHead(testUri); - HttpResponse response = this.client.execute(head, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, head, this.context); + int status = response.getCode(); assertEquals(200, status); Header newlm = response.getFirstHeader("last-modified"); assertNotNull(newlm); @@ -133,8 +133,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { HttpPut put = new HttpPut(testUri); put.setHeader("If-Unmodified-Since", lm.getValue()); put.setEntity(new StringEntity("qux")); - HttpResponse response = this.client.execute(put, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); + int status = response.getCode(); assertEquals(204, status); } @@ -143,8 +143,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { HttpPut put = new HttpPut(testUri); put.setHeader("If-Unmodified-Since", lm.getValue()); put.setEntity(new StringEntity("lazydog")); - HttpResponse response = this.client.execute(put, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); + int status = response.getCode(); assertEquals(412, status); } @@ -154,8 +154,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { put.addHeader("If-Unmodified-Since", lm.getValue()); put.addHeader("If-Unmodified-Since", "foo"); put.setEntity(new StringEntity("qux")); - HttpResponse response = this.client.execute(put, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); + int status = response.getCode(); assertEquals(204, status); } } finally { @@ -166,8 +166,8 @@ public void testPutCheckLastModified() throws IOException, ParseException { public void testGetCollectionEtag() throws IOException, ParseException { String testUri = this.uri.toString() + (this.uri.toString().endsWith("/") ? "" : "/"); HttpGet get = new HttpGet(testUri); - HttpResponse response = this.client.execute(get, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, get, this.context); + int status = response.getCode(); assertEquals(200, status); Header etag = response.getFirstHeader("etag"); if (etag != null) { diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java index d7553dcf9bc..770382130b7 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ContentCodingTest.java @@ -26,13 +26,14 @@ import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPOutputStream; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.message.BasicHeader; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicHeader; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatusResponse; @@ -45,7 +46,7 @@ public void testPutNoContentCoding() throws IOException { try { HttpPut put = new HttpPut(testUri); put.setEntity(new StringEntity("foobar")); - int status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals(201, status); } finally { delete(testUri); @@ -57,10 +58,9 @@ public void testPutUnknownContentCoding() throws IOException { int status = -1; try { HttpPut put = new HttpPut(testUri); - StringEntity entity = new StringEntity("foobarfoobarfoobar"); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "qux")); + StringEntity entity = new StringEntity("foobarfoobarfoobar", ContentType.TEXT_PLAIN, "qux", false); put.setEntity(entity); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertTrue("server must signal error for unknown content coding, got: " + status, status == 415); } finally { if (status / 2 == 100) { @@ -77,16 +77,15 @@ public void testPutGzipContentCoding() throws IOException { HttpPut put = new HttpPut(testUri); byte gzbytes[] = asGzipOctets(bytes); assertTrue(gzbytes.length != bytes.length); - ByteArrayEntity entity = new ByteArrayEntity(gzbytes); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip")); + ByteArrayEntity entity = new ByteArrayEntity(gzbytes, null, "gzip"); put.setEntity(entity); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertTrue("server create or signal error, got: " + status, status == 201 || status == 415); if (status / 2 == 100) { // check length HttpHead head = new HttpHead(testUri); - HttpResponse response = this.client.execute(head, this.context); - assertEquals(200, response.getStatusLine().getStatusCode()); + ClassicHttpResponse response = this.client.executeOpen(null, head, this.context); + assertEquals(200, response.getCode()); assertEquals(bytes.length, Integer.parseInt(response.getFirstHeader("Content-Length").getValue())); } } finally { @@ -98,8 +97,8 @@ public void testPutGzipContentCoding() throws IOException { public void testPropfindNoContentCoding() throws IOException, DavException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - HttpResponse response = this.client.execute(propfind, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, propfind, this.context); + int status = response.getCode(); assertEquals(207, status); List encodings = getContentCodings(response); assertTrue("Accept should list 'gzip' but did not: " + encodings, encodings.contains("gzip")); @@ -109,8 +108,8 @@ public void testPropfindNoContentCoding() throws IOException, DavException { public void testPropfindAcceptReponseEncoding() throws IOException, DavException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); propfind.setHeader(new BasicHeader("Accept-Encoding", "gzip;q=0.555")); - HttpResponse response = this.client.execute(propfind, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, propfind, this.context); + int status = response.getCode(); assertEquals(207, status); MultiStatusResponse[] responses = propfind.getResponseBodyAsMultiStatus(response).getResponses(); assertEquals(1, responses.length); @@ -120,11 +119,10 @@ public void testPropfindAcceptReponseEncoding() throws IOException, DavException public void testPropfindUnknownContentCoding() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - StringEntity entity = new StringEntity(PF); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "qux")); + StringEntity entity = new StringEntity(PF, ContentType.TEXT_PLAIN, "qux", false); propfind.setEntity(entity); - HttpResponse response = this.client.execute(propfind, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, propfind, this.context); + int status = response.getCode(); assertTrue("server must signal error for unknown content coding, got: " + status, status == 415); List encodings = getContentCodings(response); assertTrue("Accept should list 'gzip' but did not: " + encodings, encodings.contains("gzip")); @@ -133,10 +131,9 @@ public void testPropfindUnknownContentCoding() throws IOException { public void testPropfindGzipContentCoding() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(PF)); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip")); + ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(PF), null, "gzip"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(207, status); } @@ -144,47 +141,42 @@ public void testPropfindGzipContentCoding() throws IOException { // coding name public void testPropfindGzipContentCodingTwice() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF))); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "gziP,, Gzip")); + ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF)), null, "gziP,, Gzip"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(207, status); } // double encoded, but only when encoding in header field public void testPropfindGzipContentCodingBadSpec() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF))); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip")); + ByteArrayEntity entity = new ByteArrayEntity(asGzipOctets(asGzipOctets(PF)), null, "gzip"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(400, status); } public void testPropfindDeflateContentCoding() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(PF)); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "deflate")); + ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(PF), null, "deflate"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(207, status); } public void testPropfindGzipDeflateContentCoding() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(asGzipOctets(PF))); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "gzip, deflate")); + ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(asGzipOctets(PF)), null, "gzip, deflate"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(207, status); } public void testPropfindGzipDeflateContentCodingMislabeled() throws IOException { HttpPropfind propfind = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, 0); - ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(asGzipOctets(PF))); - entity.setContentEncoding(new BasicHeader("Content-Encoding", "deflate, gzip")); + ByteArrayEntity entity = new ByteArrayEntity(asDeflateOctets(asGzipOctets(PF)), null, "deflate, gzip"); propfind.setEntity(entity); - int status = this.client.execute(propfind, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, propfind, this.context).getCode(); assertEquals(400, status); } @@ -214,7 +206,7 @@ private static byte[] asDeflateOctets(byte[] input) throws IOException { return bos.toByteArray(); } - private static List getContentCodings(HttpResponse response) { + private static List getContentCodings(ClassicHttpResponse response) { List result = Collections.emptyList(); for (Header l : response.getHeaders("Accept-Encoding")) { for (String h : l.getValue().split(",")) { diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/HttpsSelfSignedTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/HttpsSelfSignedTest.java index 6cee0eeb801..2a4d9697942 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/HttpsSelfSignedTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/HttpsSelfSignedTest.java @@ -21,9 +21,9 @@ import javax.net.ssl.SSLHandshakeException; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.io.entity.StringEntity; public class HttpsSelfSignedTest extends WebDAVTestBase { @@ -34,7 +34,7 @@ public void testPutCheckLastModified() throws IOException, ParseException { String testUri = this.httpsUri.toString(); HttpPut put = new HttpPut(testUri); put.setEntity(new StringEntity("foobar")); - HttpResponse response = this.client.execute(put, this.context); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); fail("should failt with SSLHandshakeException, but got: " + response); } catch (SSLHandshakeException expected) { } diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ProppatchTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ProppatchTest.java index dc7d0758cd5..64b20ab219a 100755 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ProppatchTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/ProppatchTest.java @@ -18,10 +18,10 @@ import java.io.IOException; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatusResponse; import org.apache.jackrabbit.webdav.client.methods.HttpPropfind; @@ -44,7 +44,7 @@ public void testPropPatchSurrogate() throws IOException, DavException { HttpPut put = new HttpPut(testuri); put.setEntity(new StringEntity("1")); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals("status: " + status, 201, status); DavPropertyName name = DavPropertyName.create("foobar", Namespace.EMPTY_NAMESPACE); @@ -52,16 +52,16 @@ public void testPropPatchSurrogate() throws IOException, DavException { DavProperty foobar = new DefaultDavProperty<>(name, "\uD83D\uDCA9"); props.add(foobar); HttpProppatch proppatch = new HttpProppatch(testuri, props, new DavPropertyNameSet()); - HttpResponse resp = this.client.execute(proppatch, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, proppatch, this.context); + status = resp.getCode(); assertEquals(207, status); EntityUtils.consume(resp.getEntity()); DavPropertyNameSet names = new DavPropertyNameSet(); names.add(name); HttpPropfind propfind = new HttpPropfind(testuri, names, 0); - resp = this.client.execute(propfind, this.context); - status = resp.getStatusLine().getStatusCode(); + resp = this.client.executeOpen(null, propfind, this.context); + status = resp.getCode(); assertEquals(207, status); MultiStatusResponse[] responses = propfind.getResponseBodyAsMultiStatus(resp).getResponses(); assertEquals(1, responses.length); diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/PutTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/PutTest.java index 056dc014b9e..7238f71985f 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/PutTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/PutTest.java @@ -19,9 +19,9 @@ import java.io.IOException; import java.text.ParseException; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.io.entity.StringEntity; /** * Test cases for HTTP PUT method @@ -33,8 +33,8 @@ public void testPutWithContentRange() throws IOException, ParseException { HttpPut put = new HttpPut(testUri); put.addHeader("Content-Range", "bytes 0-5/6"); put.setEntity(new StringEntity("foobar")); - HttpResponse response = this.client.execute(put, this.context); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, put, this.context); + int status = response.getCode(); assertEquals(400, status); } } diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918DestinationHeaderTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918DestinationHeaderTest.java index ec2a6718586..71f420300a9 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918DestinationHeaderTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918DestinationHeaderTest.java @@ -20,10 +20,10 @@ import java.net.URI; import java.net.URISyntaxException; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.jackrabbit.webdav.client.methods.HttpMove; /** @@ -40,48 +40,48 @@ public void testMove() throws IOException, URISyntaxException { // make sure the scheme is removed assertFalse(destinationpath.contains(":")); - HttpRequestBase requestBase = null; + HttpUriRequestBase requestBase = null; try { requestBase = new HttpPut(testuri); - int status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); // try to move outside the servlet's name space requestBase = new HttpMove(testuri, "/foobar", true); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 502); - requestBase.releaseConnection(); + requestBase.reset(); // try a relative path requestBase = new HttpMove(testuri, "foobar", true); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 400); - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpMove(testuri, destinationpath, true); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpHead(destinationuri); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200); - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpHead(testuri); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 404); } finally { - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpDelete(testuri); - int status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204 || status == 404); - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpDelete(destinationuri); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204 || status == 404); - requestBase.releaseConnection(); + requestBase.reset(); } } } diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918IfHeaderTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918IfHeaderTest.java index f6507321338..585ba1a0134 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918IfHeaderTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918IfHeaderTest.java @@ -20,11 +20,11 @@ import java.net.URI; import java.net.URISyntaxException; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.entity.StringEntity; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.jackrabbit.webdav.client.methods.HttpLock; import org.apache.jackrabbit.webdav.lock.LockInfo; import org.apache.jackrabbit.webdav.lock.Scope; @@ -46,17 +46,17 @@ public void testPutIfEtag() throws IOException { String condition = "<" + testuri + "> ([" + "\"an-etag-this-testcase-invented\"" + "])"; put.setEntity(new StringEntity("1")); put.setHeader("If", condition); - int status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals("status: " + status, 412, status); - put.releaseConnection(); + put.reset(); } finally { - put.releaseConnection(); + put.reset(); HttpDelete delete = new HttpDelete(testuri); - int status = this.client.execute(delete, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, delete, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204 || status == 404); - delete.releaseConnection(); + delete.reset(); } } @@ -65,78 +65,78 @@ public void testPutIfLockToken() throws IOException, URISyntaxException { String testuri = this.root + "iflocktest"; String locktoken = null; - HttpRequestBase requestBase = null; + HttpUriRequestBase requestBase = null; try { requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("1")); - int status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 201 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpLock(testuri, new LockInfo( Scope.EXCLUSIVE, Type.WRITE, "testcase", 10000, true)); - HttpResponse response = this.client.execute(requestBase, this.context); - status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = this.client.executeOpen(null, requestBase, this.context); + status = response.getCode(); assertEquals("status", 200, status); locktoken = ((HttpLock)requestBase).getLockToken(response); assertNotNull(locktoken); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite without lock token requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("2")); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertEquals("status: " + status, 423, status); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite using bad lock token requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("2")); requestBase.setHeader("If", "(<" + "DAV:foobar" + ">)"); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertEquals("status: " + status, 412, status); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite using correct lock token, using No-Tag-list format requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("2")); requestBase.setHeader("If", "(<" + locktoken + ">)"); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite using correct lock token, using Tagged-list format // and full URI requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("3")); requestBase.setHeader("If", "<" + testuri + ">" + "(<" + locktoken + ">)"); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite using correct lock token, using Tagged-list format // and absolute path only requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("4")); requestBase.setHeader("If", "<" + new URI(testuri).getRawPath() + ">" + "(<" + locktoken + ">)"); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); - requestBase.releaseConnection(); + requestBase.reset(); // try to overwrite using correct lock token, using Tagged-list format // and bad path requestBase = new HttpPut(testuri); ((HttpPut)requestBase).setEntity(new StringEntity("5")); requestBase.setHeader("If", "" + "(<" + locktoken + ">)"); - status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 404 || status == 412); } finally { - requestBase.releaseConnection(); + requestBase.reset(); requestBase = new HttpDelete(testuri); if (locktoken != null) { requestBase.setHeader("If", "(<" + locktoken + ">)"); } - int status = this.client.execute(requestBase, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, requestBase, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204 || status == 404); } } diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918PropfindTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918PropfindTest.java index a1c6cbd5f92..0185c040d00 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918PropfindTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RFC4918PropfindTest.java @@ -18,9 +18,9 @@ import java.io.IOException; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.StringEntity; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatus; @@ -40,7 +40,7 @@ public class RFC4918PropfindTest extends WebDAVTestBase { public void testOptions() throws IOException { HttpOptions options = new HttpOptions(this.root); - HttpResponse response = this.client.execute(options, this.context); + ClassicHttpResponse response = this.client.executeOpen(null, options, this.context); assertTrue(options.getDavComplianceClasses(response).contains("3")); } @@ -52,14 +52,14 @@ public void testPropfindInclude() throws IOException, DavException { try { HttpPut put = new HttpPut(testuri); put.setEntity(new StringEntity("1")); - status = this.client.execute(put, this.context).getStatusLine().getStatusCode(); + status = this.client.executeOpen(null, put, this.context).getCode(); assertEquals("status: " + status, 201, status); DavPropertyNameSet names = new DavPropertyNameSet(); names.add(DeltaVConstants.COMMENT); HttpPropfind propfind = new HttpPropfind(testuri, DavConstants.PROPFIND_ALL_PROP_INCLUDE, names, 0); - HttpResponse resp = this.client.execute(propfind, this.context); - status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, propfind, this.context); + status = resp.getCode(); assertEquals(207, status); MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus(resp); diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RemotingTest.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RemotingTest.java index ee4a780685e..8eedee1d3d8 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RemotingTest.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/RemotingTest.java @@ -18,7 +18,7 @@ import java.io.IOException; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatus; @@ -43,8 +43,8 @@ public void testRoot() throws IOException, DavException { names.add(pntn); HttpPropfind propfind = new HttpPropfind(testuri, DavConstants.PROPFIND_BY_PROPERTY, names, 0); - HttpResponse resp = this.client.execute(propfind, this.context); - int status = resp.getStatusLine().getStatusCode(); + ClassicHttpResponse resp = this.client.executeOpen(null, propfind, this.context); + int status = resp.getCode(); assertEquals(207, status); MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus(resp); diff --git a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/WebDAVTestBase.java b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/WebDAVTestBase.java index 97c9347b8f9..ed70e88dcb9 100644 --- a/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/WebDAVTestBase.java +++ b/jackrabbit-jcr-server/src/test/java/org/apache/jackrabbit/webdav/server/WebDAVTestBase.java @@ -27,19 +27,21 @@ import javax.servlet.ServletException; import org.apache.commons.io.IOUtils; -import org.apache.http.HttpHost; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicAuthCache; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.util.Timeout; +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.auth.AuthCache; +import org.apache.hc.client5.http.auth.CredentialsStore; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.classic.methods.HttpDelete; +import org.apache.hc.client5.http.protocol.HttpClientContext; +import org.apache.hc.client5.http.impl.auth.BasicScheme; +import org.apache.hc.client5.http.impl.auth.BasicAuthCache; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.jackrabbit.core.RepositoryContext; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.jackrabbit.server.remoting.davex.JcrRemotingServlet; @@ -83,6 +85,9 @@ public class WebDAVTestBase extends AbstractJCRTest { public HttpClient client; public HttpClientContext context; + // see the comment on the HTTPS connector below + private static final String LOOPBACK = "127.0.0.1"; + private static final String KEYSTORE = "keystore"; private static final String KEYSTOREPW = "geheimer"; @@ -153,7 +158,12 @@ public Repository getRepository() { sslContextFactory.setTrustStorePassword(KEYSTOREPW); SslConnectionFactory cfac = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); httpsConnector = new ServerConnector(server, cfac, new HttpConnectionFactory(new HttpConfiguration())); - httpsConnector.setHost("localhost"); + // Bind to a literal address rather than "localhost": where localhost + // resolves to both 127.0.0.1 and ::1, HttpClient 5 treats a TLS handshake + // failure on the first address as a reason to try the next one, so the + // SSLHandshakeException that HttpsSelfSignedTest asserts on would be + // replaced by a connection failure against the unbound address. + httpsConnector.setHost(LOOPBACK); httpsConnector.setPort(0); server.addConnector(httpsConnector); } @@ -168,21 +178,30 @@ public Repository getRepository() { this.uri = new URI("http", null, "localhost", httpConnector.getLocalPort(), "/default/", null, null); this.remotingUri = new URI("http", null, "localhost", httpConnector.getLocalPort(), REMOTING_PREFIX + "/", null, null); - this.httpsUri = new URI("https", null, "localhost", httpsConnector.getLocalPort(), "/default/", null, null); + this.httpsUri = new URI("https", null, LOOPBACK, httpsConnector.getLocalPort(), "/default/", null, null); this.root = this.uri.toASCIIString(); + // HttpClient 5 keeps a pooled connection leased until the response is closed or + // its entity consumed, including for status-only responses that HttpClient 4 + // released automatically. These tests read status codes without closing the + // response, so the default pool of 5 per route is exhausted quickly; size the + // pool for the busiest test class and fail fast rather than block for the + // default three minutes if it is ever exhausted anyway. PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); - //cm.setMaxTotal(100); + cm.setDefaultMaxPerRoute(100); + cm.setMaxTotal(100); HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort()); - CredentialsProvider credsProvider = new BasicCredentialsProvider(); - credsProvider.setCredentials( - new AuthScope(targetHost.getHostName(), targetHost.getPort()), - new UsernamePasswordCredentials("admin", "admin")); + UsernamePasswordCredentials credentials = + new UsernamePasswordCredentials("admin", "admin".toCharArray()); + CredentialsStore credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials(new AuthScope(targetHost), credentials); AuthCache authCache = new BasicAuthCache(); - // Generate BASIC scheme object and add it to the local auth cache + // Generate BASIC scheme object and add it to the local auth cache; + // HttpClient 5 only pre-authenticates from a primed scheme BasicScheme basicAuth = new BasicScheme(); + basicAuth.initPreemptive(credentials); authCache.put(targetHost, basicAuth); // Add AuthCache to the execution context @@ -190,14 +209,19 @@ public Repository getRepository() { this.context.setCredentialsProvider(credsProvider); this.context.setAuthCache(authCache); - this.client = HttpClients.custom().setConnectionManager(cm).build(); + this.client = HttpClients.custom() + .setConnectionManager(cm) + .setDefaultRequestConfig(RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofSeconds(10)) + .build()) + .build(); super.setUp(); } protected void delete(String uri) throws IOException { HttpDelete delete = new HttpDelete(uri); - int status = this.client.execute(delete, this.context).getStatusLine().getStatusCode(); + int status = this.client.executeOpen(null, delete, this.context).getCode(); assertTrue("status: " + status, status == 200 || status == 204); } diff --git a/jackrabbit-parent/pom.xml b/jackrabbit-parent/pom.xml index 9c888e264e3..3a9ce62cb32 100644 --- a/jackrabbit-parent/pom.xml +++ b/jackrabbit-parent/pom.xml @@ -60,6 +60,8 @@ 1.22.24 9.4.58.v20250814 2.4.1 + 5.6.1 + 5.4.2 ${project.build.sourceEncoding} 1.7.36 1.7.36 @@ -249,9 +251,8 @@ https://s.apache.org/jcr-2.0-javadoc/ https://jackrabbit.apache.org/oak/docs/apidocs - https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/ - https://hc.apache.org/httpcomponents-client-4.5.x/current/httpmime/apidocs/ - https://hc.apache.org/httpcomponents-core-4.4.x/current/httpcore/apidocs/ + https://hc.apache.org/httpcomponents-client-5.6.x/current/httpclient5/apidocs/ + https://hc.apache.org/httpcomponents-core-5.4.x/current/httpcore5/apidocs/ @@ -631,6 +632,16 @@ mockito-core 5.23.0 + + org.apache.httpcomponents.client5 + httpclient5 + ${httpcomponents.client5.version} + + + org.apache.httpcomponents.core5 + httpcore5 + ${httpcomponents.core5.version} + diff --git a/jackrabbit-spi2dav/pom.xml b/jackrabbit-spi2dav/pom.xml index ef70fbeda1e..3df3fb6b25f 100644 --- a/jackrabbit-spi2dav/pom.xml +++ b/jackrabbit-spi2dav/pom.xml @@ -41,6 +41,8 @@ **/ConnectionOptionsTest.java **/spi2dav/ConnectionTest.java + **/spi2dav/GetHeadRedirectStrategyTest.java + **/spi2davex/Rfc6532MultipartEntityTest.java @@ -175,9 +177,8 @@ javax.servlet-api - org.apache.httpcomponents - httpmime - 4.5.14 + org.apache.httpcomponents.client5 + httpclient5 junit diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ConnectionOptions.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ConnectionOptions.java index 210c2ff16fd..6f5a5de0661 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ConnectionOptions.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ConnectionOptions.java @@ -19,8 +19,8 @@ import java.util.HashMap; import java.util.Map; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.jackrabbit.spi2davex.Spi2davexRepositoryServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/CredentialsWrapper.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/CredentialsWrapper.java index f37b72e6c55..ee20fee7d5c 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/CredentialsWrapper.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/CredentialsWrapper.java @@ -18,8 +18,8 @@ import javax.jcr.SimpleCredentials; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.auth.Credentials; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; /** * CredentialsWrapper... @@ -38,10 +38,22 @@ class CredentialsWrapper { } else if (creds instanceof SimpleCredentials) { SimpleCredentials sCred = (SimpleCredentials) creds; userId = sCred.getUserID(); - this.credentials = new UsernamePasswordCredentials(userId, String.valueOf(sCred.getPassword())); + // copy the password: SimpleCredentials hands out its internal array and + // UsernamePasswordCredentials keeps the reference, so without a copy a + // caller zeroing the password after login would break the open session + this.credentials = new UsernamePasswordCredentials(userId, sCred.getPassword().clone()); } else { userId = ""; - this.credentials = new UsernamePasswordCredentials(creds.toString()); + // HttpClient 5 dropped the single-argument "username:password" + // constructor, so split the pair here instead + String usernamePassword = creds.toString(); + int colon = usernamePassword.indexOf(':'); + if (colon < 0) { + this.credentials = new UsernamePasswordCredentials(usernamePassword, new char[0]); + } else { + this.credentials = new UsernamePasswordCredentials(usernamePassword.substring(0, colon), + usernamePassword.substring(colon + 1).toCharArray()); + } } } diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ExceptionConverter.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ExceptionConverter.java index e0346c7bebb..00fd9359159 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ExceptionConverter.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/ExceptionConverter.java @@ -26,7 +26,7 @@ import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; -import org.apache.http.client.methods.HttpRequestBase; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.DavMethods; @@ -46,7 +46,7 @@ public static RepositoryException generate(DavException davExc) { return generate(davExc, null); } - public static RepositoryException generate(DavException davExc, HttpRequestBase request) { + public static RepositoryException generate(DavException davExc, HttpUriRequestBase request) { String name = (request == null) ? "_undefined_" : request.getMethod(); int code = DavMethods.getMethodCode(name); return generate(davExc, code, name); diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategy.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategy.java new file mode 100644 index 00000000000..481456588e2 --- /dev/null +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategy.java @@ -0,0 +1,64 @@ +/* + * 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.jackrabbit.spi2dav; + +import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpRequest; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.protocol.HttpContext; + +/** + * Redirect strategy that only follows redirects for safe methods. + *

+ * HttpClient 5 follows redirects for every method, whereas HttpClient 4 restricted + * automatic redirects to GET and HEAD. Following a redirect automatically for a + * WebDAV method such as MOVE, COPY, PUT or DELETE would silently retarget a write + * at a different resource instead of surfacing the redirect to the caller, so this + * strategy reproduces the HttpClient 4 rules. + * + * @see org.apache.hc.client5.http.impl.DefaultRedirectStrategy + */ +final class GetHeadRedirectStrategy extends DefaultRedirectStrategy { + + static final GetHeadRedirectStrategy INSTANCE = new GetHeadRedirectStrategy(); + + @Override + public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) { + switch (response.getCode()) { + case HttpStatus.SC_MOVED_TEMPORARILY: + // 302 additionally required a Location header under HttpClient 4 + return isRedirectable(request.getMethod()) + && response.getFirstHeader(HttpHeaders.LOCATION) != null; + case HttpStatus.SC_MOVED_PERMANENTLY: + case HttpStatus.SC_TEMPORARY_REDIRECT: + case HttpStatus.SC_PERMANENT_REDIRECT: + return isRedirectable(request.getMethod()); + case HttpStatus.SC_SEE_OTHER: + // 303 tells the client to fetch a different resource with GET, + // which is safe regardless of the original method + return true; + default: + return false; + } + } + + private static boolean isRedirectable(String method) { + return "GET".equalsIgnoreCase(method) || "HEAD".equalsIgnoreCase(method); + } +} diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java index 05755a9e297..04d91791af9 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImpl.java @@ -56,42 +56,42 @@ import javax.net.ssl.SSLContext; import javax.xml.parsers.ParserConfigurationException; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; -import org.apache.http.client.CredentialsProvider; -import org.apache.http.client.HttpClient; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.config.Registry; -import org.apache.http.config.RegistryBuilder; -import org.apache.http.conn.socket.ConnectionSocketFactory; -import org.apache.http.conn.socket.PlainConnectionSocketFactory; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustSelfSignedStrategy; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.auth.BasicScheme; -import org.apache.http.impl.client.BasicAuthCache; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.client.ProxyAuthenticationStrategy; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.protocol.HttpContext; -import org.apache.http.ssl.SSLContextBuilder; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.message.StatusLine; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.auth.AuthCache; +import org.apache.hc.client5.http.auth.CredentialsStore; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.config.ConnectionConfig; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.client5.http.classic.methods.HttpPut; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.classic.methods.HttpUriRequest; +import org.apache.hc.client5.http.protocol.HttpClientContext; +import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; +import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy; +import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; +import org.apache.hc.client5.http.ssl.TlsSocketStrategy; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.io.entity.InputStreamEntity; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.client5.http.impl.auth.BasicScheme; +import org.apache.hc.client5.http.impl.auth.BasicAuthCache; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; import org.apache.jackrabbit.commons.webdav.AtomFeedConstants; import org.apache.jackrabbit.commons.webdav.EventUtil; import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants; @@ -242,7 +242,7 @@ public class RepositoryServiceImpl implements RepositoryService, DavConstants { /** * Default value for the maximum number of connections per host such as - * configured with {@link PoolingHttpClientConnectionManager#setDefaultMaxPerRoute(int)}. + * configured with {@link PoolingHttpClientConnectionManagerBuilder#setMaxConnPerRoute(int)}. */ public static final int MAX_CONNECTIONS_DEFAULT = 20; @@ -260,7 +260,7 @@ public class RepositoryServiceImpl implements RepositoryService, DavConstants { private final HttpHost httpHost; private final ConcurrentMap clients; private final HttpClientBuilder httpClientBuilder; - private final Map commonCredentials; + private final Map commonCredentials; private final Map nodeTypeDefinitions = new HashMap(); @@ -345,7 +345,7 @@ public RepositoryServiceImpl(String uri, IdFactory idFactory, try { URI repositoryUri = computeRepositoryUri(uri); - httpHost = new HttpHost(repositoryUri.getHost(), repositoryUri.getPort(), repositoryUri.getScheme()); + httpHost = new HttpHost(repositoryUri.getScheme(), repositoryUri.getHost(), repositoryUri.getPort()); nsCache = new NamespaceCache(); uriResolver = new URIResolverImpl(repositoryUri, this, DomUtil.createDocument()); @@ -360,14 +360,24 @@ public RepositoryServiceImpl(String uri, IdFactory idFactory, HttpClientBuilder hcb = HttpClients.custom(); - final SSLConnectionSocketFactory sslSocketFactory; + // HttpClient 5 would follow redirects for every method; restrict this to the + // safe methods that HttpClient 4 redirected, so that a redirected MOVE, COPY, + // PUT or DELETE surfaces to the caller instead of silently retargeting + hcb.setRedirectStrategy(GetHeadRedirectStrategy.INSTANCE); + + final TlsSocketStrategy tlsSocketStrategy; // request config - RequestConfig requestConfig = RequestConfig.custom(). - setConnectTimeout(connectionOptions.getConnectionTimeoutMs()). - setConnectionRequestTimeout(connectionOptions.getRequestTimeoutMs()). - setSocketTimeout(connectionOptions.getSocketTimeoutMs()).build(); + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout(toLeaseTimeout(connectionOptions.getRequestTimeoutMs())) + .build(); hcb.setDefaultRequestConfig(requestConfig); + + // connect and socket timeouts moved from RequestConfig to ConnectionConfig in HttpClient 5 + ConnectionConfig connectionConfig = ConnectionConfig.custom() + .setConnectTimeout(toTimeout(connectionOptions.getConnectionTimeoutMs())) + .setSocketTimeout(toTimeout(connectionOptions.getSocketTimeoutMs())) + .build(); if (Boolean.getBoolean("jackrabbit.client.useSystemProperties") || connectionOptions.isUseSystemPropertes()) { log.debug("Using system properties for establishing connection!"); @@ -383,15 +393,16 @@ public RepositoryServiceImpl(String uri, IdFactory idFactory, // support Java system proxy? (JCR-3211) hcb.useSystemProperties(); - sslSocketFactory = SSLConnectionSocketFactory.getSystemSocketFactory(); + tlsSocketStrategy = DefaultClientTlsStrategy.createSystemDefault(); } else { // TLS settings (via connection manager) final SSLContext sslContext; try { if (connectionOptions.isAllowSelfSignedCertificates()) { log.warn("Nonsecure TLS setting: Accepting self-signed certificates!"); - sslContext = SSLContextBuilder.create().loadTrustMaterial(new TrustSelfSignedStrategy()).build(); - hcb.setSSLContext(sslContext); + // what the deprecated TrustSelfSignedStrategy did: trust any + // certificate that arrives without an issuer chain + sslContext = SSLContextBuilder.create().loadTrustMaterial((chain, authType) -> chain.length == 1).build(); } else { sslContext = SSLContextBuilder.create().build(); } @@ -402,39 +413,43 @@ public RepositoryServiceImpl(String uri, IdFactory idFactory, if (connectionOptions.isDisableHostnameVerification()) { log.warn("Nonsecure TLS setting: Host name verification of TLS certificates disabled!"); // we can optionally disable hostname verification. - sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + // HostnameVerificationPolicy.CLIENT is required in addition to the noop verifier: with the + // default policy the JSSE built-in endpoint identification still runs during the handshake + tlsSocketStrategy = new DefaultClientTlsStrategy(sslContext, HostnameVerificationPolicy.CLIENT, + NoopHostnameVerifier.INSTANCE); } else { - sslSocketFactory = new SSLConnectionSocketFactory(sslContext); + tlsSocketStrategy = new DefaultClientTlsStrategy(sslContext); } } - Registry socketFactoryRegistry = RegistryBuilder.create() - .register("http", PlainConnectionSocketFactory.getSocketFactory()) - .register("https", sslSocketFactory) - .build(); + PoolingHttpClientConnectionManagerBuilder cmgrBuilder = PoolingHttpClientConnectionManagerBuilder.create() + .setTlsSocketStrategy(tlsSocketStrategy) + .setDefaultConnectionConfig(connectionConfig); - PoolingHttpClientConnectionManager cmgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry); int maxConnections = connectionOptions.getMaxConnections(); if (maxConnections > 0) { - cmgr.setDefaultMaxPerRoute(connectionOptions.getMaxConnections()); - cmgr.setMaxTotal(connectionOptions.getMaxConnections()); + cmgrBuilder.setMaxConnPerRoute(maxConnections); + cmgrBuilder.setMaxConnTotal(maxConnections); } else { maxConnections = ConnectionOptions.MAX_CONNECTIONS_DEFAULT; } - hcb.setConnectionManager(cmgr); + hcb.setConnectionManager(cmgrBuilder.build()); if (connectionOptions.getProxyHost() != null) { - // https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/connmgmt.html#d5e485 - HttpHost proxy = new HttpHost(connectionOptions.getProxyHost(), connectionOptions.getProxyPort(), connectionOptions.getProxyProtocol()); + // https://hc.apache.org/httpcomponents-client-5.6.x/current/tutorial/html/connmgmt.html + HttpHost proxy = new HttpHost(connectionOptions.getProxyProtocol(), connectionOptions.getProxyHost(), connectionOptions.getProxyPort()); DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); hcb.setRoutePlanner(routePlanner); log.debug("Connection via proxy {}", proxy); if (connectionOptions.getProxyUsername() != null) { log.debug("Proxy connection with credentials {}", proxy); + // HttpClient 5 handles proxy authentication through the shared + // authentication strategy, so no separate proxy strategy is needed + String proxyPassword = connectionOptions.getProxyPassword(); commonCredentials.put( new AuthScope(proxy), - new UsernamePasswordCredentials(connectionOptions.getProxyUsername(), connectionOptions.getProxyPassword())); - hcb.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); + new UsernamePasswordCredentials(connectionOptions.getProxyUsername(), + proxyPassword == null ? new char[0] : proxyPassword.toCharArray())); } } httpClientBuilder = hcb; @@ -446,6 +461,36 @@ public RepositoryServiceImpl(String uri, IdFactory idFactory, clients = new ConcurrentHashMap(maxConnections, .75f, maxConnections); } + /** + * A timeout that never expires. Note that neither {@link Timeout#DISABLED} nor + * {@link Timeout#INFINITE} can be used for this: both are zero, which the socket + * layer reads as "no timeout" but the connection pool reads as "do not wait at + * all", so a contended lease fails immediately. + */ + private static final Timeout NO_TIMEOUT = Timeout.ofMilliseconds(Long.MAX_VALUE); + + /** + * Converts a jackrabbit connect or socket timeout in milliseconds to an HttpClient 5 + * {@link Timeout}. The value -1 means "not configured" and HttpClient 4 treated any + * other negative value as infinite too, so all of them map to a disabled timeout; + * zero carries that meaning to the socket layer as well. Simply omitting the setter + * would instead pick up the HttpClient 5 default of three minutes. + */ + private static Timeout toTimeout(int timeoutMs) { + return timeoutMs < 0 ? Timeout.DISABLED : Timeout.ofMilliseconds(timeoutMs); + } + + /** + * Converts a jackrabbit connection request timeout in milliseconds to an HttpClient 5 + * {@link Timeout}. This is the time spent waiting for a connection from the pool, where + * a zero timeout means "fail immediately" rather than the "wait forever" it meant under + * HttpClient 4, so zero and every negative value have to map to an explicitly unbounded + * value. + */ + private static Timeout toLeaseTimeout(int timeoutMs) { + return timeoutMs <= 0 ? NO_TIMEOUT : Timeout.ofMilliseconds(timeoutMs); + } + private static void checkSessionInfo(SessionInfo sessionInfo) throws RepositoryException { if (!(sessionInfo instanceof SessionInfoImpl)) { throw new RepositoryException("Unknown SessionInfo implementation."); @@ -624,18 +669,21 @@ protected HttpClient getClient(SessionInfo sessionInfo) throws RepositoryExcepti protected HttpContext getContext(SessionInfo sessionInfo) throws RepositoryException { HttpClientContext result = HttpClientContext.create(); - CredentialsProvider credsProvider = new BasicCredentialsProvider(); + CredentialsStore credsProvider = new BasicCredentialsProvider(); result.setCredentialsProvider(credsProvider); // take over default credentials (e.g. for proxy) - for (Map.Entry entry : commonCredentials.entrySet()) { + for (Map.Entry entry : commonCredentials.entrySet()) { credsProvider.setCredentials(entry.getKey(), entry.getValue()); } if (sessionInfo != null) { checkSessionInfo(sessionInfo); - org.apache.http.auth.Credentials creds = ((SessionInfoImpl) sessionInfo).getCredentials().getHttpCredentials(); + org.apache.hc.client5.http.auth.Credentials creds = ((SessionInfoImpl) sessionInfo).getCredentials().getHttpCredentials(); if (creds != null) { - credsProvider.setCredentials(new org.apache.http.auth.AuthScope(httpHost.getHostName(), httpHost.getPort()), creds); + credsProvider.setCredentials(new AuthScope(httpHost), creds); BasicScheme basicAuth = new BasicScheme(); + // HttpClient 5 only pre-authenticates from a cached scheme that + // has been primed with the credentials + basicAuth.initPreemptive(creds); AuthCache authCache = new BasicAuthCache(); authCache.put(httpHost, basicAuth); result.setAuthCache(authCache); @@ -730,11 +778,11 @@ int getIndex(DavPropertySet propSet) { /** * Execute a 'Workspace' operation. */ - private HttpResponse execute(BaseDavRequest request, SessionInfo sessionInfo) throws RepositoryException { + private ClassicHttpResponse execute(BaseDavRequest request, SessionInfo sessionInfo) throws RepositoryException { try { initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); return response; } catch (IOException e) { @@ -778,8 +826,8 @@ public Map getRepositoryDescriptors() throws RepositoryExcepti HttpReport request = null; try { request = new HttpReport(uriResolver.getRepositoryUri(), info); - HttpResponse response = executeRequest(null, request); - int sc = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = executeRequest(null, request); + int sc = response.getCode(); if (sc == HttpStatus.SC_UNAUTHORIZED || sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) { // JCR-3076: Mandatory authentication prevents us from @@ -822,7 +870,7 @@ public Map getRepositoryDescriptors() throws RepositoryExcepti throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -861,7 +909,7 @@ private SessionInfo obtain(CredentialsWrapper credentials, String workspaceName) nameSet.add(JcrRemotingConstants.JCR_WORKSPACE_NAME_LN, ItemResourceConstants.NAMESPACE); request = new HttpPropfind(uriResolver.getWorkspaceUri(workspaceName), nameSet, DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -895,7 +943,7 @@ private SessionInfo obtain(CredentialsWrapper credentials, String workspaceName) throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } @@ -927,7 +975,7 @@ public String[] getWorkspaceNames(SessionInfo sessionInfo) throws RepositoryExce HttpPropfind request = null; try { request = new HttpPropfind(uriResolver.getRepositoryUri(), nameSet, DEPTH_1); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); Set wspNames = new HashSet(); @@ -947,7 +995,7 @@ public String[] getWorkspaceNames(SessionInfo sessionInfo) throws RepositoryExce throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -961,7 +1009,7 @@ public boolean isGranted(SessionInfo sessionInfo, ItemId itemId, String[] action reportInfo.setContentElement(DomUtil.hrefToXml(uri, DomUtil.createDocument())); request = new HttpReport(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()), reportInfo); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -993,7 +1041,7 @@ public boolean isGranted(SessionInfo sessionInfo, ItemId itemId, String[] action throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1018,7 +1066,7 @@ public Name[] getPrivilegeNames(SessionInfo sessionInfo, NodeId nodeId) throws R HttpPropfind propfindRequest = null; try { propfindRequest = new HttpPropfind(uri, nameSet, DEPTH_0); - HttpResponse response = execute(propfindRequest, sessionInfo); + ClassicHttpResponse response = execute(propfindRequest, sessionInfo); propfindRequest.checkSuccess(response); MultiStatusResponse[] mresponses = propfindRequest.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1044,7 +1092,7 @@ public Name[] getPrivilegeNames(SessionInfo sessionInfo, NodeId nodeId) throws R throw ExceptionConverter.generate(e); } finally { if (propfindRequest != null) { - propfindRequest.releaseConnection(); + propfindRequest.reset(); } } } @@ -1055,7 +1103,7 @@ private PrivilegeDefinition[] internalGetPrivilegeDefinitions(SessionInfo sessio HttpPropfind request = null; try { request = new HttpPropfind(uri, nameSet, DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1096,7 +1144,7 @@ private PrivilegeDefinition[] internalGetPrivilegeDefinitions(SessionInfo sessio throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1140,7 +1188,7 @@ private QItemDefinition getItemDefinition(SessionInfo sessionInfo, ItemId itemId try { String uri = getItemUri(itemId, sessionInfo); request = new HttpPropfind(uri, nameSet, DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1186,7 +1234,7 @@ private QItemDefinition getItemDefinition(SessionInfo sessionInfo, ItemId itemId throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1209,7 +1257,7 @@ public NodeInfo getNodeInfo(SessionInfo sessionInfo, NodeId nodeId) throws Repos try { String uri = getItemUri(nodeId, sessionInfo); request = new HttpPropfind(uri, nameSet, DEPTH_1); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1264,7 +1312,7 @@ public NodeInfo getNodeInfo(SessionInfo sessionInfo, NodeId nodeId) throws Repos throw new RepositoryException(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1354,7 +1402,7 @@ public Iterator getChildInfos(SessionInfo sessionInfo, NodeId parentI try { String uri = getItemUri(parentId, sessionInfo); request = new HttpPropfind(uri, nameSet, DEPTH_1); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); List childEntries; @@ -1384,7 +1432,7 @@ public Iterator getChildInfos(SessionInfo sessionInfo, NodeId parentI throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1408,7 +1456,7 @@ public Iterator getReferences(SessionInfo sessionInfo, NodeId nodeId try { String uri = getItemUri(nodeId, sessionInfo); request = new HttpPropfind(uri, nameSet, DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1441,7 +1489,7 @@ public Iterator getReferences(SessionInfo sessionInfo, NodeId nodeId throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1452,17 +1500,17 @@ public PropertyInfo getPropertyInfo(SessionInfo sessionInfo, PropertyId property try { String uri = getItemUri(propertyId, sessionInfo); request = new HttpGet(uri); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); - int status = response.getStatusLine().getStatusCode(); + int status = response.getCode(); if (status != DavServletResponse.SC_OK) { - throw ExceptionConverter.generate(new DavException(status, response.getStatusLine().getReasonPhrase())); + throw ExceptionConverter.generate(new DavException(status, response.getReasonPhrase())); } Path path = uriResolver.getQPath(uri, sessionInfo); HttpEntity entity = response.getEntity(); - ContentType ct = ContentType.get(entity); + ContentType ct = (entity == null) ? null : ContentType.parse(entity.getContentType()); boolean isMultiValued; QValue[] values; @@ -1509,7 +1557,7 @@ public PropertyInfo getPropertyInfo(SessionInfo sessionInfo, PropertyId property throw new RepositoryException(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1558,7 +1606,7 @@ private int loadType(String propertyURI, HttpClient client, PropertyId propertyI HttpPropfind request = null; try { request = new HttpPropfind(propertyURI, nameSet, DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1575,7 +1623,7 @@ private int loadType(String propertyURI, HttpClient client, PropertyId propertyI } } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1597,29 +1645,29 @@ public void submit(Batch batch) throws RepositoryException { return; } - HttpRequestBase request = null; + HttpUriRequestBase request = null; try { HttpClient client = batchImpl.start(); boolean success = false; try { - Iterator it = batchImpl.requests(); + Iterator it = batchImpl.requests(); while (it.hasNext()) { request = it.next(); initMethod(request, batchImpl, true); - HttpResponse response = client.execute(request); + ClassicHttpResponse response = client.executeOpen(null, request, null); if (request instanceof BaseDavRequest) { ((BaseDavRequest) request).checkSuccess(response); } else { // use generic HTTP status code checking - int statusCode = response.getStatusLine().getStatusCode(); + int statusCode = response.getCode(); if (statusCode < 200 || statusCode >= 300) { throw new DavException(statusCode, "Unexpected status code " + statusCode + " in response to " + request.getMethod() + " request."); } } - request.releaseConnection(); + request.reset(); } success = true; } finally { @@ -1663,7 +1711,7 @@ public void move(SessionInfo sessionInfo, NodeId srcNodeId, NodeId destParentNod HttpMove request = new HttpMove(uri, destUri, false); try { initMethod(request, sessionInfo); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); // need to clear the cache as the move may have affected nodes with // uuid. @@ -1673,7 +1721,7 @@ public void move(SessionInfo sessionInfo, NodeId srcNodeId, NodeId destParentNod } catch (DavException e) { throw ExceptionConverter.generate(e, request); } finally { - request.releaseConnection(); + request.reset(); } } @@ -1687,14 +1735,14 @@ public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNod HttpCopy request = new HttpCopy(uri, destUri, false, false); try { initMethod(request, sessionInfo); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException ex) { throw new RepositoryException(ex); } catch (DavException e) { throw ExceptionConverter.generate(e, request); } finally { - request.releaseConnection(); + request.reset(); } } @@ -1725,7 +1773,7 @@ public LockInfo getLockInfo(SessionInfo sessionInfo, NodeId nodeId) throws Repos request = new HttpPropfind(uri, nameSet, DEPTH_0); initMethod(request, sessionInfo, false); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -1753,7 +1801,7 @@ public LockInfo getLockInfo(SessionInfo sessionInfo, NodeId nodeId) throws Repos throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1776,7 +1824,7 @@ public LockInfo lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, boole Scope scope = (sessionScoped) ? ItemResourceConstants.EXCLUSIVE_SESSION : Scope.EXCLUSIVE; request = new HttpLock(uri, new org.apache.jackrabbit.webdav.lock.LockInfo(scope, Type.WRITE, ownerInfo, davTimeout, deep)); - HttpResponse response = execute(request, sessionInfo); + ClassicHttpResponse response = execute(request, sessionInfo); String lockToken = request.getLockToken(response); ((SessionInfoImpl) sessionInfo).addLockToken(lockToken, sessionScoped); @@ -1789,7 +1837,7 @@ public LockInfo lock(SessionInfo sessionInfo, NodeId nodeId, boolean deep, boole throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -1808,7 +1856,7 @@ public void refreshLock(SessionInfo sessionInfo, NodeId nodeId) throws Repositor execute(httpLock, sessionInfo); } finally { if (httpLock != null) { - httpLock.releaseConnection(); + httpLock.reset(); } } } @@ -1837,7 +1885,7 @@ public void unlock(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryExce execute(unlockRequest, sessionInfo); ((SessionInfoImpl) sessionInfo).removeLockToken(lockToken, isSessionScoped); } finally { - unlockRequest.releaseConnection(); + unlockRequest.reset(); } } @@ -1883,16 +1931,19 @@ public NodeId checkin(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryE HttpCheckin request = new HttpCheckin(uri); try { initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); - org.apache.http.Header rh = response.getFirstHeader(DeltaVConstants.HEADER_LOCATION); + Header rh = response.getFirstHeader(DeltaVConstants.HEADER_LOCATION); + if (rh == null) { + throw new RepositoryException("CHECKIN of " + uri + " failed: no Location header in response."); + } return uriResolver.getNodeId(resolve(uri, rh.getValue()), sessionInfo); } catch (IOException e) { throw new RepositoryException(e); } catch (DavException ex) { throw ExceptionConverter.generate(ex); } finally { - request.releaseConnection(); + request.reset(); } } @@ -1902,14 +1953,14 @@ public void checkout(SessionInfo sessionInfo, NodeId nodeId) throws RepositoryEx HttpCheckout request = new HttpCheckout(uri); try { initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); } catch (DavException ex) { throw ExceptionConverter.generate(ex); } finally { - request.releaseConnection(); + request.reset(); } } @@ -1947,14 +1998,14 @@ public void removeVersion(SessionInfo sessionInfo, NodeId versionHistoryId, Node HttpDelete request = new HttpDelete(uri); try { initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException ex) { throw new RepositoryException(ex); } catch (DavException ex) { throw ExceptionConverter.generate(ex); } finally { - request.releaseConnection(); + request.reset(); } } @@ -1991,7 +2042,7 @@ public void restore(SessionInfo sessionInfo, NodeId nodeId, NodeId versionId, bo private boolean exists(SessionInfo sInfo, String uri) { HttpHead request = new HttpHead(uri); try { - int statusCode = executeRequest(sInfo, request).getStatusLine().getStatusCode(); + int statusCode = executeRequest(sInfo, request).getCode(); return (statusCode == DavServletResponse.SC_OK); } catch (IOException e) { log.error("Unexpected error while testing existence of item.", e); @@ -2000,7 +2051,7 @@ private boolean exists(SessionInfo sInfo, String uri) { log.error(e.getMessage()); return false; } finally { - request.releaseConnection(); + request.reset(); } } @@ -2036,7 +2087,7 @@ private void update(String uri, Path relPath, String[] updateSource, int updateT request = new HttpUpdate(uri, uInfo); initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2046,7 +2097,7 @@ private void update(String uri, Path relPath, String[] updateSource, int updateT throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2071,7 +2122,7 @@ public Iterator merge(SessionInfo sessionInfo, NodeId nodeId, String src String uri = getItemUri(nodeId, sessionInfo); request = new HttpMerge(uri, mInfo); initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] resps = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -2089,7 +2140,7 @@ public Iterator merge(SessionInfo sessionInfo, NodeId nodeId, String src throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2115,7 +2166,7 @@ public void resolveMergeConflict(SessionInfo sessionInfo, NodeId nodeId, NodeId[ request = new HttpProppatch(getItemUri(nodeId, sessionInfo), changeList); initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2123,7 +2174,7 @@ public void resolveMergeConflict(SessionInfo sessionInfo, NodeId nodeId, NodeId[ throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2136,14 +2187,14 @@ public void addVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, No String strLabel = getNamePathResolver(sessionInfo).getJCRName(label); request = new HttpLabel(uri, new LabelInfo(strLabel, moveLabel ? LabelInfo.TYPE_SET : LabelInfo.TYPE_ADD)); initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); } catch (DavException ex) { throw ExceptionConverter.generate(ex); } finally { - request.releaseConnection(); + request.reset(); } } @@ -2155,14 +2206,14 @@ public void removeVersionLabel(SessionInfo sessionInfo, NodeId versionHistoryId, String strLabel = getNamePathResolver(sessionInfo).getJCRName(label); request = new HttpLabel(uri, new LabelInfo(strLabel, LabelInfo.TYPE_REMOVE)); initMethod(request, sessionInfo, !isUnLockMethod(request)); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); } catch (DavException ex) { throw ExceptionConverter.generate(ex); } finally { - request.releaseConnection(); + request.reset(); } } @@ -2194,8 +2245,8 @@ public NodeId createConfiguration(SessionInfo sessionInfo, NodeId nodeId) throws public String[] getSupportedQueryLanguages(SessionInfo sessionInfo) throws RepositoryException { HttpOptions request = new HttpOptions(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName())); try { - HttpResponse response = executeRequest(sessionInfo, request); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = executeRequest(sessionInfo, request); + int status = response.getCode(); if (status != DavServletResponse.SC_OK) { throw new DavException(status); } @@ -2205,7 +2256,7 @@ public String[] getSupportedQueryLanguages(SessionInfo sessionInfo) throws Repos } catch (DavException e) { throw ExceptionConverter.generate(e); } finally { - request.releaseConnection(); + request.reset(); } } @@ -2240,7 +2291,7 @@ public QueryInfo executeQuery(SessionInfo sessionInfo, String statement, String } request = new HttpSearch(uri, sInfo); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatus ms = request.getResponseBodyAsMultiStatus(response); @@ -2252,7 +2303,7 @@ public QueryInfo executeQuery(SessionInfo sessionInfo, String statement, String throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2309,10 +2360,10 @@ public EventBundle getEvents(SessionInfo sessionInfo, EventFilter filter, long a request.addHeader("If-None-Match", "\"" + Long.toHexString(after) + "\""); // TODO initMethod(request, sessionInfo); - HttpResponse response = executeRequest(sessionInfo, request); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = executeRequest(sessionInfo, request); + int status = response.getCode(); if (status != 200) { - throw new RepositoryException("getEvents to " + rootUri + " failed with " + response.getStatusLine()); + throw new RepositoryException("getEvents to " + rootUri + " failed with " + new StatusLine(response)); } HttpEntity entity = response.getEntity(); @@ -2355,7 +2406,7 @@ public EventBundle getEvents(SessionInfo sessionInfo, EventFilter filter, long a throw new RepositoryException("extracting events from journal feed: " + ex.getMessage(), ex); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2430,7 +2481,7 @@ private String subscribe(String uri, SubscriptionInfo subscriptionInfo, request.setHeader(ch.getHeaderName(), ch.getHeaderValue()); } - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); org.apache.jackrabbit.webdav.observation.Subscription[] subs = request.getResponseBodyAsSubscriptionDiscovery(response) @@ -2447,7 +2498,7 @@ private String subscribe(String uri, SubscriptionInfo subscriptionInfo, throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2457,7 +2508,7 @@ private void unsubscribe(String uri, String subscriptionId, SessionInfo sessionI try { request = new HttpUnsubscribe(uri, subscriptionId); initMethod(request, sessionInfo); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2465,7 +2516,7 @@ private void unsubscribe(String uri, String subscriptionId, SessionInfo sessionI throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2486,7 +2537,7 @@ private EventBundle[] poll(String uri, String subscriptionId, long timeout, Sess HttpPoll request = null; try { request = new HttpPoll(uri, subscriptionId, timeout); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); EventDiscovery disc = request.getResponseBodyAsEventDiscovery(response); @@ -2522,7 +2573,7 @@ private EventBundle[] poll(String uri, String subscriptionId, long timeout, Sess throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2622,7 +2673,7 @@ public Map getRegisteredNamespaces(SessionInfo sessionInfo) thro HttpReport request = null; try { request = new HttpReport(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()), info); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); Document doc = request.getResponseBodyAsDocument(response.getEntity()); @@ -2655,7 +2706,7 @@ public Map getRegisteredNamespaces(SessionInfo sessionInfo) thro throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2734,7 +2785,7 @@ private void internalSetNamespaces(SessionInfo sessionInfo, Map request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); initMethod(request, sessionInfo, true); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2742,7 +2793,7 @@ private void internalSetNamespaces(SessionInfo sessionInfo, Map throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2756,7 +2807,7 @@ public Iterator getQNodeTypeDefinitions(SessionInfo session String workspaceUri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); request = new HttpReport(workspaceUri, info); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); Document reportDoc = request.getResponseBodyAsDocument(response.getEntity()); @@ -2769,7 +2820,7 @@ public Iterator getQNodeTypeDefinitions(SessionInfo session throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2790,7 +2841,7 @@ public void registerNodeTypes(SessionInfo sessionInfo, QNodeTypeDefinition[] nod String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); initMethod(request, sessionInfo, true); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2798,7 +2849,7 @@ public void registerNodeTypes(SessionInfo sessionInfo, QNodeTypeDefinition[] nod throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2812,7 +2863,7 @@ public void unregisterNodeTypes(SessionInfo sessionInfo, Name[] nodeTypeNames) t String uri = uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName()); request = new HttpProppatch(uri, setProperties, new DavPropertyNameSet()); initMethod(request, sessionInfo, true); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2820,7 +2871,7 @@ public void unregisterNodeTypes(SessionInfo sessionInfo, Name[] nodeTypeNames) t throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2835,7 +2886,7 @@ public void createWorkspace(SessionInfo sessionInfo, String name, String srcWork try { request = new HttpMkworkspace(uriResolver.getWorkspaceUri(name)); initMethod(request, sessionInfo, true); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2843,7 +2894,7 @@ public void createWorkspace(SessionInfo sessionInfo, String name, String srcWork throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2854,7 +2905,7 @@ public void deleteWorkspace(SessionInfo sessionInfo, String name) throws Reposit try { request = new HttpDelete(uriResolver.getWorkspaceUri(name)); initMethod(request, sessionInfo, true); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -2862,7 +2913,7 @@ public void deleteWorkspace(SessionInfo sessionInfo, String name) throws Reposit throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -2887,8 +2938,8 @@ public static URI computeRepositoryUri(String uri) throws URISyntaxException { return repositoryUri; } - public HttpResponse executeRequest(SessionInfo sessionInfo, HttpUriRequest request) throws IOException, RepositoryException { - return getClient(sessionInfo).execute(request, getContext(sessionInfo)); + public ClassicHttpResponse executeRequest(SessionInfo sessionInfo, HttpUriRequest request) throws IOException, RepositoryException { + return getClient(sessionInfo).executeOpen(null, request, getContext(sessionInfo)); } /** @@ -3022,8 +3073,8 @@ private Set getDavComplianceClasses(SessionInfo sessionInfo) throws Repo if (this.remoteDavComplianceClasses == null) { HttpOptions request = new HttpOptions(uriResolver.getWorkspaceUri(sessionInfo.getWorkspaceName())); try { - HttpResponse response = executeRequest(sessionInfo, request); - int status = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = executeRequest(sessionInfo, request); + int status = response.getCode(); if (status != DavServletResponse.SC_OK) { throw new DavException(status); } @@ -3033,7 +3084,7 @@ private Set getDavComplianceClasses(SessionInfo sessionInfo) throws Repo } catch (DavException e) { throw ExceptionConverter.generate(e); } finally { - request.releaseConnection(); + request.reset(); } } return this.remoteDavComplianceClasses; @@ -3081,7 +3132,7 @@ private class BatchImpl implements Batch { private final SessionInfo sessionInfo; private final ItemId targetId; - private final List requests = new ArrayList(); + private final List requests = new ArrayList(); private final NamePathResolver resolver; private String batchId; @@ -3106,8 +3157,8 @@ private HttpClient start() throws RepositoryException { initMethod(request, sessionInfo, true); HttpClient client = getClient(sessionInfo); - HttpResponse response = client.execute(request,getContext(sessionInfo)); - if (response.getStatusLine().getStatusCode() == DavServletResponse.SC_PRECONDITION_FAILED) { + ClassicHttpResponse response = client.executeOpen(null, request, getContext(sessionInfo)); + if (response.getCode() == DavServletResponse.SC_PRECONDITION_FAILED) { throw new InvalidItemStateException("Unable to persist transient changes."); } request.checkSuccess(response); @@ -3121,7 +3172,7 @@ private HttpClient start() throws RepositoryException { throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -3140,7 +3191,7 @@ private void end(HttpClient client, boolean commit) throws RepositoryException { // in contrast to standard UNLOCK, the tx-unlock provides a // request body. request.setEntity(XmlEntity.create(new TransactionInfo(commit))); - HttpResponse response = client.execute(request, getContext(sessionInfo)); + ClassicHttpResponse response = client.executeOpen(null, request, getContext(sessionInfo)); request.checkSuccess(response); if (sessionInfo instanceof SessionInfoImpl) { ((SessionInfoImpl) sessionInfo).setLastBatchId(batchId); @@ -3155,7 +3206,7 @@ private void end(HttpClient client, boolean commit) throws RepositoryException { } finally { if (request != null) { // release UNLOCK method - request.releaseConnection(); + request.reset(); } } } @@ -3175,7 +3226,7 @@ private boolean isEmpty() { return requests.isEmpty(); } - private Iterator requests() { + private Iterator requests() { return requests.iterator(); } diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java index ff2c3f37c89..8911e88f287 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2dav/URIResolverImpl.java @@ -16,7 +16,7 @@ */ package org.apache.jackrabbit.spi2dav; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants; import org.apache.jackrabbit.spi.commons.conversion.NameException; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; @@ -122,7 +122,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo) String wspUri = getWorkspaceUri(workspaceName); request = new HttpReport(wspUri, rInfo); - HttpResponse response = service.executeRequest(sessionInfo, request); + ClassicHttpResponse response = service.executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatus ms = request.getResponseBodyAsMultiStatus(response); @@ -141,7 +141,7 @@ String getItemUri(ItemId itemId, String workspaceName, SessionInfo sessionInfo) throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -282,10 +282,10 @@ private NodeId getNodeId(String uri, SessionInfo sessionInfo, boolean nodeIsGone try { request = new HttpPropfind(uri, nameSet, DavConstants.DEPTH_0); - HttpResponse response = service.executeRequest(sessionInfo, request); - if (response.getStatusLine().getStatusCode() != DavServletResponse.SC_MULTI_STATUS) { + ClassicHttpResponse response = service.executeRequest(sessionInfo, request); + if (response.getCode() != DavServletResponse.SC_MULTI_STATUS) { throw new ItemNotFoundException("Unable to retrieve the node with id " + uri + ", response status was: " - + response.getStatusLine().getStatusCode()); + + response.getCode()); } MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); if (responses.length != 1) { @@ -298,7 +298,7 @@ private NodeId getNodeId(String uri, SessionInfo sessionInfo, boolean nodeIsGone throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/HttpPost.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/HttpPost.java index 703031b88c6..f9af80c78c9 100755 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/HttpPost.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/HttpPost.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.client.methods.BaseDavRequest; @@ -26,21 +26,16 @@ public class HttpPost extends BaseDavRequest { public HttpPost(URI uri) { - super(uri); + super(DavMethods.METHOD_POST, uri); } public HttpPost(String uri) { - super(URI.create(uri)); + this(URI.create(uri)); } @Override - public String getMethod() { - return DavMethods.METHOD_POST; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_NO_CONTENT || statusCode == DavServletResponse.SC_CREATED; } diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java index 1a497fb828b..fb48b088864 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/RepositoryServiceImpl.java @@ -21,28 +21,29 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.jcr.Credentials; import javax.jcr.ItemNotFoundException; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.FormBodyPart; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.message.BasicNameValuePair; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpUriRequest; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.client5.http.entity.mime.FormBodyPart; +import org.apache.hc.core5.http.message.BasicNameValuePair; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.commons.json.JsonParser; import org.apache.jackrabbit.commons.json.JsonUtil; @@ -337,10 +338,10 @@ public Iterator getItemInfos(SessionInfo sessionInfo, ItemId int depth = batchReadConfig.getDepth(path, this.getNamePathResolver(sessionInfo)); HttpGet request = new HttpGet(uri + "." + depth + ".json"); - HttpResponse response = null; + ClassicHttpResponse response = null; try { response = executeRequest(sessionInfo, request); - int statusCode = response.getStatusLine().getStatusCode(); + int statusCode = response.getCode(); if (statusCode == DavServletResponse.SC_OK) { HttpEntity entity = response.getEntity(); if (entity.getContentLength() == 0) { @@ -353,7 +354,10 @@ public Iterator getItemInfos(SessionInfo sessionInfo, ItemId ItemInfoJsonHandler handler = new ItemInfoJsonHandler(resolver, nInfo, getRootURI(sessionInfo), getQValueFactory(sessionInfo), getPathFactory(), getIdFactory()); JsonParser ps = new JsonParser(handler); - ps.parse(entity.getContent(), ContentType.get(entity).getCharset().name()); + // both the header and its charset parameter are optional; JSON defaults to UTF-8 + ContentType contentType = ContentType.parse(entity.getContentType()); + Charset charset = contentType == null ? null : contentType.getCharset(); + ps.parse(entity.getContent(), (charset == null ? StandardCharsets.UTF_8 : charset).name()); Iterator it = handler.getItemInfos(); if (!it.hasNext()) { @@ -367,7 +371,7 @@ public Iterator getItemInfos(SessionInfo sessionInfo, ItemId log.error("Internal error while retrieving NodeInfo for " + uri + ".", e); throw new RepositoryException(e.getMessage(), e); } finally { - request.releaseConnection(); + request.reset(); } } } @@ -382,7 +386,7 @@ public PropertyInfo getPropertyInfo(SessionInfo sessionInfo, PropertyId property HttpPropfind request = null; try { request = new HttpPropfind(uri, LAZY_PROPERTY_NAME_SET, DavConstants.DEPTH_0); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); MultiStatusResponse[] mresponses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -428,7 +432,7 @@ public PropertyInfo getPropertyInfo(SessionInfo sessionInfo, PropertyId property throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -466,8 +470,11 @@ public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNod } HttpPost request = null; try { - request = new HttpPost(getWorkspaceURI(sessionInfo)); - request.setHeader("Referer", request.getURI().toASCIIString()); + String workspaceUri = getWorkspaceURI(sessionInfo); + request = new HttpPost(workspaceUri); + // the absolute request URI: HttpClient 5's getRequestUri() would only + // return the path, which a CSRF referrer check may reject + request.setHeader("Referer", workspaceUri); addIfHeader(sessionInfo, request); NamePathResolver resolver = getNamePathResolver(sessionInfo); @@ -485,7 +492,7 @@ public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNod List nvps = Collections.singletonList(new BasicNameValuePair(PARAM_COPY, args.toString())); HttpEntity entity = new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")); request.setEntity(entity); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); } catch (IOException e) { throw new RepositoryException(e); @@ -493,7 +500,7 @@ public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNod throw ExceptionConverter.generate(e, request); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -502,8 +509,10 @@ public void copy(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNod public void clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNodeId, NodeId destParentNodeId, Name destName, boolean removeExisting) throws RepositoryException { HttpPost request = null; try { - request = new HttpPost(getWorkspaceURI(sessionInfo)); - request.setHeader("Referer", request.getURI().toASCIIString()); + String workspaceUri = getWorkspaceURI(sessionInfo); + request = new HttpPost(workspaceUri); + // the absolute request URI, see copy() + request.setHeader("Referer", workspaceUri); addIfHeader(sessionInfo, request); NamePathResolver resolver = getNamePathResolver(sessionInfo); @@ -522,7 +531,7 @@ public void clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNo List nvps = Collections.singletonList(new BasicNameValuePair(PARAM_CLONE, args.toString())); HttpEntity entity = new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")); request.setEntity(entity); - HttpResponse response = executeRequest(sessionInfo, request); + ClassicHttpResponse response = executeRequest(sessionInfo, request); request.checkSuccess(response); if (removeExisting) { clearItemUriCache(sessionInfo); @@ -533,7 +542,7 @@ public void clone(SessionInfo sessionInfo, String srcWorkspaceName, NodeId srcNo throw ExceptionConverter.generate(e, request); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } @@ -580,7 +589,12 @@ private BatchImpl(ItemId targetId, SessionInfo sessionInfo) { private void start() throws RepositoryException { checkConsumed(); - request.setHeader("Referer", request.getURI().toASCIIString()); + try { + // the absolute request URI, see copy() + request.setHeader("Referer", request.getUri().toASCIIString()); + } catch (URISyntaxException e) { + throw new RepositoryException(e); + } // add lock tokens addIfHeader(sessionInfo, request); @@ -599,16 +613,15 @@ private void start() throws RepositoryException { // engine has a form-size restriction (JCR-3726) Utils.addPart(PARAM_DIFF, buf.toString(), parts); - // JCR-4317: need RFC6532 mode so that values are encoded in UTF-8 - MultipartEntityBuilder b = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532); - for (FormBodyPart p : parts) { - b.addPart(p.getName(), p.getBody()); - } - request.setEntity(b.build()); + // JCR-4317: part names carry the JCR path and must survive as UTF-8, + // which MultipartEntityBuilder cannot do in HttpClient 5. + // The boundary must consist of token characters only ('=' is not one), + // because Rfc6532MultipartEntity does not quote it in the Content-Type + request.setEntity(new Rfc6532MultipartEntity(parts, "----Part_" + UUID.randomUUID())); - org.apache.http.client.HttpClient client = getClient(sessionInfo); + HttpClient client = getClient(sessionInfo); try { - HttpResponse response = client.execute(request, getContext(sessionInfo)); + ClassicHttpResponse response = client.executeOpen(null, request, getContext(sessionInfo)); request.checkSuccess(response); if (clear) { RepositoryServiceImpl.super.clearItemUriCache(sessionInfo); @@ -618,7 +631,7 @@ private void start() throws RepositoryException { } catch (DavException e) { throw ExceptionConverter.generate(e, request); } finally { - request.releaseConnection(); + request.reset(); } } diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntity.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntity.java new file mode 100644 index 00000000000..b179cc9118b --- /dev/null +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntity.java @@ -0,0 +1,210 @@ +/* + * 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.jackrabbit.spi2davex; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.apache.hc.client5.http.entity.mime.FormBodyPart; +import org.apache.hc.client5.http.entity.mime.MimeField; +import org.apache.hc.core5.function.Supplier; +import org.apache.hc.core5.http.ContentTooLongException; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; + +/** + * A multipart entity that writes part headers as UTF-8, as described by + * RFC 6532. + *

+ * The server matches parts by the JCR path carried in the {@code name} parameter of + * {@code Content-Disposition}, so a part name has to survive verbatim (JCR-4317). + * HttpClient 4 achieved this with {@code HttpMultipartMode.RFC6532}, but HttpClient 5 + * offers no equivalent through {@code MultipartEntityBuilder}: its {@code EXTENDED} and + * {@code STRICT} modes replace a non-ASCII part name with {@code '?'}, and its + * {@code LEGACY} mode preserves the name but omits the per-part {@code Content-Type} and + * {@code Content-Transfer-Encoding} headers. HttpClient 5 also advertises + * {@code charset=ISO-8859-1} on the entity content type, which makes a server decode + * those UTF-8 header bytes as Latin-1. + *

+ * This entity therefore does the framing itself. The parts are still built with + * {@code FormBodyPartBuilder}, whose generated headers are byte for byte what HttpClient 4 + * produced; only the assembly differs. The output is byte identical to that of HttpClient 4 + * in {@code RFC6532} mode, which {@code Rfc6532MultipartEntityTest} verifies. + */ +final class Rfc6532MultipartEntity implements HttpEntity { + + private static final byte[] CR_LF = { '\r', '\n' }; + private static final byte[] TWO_HYPHENS = { '-', '-' }; + + private final List parts; + private final String boundary; + private final long contentLength; + + Rfc6532MultipartEntity(List parts, String boundary) { + this.parts = parts; + this.boundary = boundary; + this.contentLength = computeContentLength(); + } + + /** + * @return the total length, or -1 as soon as any part has an unknown length, which + * matches what HttpClient 4 reported for a stream body + */ + private long computeContentLength() { + long total = 0; + for (FormBodyPart part : parts) { + long length = part.getBody().getContentLength(); + if (length < 0) { + return -1; + } + total += length; + } + CountingOutputStream framing = new CountingOutputStream(); + try { + writeTo(framing, false); + } catch (IOException e) { + throw new AssertionError("CountingOutputStream does not throw", e); + } + return total + framing.count; + } + + /** + * Measures the framing without buffering it: the part headers are written once + * more for real in {@link #writeTo(OutputStream)}. + */ + private static final class CountingOutputStream extends OutputStream { + + private long count; + + @Override + public void write(int b) { + count++; + } + + @Override + public void write(byte[] b, int off, int len) { + count += len; + } + } + + @Override + public void writeTo(OutputStream out) throws IOException { + writeTo(out, true); + } + + private void writeTo(OutputStream out, boolean writeContent) throws IOException { + byte[] boundaryBytes = boundary.getBytes(StandardCharsets.ISO_8859_1); + for (FormBodyPart part : parts) { + out.write(TWO_HYPHENS); + out.write(boundaryBytes); + out.write(CR_LF); + for (MimeField field : part.getHeader()) { + // the field body already carries any parameters, fully formatted + out.write(field.getName().getBytes(StandardCharsets.UTF_8)); + out.write(':'); + out.write(' '); + out.write(field.getBody().getBytes(StandardCharsets.UTF_8)); + out.write(CR_LF); + } + out.write(CR_LF); + if (writeContent) { + part.getBody().writeTo(out); + } + out.write(CR_LF); + } + out.write(TWO_HYPHENS); + out.write(boundaryBytes); + out.write(TWO_HYPHENS); + out.write(CR_LF); + } + + @Override + public String getContentType() { + // deliberately without a charset parameter, so that a server does not decode the + // UTF-8 part headers as anything else. The boundary is emitted unquoted and must + // therefore consist of token characters only. + return "multipart/form-data; boundary=" + boundary; + } + + @Override + public long getContentLength() { + return contentLength; + } + + @Override + public boolean isRepeatable() { + return contentLength != -1; + } + + @Override + public boolean isChunked() { + return contentLength == -1; + } + + @Override + public boolean isStreaming() { + return contentLength == -1; + } + + @Override + public String getContentEncoding() { + return null; + } + + /** + * The 25 KB limit HttpClient 4's {@code MultipartFormEntity.getContent()} enforced + * before buffering; anything larger (or of unknown length) is expected to go + * through {@link #writeTo(OutputStream)} instead. + */ + private static final long MAX_BUFFERED_CONTENT_LENGTH = 25 * 1024; + + @Override + public InputStream getContent() throws IOException { + // do not buffer arbitrarily large bodies; buffering would also drain any + // stream-backed part, leaving a subsequent writeTo with empty part bodies + if (contentLength < 0) { + throw new ContentTooLongException("Content length is unknown"); + } else if (contentLength > MAX_BUFFERED_CONTENT_LENGTH) { + throw new ContentTooLongException("Content length is too long: " + contentLength); + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + writeTo(buffer, true); + return new ByteArrayInputStream(buffer.toByteArray()); + } + + @Override + public Supplier> getTrailers() { + return null; + } + + @Override + public Set getTrailerNames() { + return Collections.emptySet(); + } + + @Override + public void close() throws IOException { + // the part bodies are closed by the caller via Utils.removeParts + } +} diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java index 7f1e93cccc4..c7a6eab28cd 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/Utils.java @@ -24,11 +24,12 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.FormBodyPart; -import org.apache.http.entity.mime.FormBodyPartBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; -import org.apache.http.entity.mime.content.StringBody; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.client5.http.entity.mime.FormBodyPart; +import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder; +import org.apache.hc.client5.http.entity.mime.InputStreamBody; +import org.apache.hc.client5.http.entity.mime.MimeField; +import org.apache.hc.client5.http.entity.mime.StringBody; import org.apache.jackrabbit.commons.json.JsonUtil; import org.apache.jackrabbit.commons.webdav.JcrValueType; import org.apache.jackrabbit.spi.QValue; @@ -39,8 +40,31 @@ final class Utils { private static final String DEFAULT_CHARSET = "UTF-8"; private static final ContentType DEFAULT_TYPE = ContentType.create("text/plain", DEFAULT_CHARSET); + private static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; + private static final String TRANSFER_ENCODING_TEXT = "8bit"; + private static final String TRANSFER_ENCODING_BINARY = "binary"; + private Utils() {}; + /** + * Quotes a Content-Disposition parameter value, escaping backslashes and quotes the + * way HttpClient 4 did. + */ + private static String quote(String value) { + return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; + } + + /** + * HttpClient 5 no longer derives a Content-Transfer-Encoding header from the body, + * whereas HttpClient 4 emitted 8bit for string bodies and binary for stream bodies. + * Adding it after the part is built keeps it in the same position as before, i.e. + * after Content-Disposition and Content-Type. + */ + private static FormBodyPart withTransferEncoding(FormBodyPart part, String encoding) { + part.getHeader().addField(new MimeField(CONTENT_TRANSFER_ENCODING, encoding)); + return part; + } + static String getJsonKey(String str) { return JsonUtil.getJsonString(str) + ":"; } @@ -80,7 +104,9 @@ static String getJsonString(QValue value) throws RepositoryException { * @param value */ static void addPart(String paramName, String value, List parts) { - parts.add(FormBodyPartBuilder.create().setName(paramName).setBody(new StringBody(value, DEFAULT_TYPE)).build()); + parts.add(withTransferEncoding( + FormBodyPartBuilder.create().setName(paramName).setBody(new StringBody(value, DEFAULT_TYPE)).build(), + TRANSFER_ENCODING_TEXT)); } /** @@ -98,17 +124,29 @@ static void addPart(String paramName, QValue value, NamePathResolver resolver, L switch (value.getType()) { case PropertyType.BINARY: binaries.add(value); - // server detects binaries based on presence of filename parameters (JCR-4154) - part = builder.setBody(new InputStreamBody(value.getStream(), ctype, paramName)).build(); + // server detects binaries based on presence of filename parameters (JCR-4154). + // Set the Content-Disposition explicitly: for a path outside ISO-8859-1 the + // builder would append an RFC 5987 encoded filename* parameter that + // HttpClient 4 never emitted, and the server would prefer it over the + // verbatim JCR path in the filename parameter + part = withTransferEncoding( + builder.setBody(new InputStreamBody(value.getStream(), ctype, paramName)) + .setField("Content-Disposition", + "form-data; name=" + quote(paramName) + "; filename=" + quote(paramName)) + .build(), + TRANSFER_ENCODING_BINARY); break; case PropertyType.NAME: - part = builder.setBody(new StringBody(resolver.getJCRName(value.getName()), ctype)).build(); + part = withTransferEncoding( + builder.setBody(new StringBody(resolver.getJCRName(value.getName()), ctype)).build(), TRANSFER_ENCODING_TEXT); break; case PropertyType.PATH: - part = builder.setBody(new StringBody(resolver.getJCRPath(value.getPath()), ctype)).build(); + part = withTransferEncoding( + builder.setBody(new StringBody(resolver.getJCRPath(value.getPath()), ctype)).build(), TRANSFER_ENCODING_TEXT); break; default: - part = builder.setBody(new StringBody(value.getString(), ctype)).build(); + part = withTransferEncoding( + builder.setBody(new StringBody(value.getString(), ctype)).build(), TRANSFER_ENCODING_TEXT); } parts.add(part); diff --git a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java index e309e970f15..b97e328eea4 100644 --- a/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java +++ b/jackrabbit-spi2dav/src/main/java/org/apache/jackrabbit/spi2davex/ValueLoader.java @@ -25,12 +25,13 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; -import org.apache.http.Header; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpHead; -import org.apache.http.protocol.HttpContext; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.message.StatusLine; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.classic.methods.HttpHead; +import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.jackrabbit.commons.webdav.JcrRemotingConstants; import org.apache.jackrabbit.spi2dav.ExceptionConverter; import org.apache.jackrabbit.spi2dav.ItemResourceConstants; @@ -59,15 +60,15 @@ class ValueLoader { void loadBinary(String uri, int index, Target target) throws RepositoryException, IOException { HttpGet request = new HttpGet(uri); try { - HttpResponse response = client.execute(request, context); - int statusCode = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = client.executeOpen(null, request, context); + int statusCode = response.getCode(); if (statusCode == DavServletResponse.SC_OK) { target.setStream(response.getEntity().getContent()); } else { - throw ExceptionConverter.generate(new DavException(statusCode, ("Unable to load binary at " + uri + " - Status line = " + response.getStatusLine()))); + throw ExceptionConverter.generate(new DavException(statusCode, ("Unable to load binary at " + uri + " - Status line = " + new StatusLine(response)))); } } finally { - request.releaseConnection(); + request.reset(); } } @@ -75,8 +76,8 @@ public Map loadHeaders(String uri, String[] headerNames) throws RepositoryException { HttpHead request = new HttpHead(uri); try { - HttpResponse response = client.execute(request, context); - int statusCode = response.getStatusLine().getStatusCode(); + ClassicHttpResponse response = client.executeOpen(null, request, context); + int statusCode = response.getCode(); if (statusCode == DavServletResponse.SC_OK) { Map headers = new HashMap(); for (String name : headerNames) { @@ -87,10 +88,10 @@ public Map loadHeaders(String uri, String[] headerNames) throws } return headers; } else { - throw ExceptionConverter.generate(new DavException(statusCode, ("Unable to load headers at " + uri + " - Status line = " + response.getStatusLine().toString()))); + throw ExceptionConverter.generate(new DavException(statusCode, ("Unable to load headers at " + uri + " - Status line = " + new StatusLine(response)))); } } finally { - request.releaseConnection(); + request.reset(); } } @@ -101,7 +102,7 @@ int loadType(String uri) throws RepositoryException, IOException { HttpPropfind request = null; try { request = new HttpPropfind(uri, nameSet, DavConstants.DEPTH_0); - HttpResponse response = client.execute(request, context); + ClassicHttpResponse response = client.executeOpen(null, request, context); request.checkSuccess(response); MultiStatusResponse[] responses = request.getResponseBodyAsMultiStatus(response).getResponses(); @@ -120,7 +121,7 @@ int loadType(String uri) throws RepositoryException, IOException { throw ExceptionConverter.generate(e); } finally { if (request != null) { - request.releaseConnection(); + request.reset(); } } } diff --git a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/ConnectionTest.java b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/ConnectionTest.java index 36a95ddf58f..c142667653f 100644 --- a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/ConnectionTest.java +++ b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/ConnectionTest.java @@ -24,10 +24,11 @@ import javax.jcr.RepositoryException; import javax.jcr.SimpleCredentials; +import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLPeerUnverifiedException; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.http.conn.ConnectTimeoutException; +import org.apache.hc.client5.http.ConnectTimeoutException; import org.apache.jackrabbit.spi.RepositoryService; import org.apache.jackrabbit.spi2davex.Spi2davexRepositoryServiceFactory; import org.apache.jackrabbit.webdav.server.WebDAVTestBase; @@ -48,7 +49,10 @@ RepositoryService createRepositoryService(boolean isViaHttps, final Map parameters = new HashMap<>(); if (isViaHttps) { parameters.put(Spi2davexRepositoryServiceFactory.PARAM_REPOSITORY_URI, - new URI("https", null, remotingUri.getHost(), httpsUri.getPort(), remotingUri.getPath(), null, null).toString()); + // take the host from httpsUri, which the test base pins to a single address: + // where localhost resolves to both 127.0.0.1 and ::1, HttpClient 5 retries + // the next address after a TLS failure and reports a connection error instead + new URI("https", null, httpsUri.getHost(), httpsUri.getPort(), remotingUri.getPath(), null, null).toString()); } else { parameters.put(Spi2davexRepositoryServiceFactory.PARAM_REPOSITORY_URI, remotingUri.toString()); } @@ -85,9 +89,13 @@ public void testObtainWithTLSSelfSignedCertAllowed() throws RepositoryException, try { repositoryService.obtain(new SimpleCredentials("admin", "admin".toCharArray()), null); } catch (RepositoryException e) { - Throwable cause = ExceptionUtils.getRootCause(e); - if (!(cause instanceof SSLPeerUnverifiedException)) { - fail("should have failed with SSLPeerUnverifiedException but got " + e.getCause()); + // HttpClient 5 enables the JSSE built-in endpoint identification by default, which rejects the + // host name during the handshake (SSLHandshakeException wrapping a CertificateException) instead + // of afterwards (SSLPeerUnverifiedException, as thrown by the client-side verifier of HttpClient 4) + boolean isHostnameVerificationFailure = ExceptionUtils.getThrowableList(e).stream() + .anyMatch(t -> t instanceof SSLPeerUnverifiedException || t instanceof SSLHandshakeException); + if (!isHostnameVerificationFailure) { + fail("should have failed with SSLPeerUnverifiedException or SSLHandshakeException but got " + e.getCause()); } } } diff --git a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/DavPropertyTest.java b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/DavPropertyTest.java index 65201d09d61..87dcb8d8df1 100644 --- a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/DavPropertyTest.java +++ b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/DavPropertyTest.java @@ -23,9 +23,9 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpClient; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.client5.http.classic.HttpClient; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.spi.AbstractSPITest; import org.apache.jackrabbit.spi.Batch; @@ -136,7 +136,7 @@ private static void assertPropertyNames(DavPropertyNameSet expected, DavProperty private DavPropertyNameSet doPropFindNames(String uri) throws Exception { HttpPropfind request = new HttpPropfind(uri, DavConstants.PROPFIND_PROPERTY_NAMES, DavConstants.DEPTH_0); HttpClient cl = rs.getClient(si); - HttpResponse response = cl.execute(request, rs.getContext(si)); + ClassicHttpResponse response = cl.executeOpen(null, request, rs.getContext(si)); request.checkSuccess(response); MultiStatus ms = request.getResponseBodyAsMultiStatus(response); @@ -147,7 +147,7 @@ private DavPropertyNameSet doPropFindNames(String uri) throws Exception { private DavPropertyNameSet doPropFindAll(String uri) throws Exception { HttpPropfind request = new HttpPropfind(uri, DavConstants.PROPFIND_ALL_PROP, DavConstants.DEPTH_0); HttpClient cl = rs.getClient(si); - HttpResponse response = cl.execute(request, rs.getContext(si)); + ClassicHttpResponse response = cl.executeOpen(null, request, rs.getContext(si)); request.checkSuccess(response); MultiStatus ms = request.getResponseBodyAsMultiStatus(response); @@ -158,7 +158,7 @@ private DavPropertyNameSet doPropFindAll(String uri) throws Exception { private DavPropertyNameSet doPropFindByProp(String uri, DavPropertyNameSet props) throws Exception { HttpPropfind request = new HttpPropfind(uri, DavConstants.PROPFIND_BY_PROPERTY, props, DavConstants.DEPTH_0); HttpClient cl = rs.getClient(si); - HttpResponse response = cl.execute(request, rs.getContext(si)); + ClassicHttpResponse response = cl.executeOpen(null, request, rs.getContext(si)); request.checkSuccess(response); MultiStatus ms = request.getResponseBodyAsMultiStatus(response); diff --git a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategyTest.java b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategyTest.java new file mode 100644 index 00000000000..4388d12175c --- /dev/null +++ b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/GetHeadRedirectStrategyTest.java @@ -0,0 +1,95 @@ +/* + * 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.jackrabbit.spi2dav; + +import java.net.URI; + +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.message.BasicClassicHttpRequest; +import org.apache.hc.core5.http.message.BasicClassicHttpResponse; +import org.apache.hc.core5.http.protocol.BasicHttpContext; +import org.apache.jackrabbit.webdav.DavMethods; +import org.junit.Assert; +import org.junit.Test; + +/** + * Checks that automatic redirects stay restricted to the safe methods, as they were + * under HttpClient 4. HttpClient 5 would otherwise follow redirects for every method. + */ +public class GetHeadRedirectStrategyTest { + + private boolean isRedirected(String method, int status, boolean withLocation) throws Exception { + BasicClassicHttpRequest request = new BasicClassicHttpRequest(method, URI.create("http://localhost/a")); + BasicClassicHttpResponse response = new BasicClassicHttpResponse(status); + if (withLocation) { + response.setHeader(HttpHeaders.LOCATION, "http://localhost/b"); + } + return GetHeadRedirectStrategy.INSTANCE.isRedirected(request, response, new BasicHttpContext()); + } + + @Test + public void testSafeMethodsAreRedirected() throws Exception { + for (int status : new int[] { HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY, + HttpStatus.SC_TEMPORARY_REDIRECT, HttpStatus.SC_PERMANENT_REDIRECT }) { + Assert.assertTrue("GET should follow " + status, isRedirected("GET", status, true)); + Assert.assertTrue("HEAD should follow " + status, isRedirected("HEAD", status, true)); + } + } + + @Test + public void testWriteMethodsAreNotRedirected() throws Exception { + String[] methods = { "PUT", "DELETE", "POST", DavMethods.METHOD_MOVE, DavMethods.METHOD_COPY, + DavMethods.METHOD_PROPPATCH, DavMethods.METHOD_MKCOL, DavMethods.METHOD_LOCK }; + for (String method : methods) { + for (int status : new int[] { HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY, + HttpStatus.SC_TEMPORARY_REDIRECT, HttpStatus.SC_PERMANENT_REDIRECT }) { + Assert.assertFalse(method + " must not follow " + status, isRedirected(method, status, true)); + } + } + } + + /** + * PROPFIND is read-only but was not redirected by HttpClient 4 either, since only + * GET and HEAD were on the list. + */ + @Test + public void testPropfindIsNotRedirected() throws Exception { + Assert.assertFalse(isRedirected(DavMethods.METHOD_PROPFIND, HttpStatus.SC_MOVED_PERMANENTLY, true)); + } + + @Test + public void testSeeOtherIsAlwaysRedirected() throws Exception { + // 303 directs the client to fetch a different resource with GET + Assert.assertTrue(isRedirected("GET", HttpStatus.SC_SEE_OTHER, true)); + Assert.assertTrue(isRedirected(DavMethods.METHOD_MOVE, HttpStatus.SC_SEE_OTHER, true)); + } + + @Test + public void testFoundRequiresLocationHeader() throws Exception { + Assert.assertTrue(isRedirected("GET", HttpStatus.SC_MOVED_TEMPORARILY, true)); + Assert.assertFalse(isRedirected("GET", HttpStatus.SC_MOVED_TEMPORARILY, false)); + } + + @Test + public void testNonRedirectStatusIsNotRedirected() throws Exception { + for (int status : new int[] { HttpStatus.SC_OK, HttpStatus.SC_NOT_MODIFIED, HttpStatus.SC_USE_PROXY, + HttpStatus.SC_MULTI_STATUS, HttpStatus.SC_NOT_FOUND }) { + Assert.assertFalse("must not follow " + status, isRedirected("GET", status, true)); + } + } +} diff --git a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImplIT.java b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImplIT.java index 64377e7b70b..996037dd858 100644 --- a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImplIT.java +++ b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2dav/RepositoryServiceImplIT.java @@ -29,12 +29,11 @@ import java.nio.file.Path; import javax.jcr.RepositoryException; -import javax.net.ssl.SSLException; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.hc.client5.http.ClientProtocolException; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -61,7 +60,7 @@ public void testGetAgainstTrustedCertServer() throws RepositoryException, Client RepositoryServiceImpl repositoryServiceImpl = RepositoryServiceImplTest.getRepositoryService("https://jackrabbit.apache.org/jcr", ConnectionOptions.builder().build()); HttpClient client = repositoryServiceImpl.getClient(null); HttpGet get = new HttpGet("https://jackrabbit.apache.org/jcr/index.html"); - String content = client.execute(get, new BasicResponseHandler()); + String content = client.execute(get, new BasicHttpClientResponseHandler()); assertFalse(content.isEmpty()); } @@ -81,8 +80,12 @@ public void testGetAgainstTrustedCertServerWithSystemProperties() throws Reposit RepositoryServiceImpl repositoryServiceImpl = RepositoryServiceImplTest.getRepositoryService("https://jackrabbit.apache.org/jcr", connectionOptions); HttpClient client = repositoryServiceImpl.getClient(null); HttpGet get = new HttpGet("https://jackrabbit.apache.org/jcr/index.html"); - // connection must fail as cert is not trusted due to used trust store being empty - assertThrows(SSLException.class, () -> client.execute(get, new BasicResponseHandler())); + // The connection must fail, as the certificate is not trusted with an empty + // trust store. The exact exception type cannot be asserted: HttpClient 5 runs + // the TLS upgrade inside its per-address retry block, so for a host resolving + // to several addresses the SSLException raised by the first address is logged + // at DEBUG and the caller sees whatever the last address produced. + assertThrows(IOException.class, () -> client.execute(get, new BasicHttpClientResponseHandler())); } finally { setOrClearSystemProperty("javax.net.ssl.trustStore", oldTrustStore); setOrClearSystemProperty("javax.net.ssl.trustStorePassword", oldTrustStorePassword); diff --git a/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntityTest.java b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntityTest.java new file mode 100644 index 00000000000..a203df49570 --- /dev/null +++ b/jackrabbit-spi2dav/src/test/java/org/apache/jackrabbit/spi2davex/Rfc6532MultipartEntityTest.java @@ -0,0 +1,161 @@ +/* + * 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.jackrabbit.spi2davex; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import org.apache.hc.client5.http.entity.mime.FormBodyPart; +import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder; +import org.apache.hc.client5.http.entity.mime.InputStreamBody; +import org.apache.hc.client5.http.entity.mime.MimeField; +import org.apache.hc.client5.http.entity.mime.StringBody; +import org.apache.hc.core5.http.ContentType; +import org.junit.Assert; +import org.junit.Test; + +/** + * Pins the wire format produced for a davex batch, which has to stay byte for byte what + * HttpClient 4 wrote in {@code RFC6532} mode. The expectations below were taken from + * HttpClient 4.5.14 output. The server matches parts by the JCR path in the {@code name} + * parameter, so a non-ASCII name surviving verbatim is the point of the exercise + * (JCR-4317); an HttpClient upgrade that changes any of this should fail here rather than + * in the remoting conformance suite. + */ +public class Rfc6532MultipartEntityTest { + + private static final ContentType TEXT = ContentType.create("text/plain", "UTF-8"); + private static final ContentType BINARY = ContentType.create("jcr-value/binary", "UTF-8"); + + private static FormBodyPart part(String name, String value, ContentType type, String encoding) { + FormBodyPart part = FormBodyPartBuilder.create().setName(name) + .setBody(new StringBody(value, type)).build(); + part.getHeader().addField(new MimeField("Content-Transfer-Encoding", encoding)); + return part; + } + + private static String write(List parts) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + new Rfc6532MultipartEntity(parts, "B").writeTo(out); + return new String(out.toByteArray(), StandardCharsets.UTF_8); + } + + @Test + public void testNonAsciiPartNameSurvivesAsUtf8() throws Exception { + String name = "/testroot/Test-ä/jcr:content/jcr:data"; + List parts = new ArrayList(); + FormBodyPart binary = FormBodyPartBuilder.create().setName(name) + .setBody(new InputStreamBody( + new ByteArrayInputStream("XYZ".getBytes(StandardCharsets.UTF_8)), BINARY, name)) + .build(); + binary.getHeader().addField(new MimeField("Content-Transfer-Encoding", "binary")); + parts.add(binary); + + Assert.assertEquals( + "--B\r\n" + + "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + name + "\"\r\n" + + "Content-Type: jcr-value/binary; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: binary\r\n" + + "\r\n" + + "XYZ\r\n" + + "--B--\r\n", + write(parts)); + } + + @Test + public void testDiffPartKeepsTypeAndEncoding() throws Exception { + List parts = new ArrayList(); + parts.add(part(":diff", "+/testroot/Test-ä : {}", TEXT, "8bit")); + + Assert.assertEquals( + "--B\r\n" + + "Content-Disposition: form-data; name=\":diff\"\r\n" + + "Content-Type: text/plain; charset=UTF-8\r\n" + + "Content-Transfer-Encoding: 8bit\r\n" + + "\r\n" + + "+/testroot/Test-ä : {}\r\n" + + "--B--\r\n", + write(parts)); + } + + /** + * JCR names may contain quotes and backslashes, which have to be escaped in the + * Content-Disposition parameters exactly as HttpClient 4 escaped them. + */ + @Test + public void testQuotesAndBackslashesInPartNameAreEscaped() throws Exception { + List parts = new ArrayList(); + parts.add(part("/testroot/na\"me-with\\quote-ä", "v", TEXT, "8bit")); + + Assert.assertTrue(write(parts).contains( + "name=\"/testroot/na\\\"me-with\\\\quote-ä\"")); + } + + /** + * For a path outside ISO-8859-1 the builder would append an RFC 5987 encoded + * {@code filename*} parameter that HttpClient 4 never emitted, so {@code Utils} + * sets the Content-Disposition explicitly; this pins that construction. + */ + @Test + public void testNonLatin1PartNameSurvivesWithoutFilenameStar() throws Exception { + String name = "/testroot/テスト/jcr:content/jcr:data"; + List parts = new ArrayList(); + FormBodyPart binary = FormBodyPartBuilder.create().setName(name) + .setBody(new InputStreamBody( + new ByteArrayInputStream("XYZ".getBytes(StandardCharsets.UTF_8)), BINARY, name)) + .setField("Content-Disposition", "form-data; name=\"" + name + "\"; filename=\"" + name + "\"") + .build(); + binary.getHeader().addField(new MimeField("Content-Transfer-Encoding", "binary")); + parts.add(binary); + + String written = write(parts); + Assert.assertFalse("no RFC 5987 filename* parameter expected", written.contains("filename*")); + Assert.assertTrue(written.contains( + "Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + name + "\"\r\n")); + } + + @Test + public void testContentTypeCarriesNoCharset() { + // a charset here would make the server decode the UTF-8 part headers as something else + Assert.assertEquals("multipart/form-data; boundary=B", + new Rfc6532MultipartEntity(new ArrayList(), "B").getContentType()); + } + + @Test + public void testLengthIsKnownForStringPartsAndUnknownForStreams() throws Exception { + List strings = new ArrayList(); + strings.add(part(":diff", "abc", TEXT, "8bit")); + Rfc6532MultipartEntity known = new Rfc6532MultipartEntity(strings, "B"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + known.writeTo(out); + Assert.assertEquals(out.size(), known.getContentLength()); + Assert.assertTrue(known.isRepeatable()); + Assert.assertFalse(known.isChunked()); + + List streams = new ArrayList(); + streams.add(FormBodyPartBuilder.create().setName("bin") + .setBody(new InputStreamBody( + new ByteArrayInputStream("XYZ".getBytes(StandardCharsets.UTF_8)), BINARY, "bin")) + .build()); + Rfc6532MultipartEntity unknown = new Rfc6532MultipartEntity(streams, "B"); + Assert.assertEquals(-1, unknown.getContentLength()); + Assert.assertTrue(unknown.isChunked()); + } +} diff --git a/jackrabbit-webdav/pom.xml b/jackrabbit-webdav/pom.xml index 468a93f378e..d65a794b1a7 100644 --- a/jackrabbit-webdav/pom.xml +++ b/jackrabbit-webdav/pom.xml @@ -69,14 +69,12 @@ provided - org.apache.httpcomponents - httpcore - 4.4.16 + org.apache.httpcomponents.core5 + httpcore5 - org.apache.httpcomponents - httpclient - 4.5.14 + org.apache.httpcomponents.client5 + httpclient5 org.slf4j diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BaseDavRequest.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BaseDavRequest.java index ca365b75f02..da649c36234 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BaseDavRequest.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BaseDavRequest.java @@ -22,10 +22,9 @@ import javax.xml.parsers.ParserConfigurationException; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.HttpEntity; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavException; import org.apache.jackrabbit.webdav.MultiStatus; @@ -44,13 +43,12 @@ /** * Base class for HTTP request classes defined in this package. */ -public abstract class BaseDavRequest extends HttpEntityEnclosingRequestBase { +public abstract class BaseDavRequest extends HttpUriRequestBase { private static Logger log = LoggerFactory.getLogger(BaseDavRequest.class); - public BaseDavRequest(URI uri) { - super(); - super.setURI(uri); + public BaseDavRequest(String method, URI uri) { + super(method, uri); } /** @@ -82,15 +80,15 @@ public Document getResponseBodyAsDocument(HttpEntity entity) throws IOException * @throws IllegalStateException when response does not represent a {@link MultiStatus} * @throws DavException for failures in obtaining/parsing the response body */ - public MultiStatus getResponseBodyAsMultiStatus(HttpResponse response) throws DavException { + public MultiStatus getResponseBodyAsMultiStatus(ClassicHttpResponse response) throws DavException { try { Document doc = getResponseBodyAsDocument(response.getEntity()); if (doc == null) { - throw new DavException(response.getStatusLine().getStatusCode(), "no response body"); + throw new DavException(response.getCode(), "no response body"); } return MultiStatus.createFromXml(doc.getDocumentElement()); } catch (IOException ex) { - throw new DavException(response.getStatusLine().getStatusCode(), ex); + throw new DavException(response.getCode(), ex); } } @@ -99,29 +97,29 @@ public MultiStatus getResponseBodyAsMultiStatus(HttpResponse response) throws Da * @throws IllegalStateException when response does not represent a {@link LockDiscovery} * @throws DavException for failures in obtaining/parsing the response body */ - public LockDiscovery getResponseBodyAsLockDiscovery(HttpResponse response) throws DavException { + public LockDiscovery getResponseBodyAsLockDiscovery(ClassicHttpResponse response) throws DavException { try { Document doc = getResponseBodyAsDocument(response.getEntity()); if (doc == null) { - throw new DavException(response.getStatusLine().getStatusCode(), "no response body"); + throw new DavException(response.getCode(), "no response body"); } Element root = doc.getDocumentElement(); if (!DomUtil.matches(root, DavConstants.XML_PROP, DavConstants.NAMESPACE) && DomUtil.hasChildElement(root, DavConstants.PROPERTY_LOCKDISCOVERY, DavConstants.NAMESPACE)) { - throw new DavException(response.getStatusLine().getStatusCode(), + throw new DavException(response.getCode(), "Missing DAV:prop response body in LOCK response."); } Element lde = DomUtil.getChildElement(root, DavConstants.PROPERTY_LOCKDISCOVERY, DavConstants.NAMESPACE); if (!DomUtil.hasChildElement(lde, DavConstants.XML_ACTIVELOCK, DavConstants.NAMESPACE)) { - throw new DavException(response.getStatusLine().getStatusCode(), + throw new DavException(response.getCode(), "The DAV:lockdiscovery must contain a least a single DAV:activelock in response to a successful LOCK request."); } return LockDiscovery.createFromXml(lde); } catch (IOException ex) { - throw new DavException(response.getStatusLine().getStatusCode(), ex); + throw new DavException(response.getCode(), ex); } } @@ -130,18 +128,18 @@ public LockDiscovery getResponseBodyAsLockDiscovery(HttpResponse response) throw * @throws IllegalStateException when response does not represent a {@link SubscriptionDiscovery} * @throws DavException for failures in obtaining/parsing the response body */ - public SubscriptionDiscovery getResponseBodyAsSubscriptionDiscovery(HttpResponse response) throws DavException { + public SubscriptionDiscovery getResponseBodyAsSubscriptionDiscovery(ClassicHttpResponse response) throws DavException { try { Document doc = getResponseBodyAsDocument(response.getEntity()); if (doc == null) { - throw new DavException(response.getStatusLine().getStatusCode(), "no response body"); + throw new DavException(response.getCode(), "no response body"); } Element root = doc.getDocumentElement(); if (!DomUtil.matches(root, DavConstants.XML_PROP, DavConstants.NAMESPACE) && DomUtil.hasChildElement(root, ObservationConstants.SUBSCRIPTIONDISCOVERY.getName(), ObservationConstants.SUBSCRIPTIONDISCOVERY.getNamespace())) { - throw new DavException(response.getStatusLine().getStatusCode(), + throw new DavException(response.getCode(), "Missing DAV:prop response body in SUBSCRIBE response."); } @@ -151,11 +149,11 @@ public SubscriptionDiscovery getResponseBodyAsSubscriptionDiscovery(HttpResponse if (((Subscription[]) sd.getValue()).length > 0) { return sd; } else { - throw new DavException(response.getStatusLine().getStatusCode(), + throw new DavException(response.getCode(), "Missing 'subscription' elements in SUBSCRIBE response body. At least a single subscription must be present if SUBSCRIBE was successful."); } } catch (IOException ex) { - throw new DavException(response.getStatusLine().getStatusCode(), ex); + throw new DavException(response.getCode(), ex); } } @@ -164,22 +162,22 @@ public SubscriptionDiscovery getResponseBodyAsSubscriptionDiscovery(HttpResponse * @throws IllegalStateException when response does not represent a {@link EventDiscovery} * @throws DavException for failures in obtaining/parsing the response body */ - public EventDiscovery getResponseBodyAsEventDiscovery(HttpResponse response) throws DavException { + public EventDiscovery getResponseBodyAsEventDiscovery(ClassicHttpResponse response) throws DavException { try { Document doc = getResponseBodyAsDocument(response.getEntity()); if (doc == null) { - throw new DavException(response.getStatusLine().getStatusCode(), "no response body"); + throw new DavException(response.getCode(), "no response body"); } return EventDiscovery.createFromXml(doc.getDocumentElement()); } catch (IOException ex) { - throw new DavException(response.getStatusLine().getStatusCode(), ex); + throw new DavException(response.getCode(), ex); } } /** * Check the response and throw when it is considered to represent a failure. */ - public void checkSuccess(HttpResponse response) throws DavException { + public void checkSuccess(ClassicHttpResponse response) throws DavException { if (!succeeded(response)) { throw getResponseException(response); } @@ -189,31 +187,33 @@ public void checkSuccess(HttpResponse response) throws DavException { * Obtain a {@link DavException} representing the response. * @throws IllegalStateException when the response is considered to be successful */ - public DavException getResponseException(HttpResponse response) { + public DavException getResponseException(ClassicHttpResponse response) { if (succeeded(response)) { String msg = "Cannot retrieve exception from successful response."; log.warn(msg); throw new IllegalStateException(msg); } - StatusLine st = response.getStatusLine(); Element responseRoot = null; try { - responseRoot = getResponseBodyAsDocument(response.getEntity()).getDocumentElement(); + Document doc = getResponseBodyAsDocument(response.getEntity()); + if (doc != null) { + responseRoot = doc.getDocumentElement(); + } } catch (IOException e) { // non-parseable body -> use null element } - return new DavException(st.getStatusCode(), st.getReasonPhrase(), null, responseRoot); + return new DavException(response.getCode(), response.getReasonPhrase(), null, responseRoot); } /** - * Check the provided {@link HttpResponse} for successful execution. The default implementation treats all + * Check the provided {@link ClassicHttpResponse} for successful execution. The default implementation treats all * 2xx status codes (RFC 7231, Section 6.3). * Implementations can further restrict the accepted range of responses (or even check the response body). */ - public boolean succeeded(HttpResponse response) { - int status = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int status = response.getCode(); return status >= 200 && status <= 299; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpBind.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpBind.java index 4c2161810f5..cdcecaf0876 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpBind.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpBind.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.bind.BindInfo; @@ -33,7 +33,7 @@ public class HttpBind extends BaseDavRequest { public HttpBind(URI uri, BindInfo info) throws IOException { - super(uri); + super(DavMethods.METHOD_BIND, uri); super.setEntity(XmlEntity.create(info)); } @@ -42,13 +42,8 @@ public HttpBind(String uri, BindInfo info) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_BIND; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_CREATED; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckin.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckin.java index 06b67aec070..1af8c825091 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckin.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckin.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -31,7 +31,7 @@ public class HttpCheckin extends BaseDavRequest { public HttpCheckin(URI uri) { - super(uri); + super(DavMethods.METHOD_CHECKIN, uri); } public HttpCheckin(String uri) { @@ -39,13 +39,8 @@ public HttpCheckin(String uri) { } @Override - public String getMethod() { - return DavMethods.METHOD_CHECKIN; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_CREATED; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckout.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckout.java index a7f5e009bda..0ecdd361a3a 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckout.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCheckout.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -31,7 +31,7 @@ public class HttpCheckout extends BaseDavRequest { public HttpCheckout(URI uri) { - super(uri); + super(DavMethods.METHOD_CHECKOUT, uri); } public HttpCheckout(String uri) { @@ -39,13 +39,8 @@ public HttpCheckout(String uri) { } @Override - public String getMethod() { - return DavMethods.METHOD_CHECKOUT; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCopy.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCopy.java index 4f5337b8732..0b413c1be3e 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCopy.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpCopy.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -32,7 +32,7 @@ public class HttpCopy extends BaseDavRequest { public HttpCopy(URI uri, URI dest, boolean overwrite, boolean shallow) { - super(uri); + super(DavMethods.METHOD_COPY, uri); super.setHeader(DavConstants.HEADER_DESTINATION, dest.toASCIIString()); if (!overwrite) { super.setHeader(DavConstants.HEADER_OVERWRITE, "F"); @@ -47,13 +47,8 @@ public HttpCopy(String uri, String dest, boolean overwrite, boolean shallow) { } @Override - public String getMethod() { - return DavMethods.METHOD_COPY; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_CREATED || statusCode == DavServletResponse.SC_NO_CONTENT; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpDelete.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpDelete.java index 99c283bab96..f948d5e0234 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpDelete.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpDelete.java @@ -29,15 +29,11 @@ public class HttpDelete extends BaseDavRequest { public HttpDelete(URI uri){ - super(uri); + super(DavMethods.METHOD_DELETE, uri); } public HttpDelete(String uri) { this(URI.create(uri)); } - @Override - public String getMethod() { - return DavMethods.METHOD_DELETE; - } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLabel.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLabel.java index 05c6f48ceea..a45b5fc951f 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLabel.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLabel.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.header.DepthHeader; @@ -34,7 +34,7 @@ public class HttpLabel extends BaseDavRequest { public HttpLabel(URI uri, LabelInfo labelInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_LABEL, uri); DepthHeader dh = new DepthHeader(labelInfo.getDepth()); super.setHeader(dh.getHeaderName(), dh.getHeaderValue()); super.setEntity(XmlEntity.create(labelInfo)); @@ -45,13 +45,8 @@ public HttpLabel(String uri, LabelInfo labelInfo) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_LABEL; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLock.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLock.java index 171776d17c5..5ea33ce6db1 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLock.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpLock.java @@ -20,8 +20,8 @@ import java.net.URI; import java.util.Arrays; -import org.apache.http.Header; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -45,7 +45,7 @@ public class HttpLock extends BaseDavRequest { private final boolean isRefresh; public HttpLock(URI uri, LockInfo lockInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_LOCK, uri); TimeoutHeader th = new TimeoutHeader(lockInfo.getTimeout()); super.setHeader(th.getHeaderName(), th.getHeaderValue()); @@ -62,7 +62,7 @@ public HttpLock(String uri, LockInfo lockInfo) throws IOException { } public HttpLock(URI uri, long timeout, String[] lockTokens) { - super(uri); + super(DavMethods.METHOD_LOCK, uri); TimeoutHeader th = new TimeoutHeader(timeout); super.setHeader(th.getHeaderName(), th.getHeaderValue()); @@ -75,22 +75,17 @@ public HttpLock(String uri, long timeout, String[] lockTokens) { this(URI.create(uri), timeout, lockTokens); } - @Override - public String getMethod() { - return DavMethods.METHOD_LOCK; - } - - public String getLockToken(HttpResponse response) { + public String getLockToken(ClassicHttpResponse response) { Header[] ltHeader = response.getHeaders(DavConstants.HEADER_LOCK_TOKEN); if (ltHeader == null || ltHeader.length == 0) { return null; } else if (ltHeader.length != 1) { - LOG.debug("Multiple 'Lock-Token' header fields in response for " + getURI() + ": " + Arrays.asList(ltHeader)); + LOG.debug("Multiple 'Lock-Token' header fields in response for " + getRequestUri() + ": " + Arrays.asList(ltHeader)); return null; } else { String v = ltHeader[0].getValue().trim(); if (!v.startsWith("<") || !v.endsWith(">")) { - LOG.debug("Invalid 'Lock-Token' header field in response for " + getURI() + ": " + Arrays.asList(ltHeader)); + LOG.debug("Invalid 'Lock-Token' header field in response for " + getRequestUri() + ": " + Arrays.asList(ltHeader)); return null; } else { return v.substring(1, v.length() - 1); @@ -99,8 +94,8 @@ public String getLockToken(HttpResponse response) { } @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); boolean lockTokenHeaderOk = isRefresh || null != getLockToken(response); return lockTokenHeaderOk && (statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_CREATED); } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMerge.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMerge.java index b8db71dcff2..58c31048dd8 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMerge.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMerge.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.version.MergeInfo; @@ -33,7 +33,7 @@ public class HttpMerge extends BaseDavRequest { public HttpMerge(URI uri, MergeInfo mergeInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_MERGE, uri); super.setEntity(XmlEntity.create(mergeInfo)); } @@ -42,13 +42,8 @@ public HttpMerge(String uri, MergeInfo mergeInfo) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_MERGE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); // TODO: is this correct? return statusCode == DavServletResponse.SC_MULTI_STATUS; } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkcol.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkcol.java index 5ecf6a237c1..5eb92587fa4 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkcol.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkcol.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -31,7 +31,7 @@ public class HttpMkcol extends BaseDavRequest { public HttpMkcol(URI uri) { - super(uri); + super(DavMethods.METHOD_MKCOL, uri); } public HttpMkcol(String uri) { @@ -39,13 +39,8 @@ public HttpMkcol(String uri) { } @Override - public String getMethod() { - return DavMethods.METHOD_MKCOL; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_CREATED; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkworkspace.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkworkspace.java index 1c14fe52ac3..1f593822eaa 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkworkspace.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMkworkspace.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -31,7 +31,7 @@ public class HttpMkworkspace extends BaseDavRequest { public HttpMkworkspace(URI uri) { - super(uri); + super(DavMethods.METHOD_MKWORKSPACE, uri); } public HttpMkworkspace(String uri) { @@ -39,13 +39,8 @@ public HttpMkworkspace(String uri) { } @Override - public String getMethod() { - return DavMethods.METHOD_MKWORKSPACE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_CREATED; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMove.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMove.java index 2411e0891a8..86f41adb2b0 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMove.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpMove.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -32,7 +32,7 @@ public class HttpMove extends BaseDavRequest { public HttpMove(URI uri, URI dest, boolean overwrite) { - super(uri); + super(DavMethods.METHOD_MOVE, uri); super.setHeader(DavConstants.HEADER_DESTINATION, dest.toASCIIString()); if (!overwrite) { super.setHeader(DavConstants.HEADER_OVERWRITE, "F"); @@ -44,13 +44,8 @@ public HttpMove(String uri, String dest, boolean overwrite) { } @Override - public String getMethod() { - return DavMethods.METHOD_MOVE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_CREATED || statusCode == DavServletResponse.SC_NO_CONTENT; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOptions.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOptions.java index 4986ee2b9a7..449e1a4a96c 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOptions.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOptions.java @@ -21,8 +21,8 @@ import java.util.HashSet; import java.util.Set; -import org.apache.http.Header; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.header.FieldValueParser; import org.apache.jackrabbit.webdav.search.SearchConstants; @@ -33,7 +33,7 @@ * @see RFC 7231, Section 4.3.7 * @since 2.13.6 */ -public class HttpOptions extends org.apache.http.client.methods.HttpOptions { +public class HttpOptions extends org.apache.hc.client5.http.classic.methods.HttpOptions { public HttpOptions(URI uri) { super(uri); @@ -46,7 +46,7 @@ public HttpOptions(String uri) { /** * Compute the set of compliance classes returned in the "dav" header field */ - public Set getDavComplianceClasses(HttpResponse response) { + public Set getDavComplianceClasses(ClassicHttpResponse response) { Header[] headers = response.getHeaders(DavConstants.HEADER_DAV); return parseTokenOrCodedUrlheaderField(headers, false); } @@ -54,7 +54,7 @@ public Set getDavComplianceClasses(HttpResponse response) { /** * Compute set of search grammars returned in the "dasl" header field */ - public Set getSearchGrammars(HttpResponse response) { + public Set getSearchGrammars(ClassicHttpResponse response) { Header[] headers = response.getHeaders(SearchConstants.HEADER_DASL); return parseTokenOrCodedUrlheaderField(headers, true); } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOrderpatch.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOrderpatch.java index 0e6f0fbf06e..401f7682366 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOrderpatch.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpOrderpatch.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.ordering.OrderPatch; @@ -33,7 +33,7 @@ public class HttpOrderpatch extends BaseDavRequest { public HttpOrderpatch(URI uri, OrderPatch info) throws IOException { - super(uri); + super(DavMethods.METHOD_ORDERPATCH, uri); super.setEntity(XmlEntity.create(info)); } @@ -42,12 +42,7 @@ public HttpOrderpatch(String uri, OrderPatch info) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_ORDERPATCH; - } - - @Override - public boolean succeeded(HttpResponse response) { - return response.getStatusLine().getStatusCode() == DavServletResponse.SC_OK; + public boolean succeeded(ClassicHttpResponse response) { + return response.getCode() == DavServletResponse.SC_OK; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPoll.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPoll.java index af7d620846b..f8307fa1cce 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPoll.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPoll.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.header.PollTimeoutHeader; @@ -33,7 +33,7 @@ public class HttpPoll extends BaseDavRequest { public HttpPoll(URI uri, String subscriptionId, long timeout) { - super(uri); + super(DavMethods.METHOD_POLL, uri); super.setHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, subscriptionId); if (timeout > 0) { PollTimeoutHeader th = new PollTimeoutHeader(timeout); @@ -46,13 +46,8 @@ public HttpPoll(String uri, String subscriptionId, long timeout) { } @Override - public String getMethod() { - return DavMethods.METHOD_POLL; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPropfind.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPropfind.java index e548db0697a..a969dbe2245 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPropfind.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpPropfind.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -36,7 +36,7 @@ public class HttpPropfind extends BaseDavRequest { public HttpPropfind(URI uri, int propfindType, DavPropertyNameSet names, int depth) throws IOException { - super(uri); + super(DavMethods.METHOD_PROPFIND, uri); DepthHeader dh = new DepthHeader(depth); super.setHeader(dh.getHeaderName(), dh.getHeaderValue()); @@ -66,12 +66,7 @@ public HttpPropfind(String uri, DavPropertyNameSet names, int depth) throws IOEx } @Override - public String getMethod() { - return DavMethods.METHOD_PROPFIND; - } - - @Override - public boolean succeeded(HttpResponse response) { - return response.getStatusLine().getStatusCode() == DavServletResponse.SC_MULTI_STATUS; + public boolean succeeded(ClassicHttpResponse response) { + return response.getCode() == DavServletResponse.SC_MULTI_STATUS; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpProppatch.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpProppatch.java index 2cd35f8f0f6..3fbfb9e5fb1 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpProppatch.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpProppatch.java @@ -20,7 +20,7 @@ import java.net.URI; import java.util.List; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; @@ -39,7 +39,7 @@ public class HttpProppatch extends BaseDavRequest { // private DavPropertyNameSet propertyNames; public HttpProppatch(URI uri, ProppatchInfo info) throws IOException { - super(uri); + super(DavMethods.METHOD_PROPPATCH, uri); super.setEntity(XmlEntity.create(info)); // this.propertyNames = info.getAffectedProperties(); } @@ -61,13 +61,8 @@ public HttpProppatch(String uri, DavPropertySet setProperties, DavPropertyNameSe } @Override - public String getMethod() { - return DavMethods.METHOD_PROPPATCH; - } - - @Override - public boolean succeeded(HttpResponse response) { - return response.getStatusLine().getStatusCode() == DavServletResponse.SC_MULTI_STATUS; + public boolean succeeded(ClassicHttpResponse response) { + return response.getCode() == DavServletResponse.SC_MULTI_STATUS; // disabled code that fails for current PROPPATCH behavior of Jackrabbit // MultiStatusResponse responses[] = super.getResponseBodyAsMultiStatus(response).getResponses(); diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpRebind.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpRebind.java index e72dea89008..292b063d255 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpRebind.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpRebind.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.bind.RebindInfo; @@ -33,7 +33,7 @@ public class HttpRebind extends BaseDavRequest { public HttpRebind(URI uri, RebindInfo info) throws IOException { - super(uri); + super(DavMethods.METHOD_REBIND, uri); super.setEntity(XmlEntity.create(info)); } @@ -42,13 +42,8 @@ public HttpRebind(String uri, RebindInfo info) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_REBIND; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_CREATED; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpReport.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpReport.java index efb20b6caf7..fd3299f4500 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpReport.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpReport.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -37,7 +37,7 @@ public class HttpReport extends BaseDavRequest { private final boolean isDeep; public HttpReport(URI uri, ReportInfo reportInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_REPORT, uri); DepthHeader dh = new DepthHeader(reportInfo.getDepth()); isDeep = reportInfo.getDepth() > DavConstants.DEPTH_0; super.setHeader(dh.getHeaderName(), dh.getHeaderValue()); @@ -49,13 +49,8 @@ public HttpReport(String uri, ReportInfo reportInfo) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_REPORT; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); if (isDeep) { return statusCode == DavServletResponse.SC_MULTI_STATUS; } else { diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSearch.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSearch.java index f230f2dfbf1..6eeb07c2add 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSearch.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSearch.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.search.SearchInfo; @@ -33,7 +33,7 @@ public class HttpSearch extends BaseDavRequest { public HttpSearch(URI uri, SearchInfo searchInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_SEARCH, uri); super.setEntity(XmlEntity.create(searchInfo)); } @@ -42,13 +42,8 @@ public HttpSearch(String uri, SearchInfo searchInfo) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_SEARCH; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_MULTI_STATUS; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSubscribe.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSubscribe.java index 6bdbb7adcdc..53651caaa41 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSubscribe.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpSubscribe.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -38,7 +38,7 @@ public class HttpSubscribe extends BaseDavRequest { public HttpSubscribe(URI uri, SubscriptionInfo info, String subscriptionId) throws IOException { - super(uri); + super(DavMethods.METHOD_SUBSCRIBE, uri); if (info == null) { throw new IllegalArgumentException("SubscriptionInfo must not be null."); } @@ -64,8 +64,8 @@ public HttpSubscribe(String uri, SubscriptionInfo info, String subscriptionId) t this(URI.create(uri), info, subscriptionId); } - public String getSubscriptionId(HttpResponse response) { - org.apache.http.Header sbHeader = response.getFirstHeader(ObservationConstants.HEADER_SUBSCRIPTIONID); + public String getSubscriptionId(ClassicHttpResponse response) { + org.apache.hc.core5.http.Header sbHeader = response.getFirstHeader(ObservationConstants.HEADER_SUBSCRIPTIONID); if (sbHeader != null) { CodedUrlHeader cuh = new CodedUrlHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, sbHeader.getValue()); return cuh.getCodedUrl(); @@ -76,13 +76,8 @@ public String getSubscriptionId(HttpResponse response) { } @Override - public String getMethod() { - return DavMethods.METHOD_SUBSCRIBE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnbind.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnbind.java index 10e7f4a5b50..cd8be95be88 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnbind.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnbind.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.bind.UnbindInfo; @@ -33,7 +33,7 @@ public class HttpUnbind extends BaseDavRequest { public HttpUnbind(URI uri, UnbindInfo info) throws IOException { - super(uri); + super(DavMethods.METHOD_UNBIND, uri); super.setEntity(XmlEntity.create(info)); } @@ -42,13 +42,8 @@ public HttpUnbind(String uri, UnbindInfo info) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_UNBIND; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_NO_CONTENT; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnlock.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnlock.java index aee34736539..e7e9a3fa0b0 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnlock.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnlock.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavConstants; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -33,7 +33,7 @@ public class HttpUnlock extends BaseDavRequest { public HttpUnlock(URI uri, String lockToken) { - super(uri); + super(DavMethods.METHOD_UNLOCK, uri); CodedUrlHeader lth = new CodedUrlHeader(DavConstants.HEADER_LOCK_TOKEN, lockToken); super.setHeader(lth.getHeaderName(), lth.getHeaderValue()); } @@ -43,13 +43,8 @@ public HttpUnlock(String uri, String lockToken) { } @Override - public String getMethod() { - return DavMethods.METHOD_UNLOCK; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK || statusCode == DavServletResponse.SC_NO_CONTENT; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnsubscribe.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnsubscribe.java index 522f17fce6e..a2a7f36c918 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnsubscribe.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUnsubscribe.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.observation.ObservationConstants; @@ -32,7 +32,7 @@ public class HttpUnsubscribe extends BaseDavRequest { public HttpUnsubscribe(URI uri, String subscriptionId) { - super(uri); + super(DavMethods.METHOD_UNSUBSCRIBE, uri); super.setHeader(ObservationConstants.HEADER_SUBSCRIPTIONID, subscriptionId); } @@ -41,13 +41,8 @@ public HttpUnsubscribe(String uri, String subscriptionId) { } @Override - public String getMethod() { - return DavMethods.METHOD_UNSUBSCRIBE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_NO_CONTENT; } } \ No newline at end of file diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUpdate.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUpdate.java index 019a24fff62..6c0845b012a 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUpdate.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpUpdate.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; import org.apache.jackrabbit.webdav.version.UpdateInfo; @@ -33,7 +33,7 @@ public class HttpUpdate extends BaseDavRequest { public HttpUpdate(URI uri, UpdateInfo updateInfo) throws IOException { - super(uri); + super(DavMethods.METHOD_UPDATE, uri); super.setEntity(XmlEntity.create(updateInfo)); } @@ -42,13 +42,8 @@ public HttpUpdate(String uri, UpdateInfo updateInfo) throws IOException { } @Override - public String getMethod() { - return DavMethods.METHOD_UPDATE; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_MULTI_STATUS; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpVersionControl.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpVersionControl.java index e23a601bee7..cfd8da9e425 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpVersionControl.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/HttpVersionControl.java @@ -18,7 +18,7 @@ import java.net.URI; -import org.apache.http.HttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.jackrabbit.webdav.DavMethods; import org.apache.jackrabbit.webdav.DavServletResponse; @@ -31,7 +31,7 @@ public class HttpVersionControl extends BaseDavRequest { public HttpVersionControl(URI uri) { - super(uri); + super(DavMethods.METHOD_VERSION_CONTROL, uri); } public HttpVersionControl(String uri) { @@ -39,13 +39,8 @@ public HttpVersionControl(String uri) { } @Override - public String getMethod() { - return DavMethods.METHOD_VERSION_CONTROL; - } - - @Override - public boolean succeeded(HttpResponse response) { - int statusCode = response.getStatusLine().getStatusCode(); + public boolean succeeded(ClassicHttpResponse response) { + int statusCode = response.getCode(); return statusCode == DavServletResponse.SC_OK; } } diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/XmlEntity.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/XmlEntity.java index 859bbe40442..b92bae083f9 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/XmlEntity.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/XmlEntity.java @@ -22,9 +22,9 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; -import org.apache.http.HttpEntity; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.ContentType; import org.apache.jackrabbit.webdav.xml.DomUtil; import org.apache.jackrabbit.webdav.xml.XmlSerializable; import org.slf4j.Logger; diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/package-info.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/package-info.java index b667d39c615..6d82fe84abf 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/package-info.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/package-info.java @@ -19,13 +19,17 @@ * Provides classes for use with the Apache HttpClient, supporting WebDAV * request methods. *

- * This version also contains classes for use with the obsolete "Commons - * HttpClient"; they have been marked "deprecated" and will be removed in the - * next major release. - * + * As of package version 3.0.0 these classes are built on Apache HttpClient 5. This is a + * breaking change: {@link org.apache.jackrabbit.webdav.client.methods.BaseDavRequest} + * now extends + * {@code org.apache.hc.client5.http.classic.methods.HttpUriRequestBase} and takes + * the request method as its first constructor argument, and the response + * accessors take {@code org.apache.hc.core5.http.ClassicHttpResponse} in place of + * the HttpClient 4 {@code HttpResponse}. + * * @see JCR-2406 * @see https://hc.apache.org/httpcomponents-client-4.5.x/ + * "https://hc.apache.org/httpcomponents-client-5.6.x/">https://hc.apache.org/httpcomponents-client-5.6.x/ */ -@org.osgi.annotation.versioning.Version("2.0.0") +@org.osgi.annotation.versioning.Version("3.0.0") package org.apache.jackrabbit.webdav.client.methods; diff --git a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/util/LinkHeaderFieldParser.java b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/util/LinkHeaderFieldParser.java index bc183959219..10bc16b1e97 100644 --- a/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/util/LinkHeaderFieldParser.java +++ b/jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/util/LinkHeaderFieldParser.java @@ -26,8 +26,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicHeaderValueParser; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.message.BasicHeaderValueParser; +import org.apache.hc.core5.http.message.ParserCursor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -161,7 +162,9 @@ public LinkRelation(String field) throws Exception { target = m.group(1); // pass the remainder to the generic parameter parser - NameValuePair[] params = BasicHeaderValueParser.parseParameters(m.group(2), null); + String paramStr = m.group(2); + NameValuePair[] params = BasicHeaderValueParser.INSTANCE.parseParameters( + paramStr, new ParserCursor(0, paramStr.length())); if (params.length == 0) { parameters = Collections.emptyMap();