SLING-8727 - NPE in SlingClient constructor when url doesn't have a protocol
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClient.java b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
index 7c4873d..b96e979 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClient.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClient.java
@@ -672,7 +672,7 @@
             return httpClientBuilder.build();
         }
 
-        protected SlingClientConfig buildSlingClientConfig() {
+        protected SlingClientConfig buildSlingClientConfig() throws ClientException {
             return configBuilder.build();
         }
 
diff --git a/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java b/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
index 43825f6..825dfe8 100644
--- a/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
+++ b/src/main/java/org/apache/sling/testing/clients/SlingClientConfig.java
@@ -207,12 +207,20 @@
             return this;
         }
 
-        public SlingClientConfig build() {
+        public SlingClientConfig build() throws ClientException {
+            if (!this.url.isAbsolute()) {
+                throw new ClientException("Url must be absolute: " + url);
+            }
+
+            HttpHost targetHost = URIUtils.extractHost(this.url);
+            if (targetHost == null) {
+                throw new ClientException("Failed to extract hostname from url " + url);
+            }
+
             // Create default CredentialsProvider if not set
             if (credsProvider == null) {
                 credsProvider = new BasicCredentialsProvider();
                 if (StringUtils.isNotEmpty(this.user)) {
-                    HttpHost targetHost = URIUtils.extractHost(this.url);
                     credsProvider.setCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()),
                             new UsernamePasswordCredentials(this.user, this.password));
                 }
@@ -222,7 +230,7 @@
             if (authCache == null) {
                 BasicScheme basicScheme = new BasicScheme();
                 authCache = new BasicAuthCache();
-                authCache.put(URIUtils.extractHost(url), basicScheme);
+                authCache.put(targetHost, basicScheme);
             }
 
             // if preemptive auth is disabled, force auth cache to be null
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientConfigTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientConfigTest.java
new file mode 100644
index 0000000..eb810a6
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/clients/SlingClientConfigTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.sling.testing.clients;
+
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+
+public class SlingClientConfigTest {
+
+    @Test(expected = ClientException.class)
+    public void testUrlNoProtocolUnauthenticated() throws URISyntaxException, ClientException {
+        // Should fail because URL doesn't start with http or https
+        SlingClientConfig.Builder
+                .create()
+                .setUrl("example.com")
+                .build();
+    }
+
+    @Test(expected = ClientException.class)
+    public void testUrlNoProtocolAuthenticated() throws URISyntaxException, ClientException {
+        // Should fail because URL doesn't start with http or https
+        SlingClientConfig.Builder
+                .create()
+                .setUser("username")
+                .setPassword("pass")
+                .setUrl("example.com")
+                .build();
+    }
+}
diff --git a/src/test/java/org/apache/sling/testing/clients/SlingClientConstructorTest.java b/src/test/java/org/apache/sling/testing/clients/SlingClientConstructorTest.java
new file mode 100644
index 0000000..ed4bf08
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/clients/SlingClientConstructorTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.sling.testing.clients;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.codehaus.jackson.JsonNode;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URI;
+
+import static org.junit.Assert.assertEquals;
+
+public class SlingClientConstructorTest {
+
+    @Test
+    public void testConstructorUrlHttp() throws Exception {
+        URI url = URI.create("http://example.com/");
+        SlingClient c = new SlingClient(url, "user", "pass");
+        assertEquals(url, c.getUrl());
+    }
+
+    @Test
+    public void testConstructorUrlHttps() throws Exception {
+        URI url = URI.create("https://example.com/");
+        SlingClient c = new SlingClient(url, "user", "pass");
+        assertEquals(url, c.getUrl());
+    }
+
+    @Test
+    public void testConstructorUrlBasePath() throws Exception {
+        URI url = URI.create("https://example.com/mypath/");
+        SlingClient c = new SlingClient(url, "user", "pass");
+        assertEquals(url, c.getUrl());
+    }
+
+    @Test
+    public void testConstructorAnonymous() throws Exception {
+        URI url = URI.create("https://example.com/");
+        SlingClient c = new SlingClient(url, null, null);
+        assertEquals(url, c.getUrl());
+    }
+
+    @Test(expected = ClientException.class)
+    public void testConstructorUrlNoProtocol() throws Exception {
+        URI url = URI.create("example.com/");
+        SlingClient c = new SlingClient(url, "user", "pass");
+        assertEquals(url, c.getUrl());
+    }
+}