Skip to content

Commit 4034e0e

Browse files
authored
Merge pull request #1561 from marklogic/feature/447-bad-ssl-error
DEVEXP-447 Better error when HTTP is used instead of HTTPS
2 parents c2d0611 + e8b989a commit 4034e0e

File tree

2 files changed

+62
-21
lines changed

2 files changed

+62
-21
lines changed

Diff for: marklogic-client-api/src/main/java/com/marklogic/client/impl/OkHttpServices.java

+32-20
Original file line numberDiff line numberDiff line change
@@ -165,26 +165,38 @@ public void setMaxDelay(int maxDelay) {
165165
this.maxDelay = maxDelay;
166166
}
167167

168-
private FailedRequest extractErrorFields(Response response) {
169-
if (response == null) return null;
170-
try {
171-
if (response.code() == STATUS_UNAUTHORIZED) {
172-
FailedRequest failure = new FailedRequest();
173-
failure.setMessageString("Unauthorized");
174-
failure.setStatusString("Failed Auth");
175-
return failure;
176-
}
177-
String responseBody = getEntity(response.body(), String.class);
178-
InputStream is = new ByteArrayInputStream(responseBody.getBytes(StandardCharsets.UTF_8));
179-
FailedRequest handler = FailedRequest.getFailedRequest(response.code(), response.header(HEADER_CONTENT_TYPE), is);
180-
if (handler.getMessage() == null) {
181-
handler.setMessageString(responseBody);
182-
}
183-
return handler;
184-
} finally {
185-
closeResponse(response);
186-
}
187-
}
168+
private FailedRequest extractErrorFields(Response response) {
169+
if (response == null) return null;
170+
try {
171+
if (response.code() == STATUS_UNAUTHORIZED) {
172+
FailedRequest failure = new FailedRequest();
173+
failure.setMessageString("Unauthorized");
174+
failure.setStatusString("Failed Auth");
175+
return failure;
176+
}
177+
178+
final String responseBody = getEntity(response.body(), String.class);
179+
// If HTTP is used but HTTPS is required, MarkLogic returns a text/html response that is not suitable to
180+
// return to a user. But it will contain the below error message, which is much nicer to return to the user.
181+
final String sslErrorMessage = "You have attempted to access an HTTPS server using HTTP";
182+
if (response.code() == STATUS_FORBIDDEN && responseBody != null && responseBody.contains(sslErrorMessage)) {
183+
FailedRequest failure = new FailedRequest();
184+
failure.setMessageString(sslErrorMessage + ".");
185+
failure.setStatusString("Forbidden");
186+
failure.setStatusCode(STATUS_FORBIDDEN);
187+
return failure;
188+
}
189+
190+
InputStream is = new ByteArrayInputStream(responseBody.getBytes(StandardCharsets.UTF_8));
191+
FailedRequest handler = FailedRequest.getFailedRequest(response.code(), response.header(HEADER_CONTENT_TYPE), is);
192+
if (handler.getMessage() == null) {
193+
handler.setMessageString(responseBody);
194+
}
195+
return handler;
196+
} finally {
197+
closeResponse(response);
198+
}
199+
}
188200

189201
@Override
190202
public void connect(String host, int port, String basePath, String database, SecurityContext securityContext){

Diff for: marklogic-client-api/src/test/java/com/marklogic/client/test/CheckSSLConnectionTest.java

+30-1
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
import com.marklogic.client.DatabaseClient;
44
import com.marklogic.client.DatabaseClientFactory;
5+
import com.marklogic.client.ForbiddenUserException;
56
import com.marklogic.client.MarkLogicIOException;
67
import com.marklogic.client.test.junit5.RequireSSLExtension;
78
import org.junit.jupiter.api.Test;
89
import org.junit.jupiter.api.extension.ExtendWith;
910

1011
import javax.net.ssl.SSLContext;
12+
import javax.net.ssl.SSLHandshakeException;
1113
import javax.net.ssl.TrustManager;
1214

1315
import static org.junit.jupiter.api.Assertions.assertEquals;
1416
import static org.junit.jupiter.api.Assertions.assertNull;
1517
import static org.junit.jupiter.api.Assertions.assertThrows;
18+
import static org.junit.jupiter.api.Assertions.assertTrue;
1619

1720
@ExtendWith(RequireSSLExtension.class)
1821
class CheckSSLConnectionTest {
@@ -58,8 +61,34 @@ void defaultSslContext() throws Exception {
5861
.withSSLHostnameVerifier(DatabaseClientFactory.SSLHostnameVerifier.ANY)
5962
.build();
6063

61-
assertThrows(MarkLogicIOException.class, () -> client.checkConnection(),
64+
MarkLogicIOException ex = assertThrows(MarkLogicIOException.class, () -> client.checkConnection(),
6265
"The connection should fail because the JVM's default SSL Context does not have a CA certificate that " +
6366
"corresponds to the test-only certificate that the app server is using for this test");
67+
68+
assertTrue(ex.getCause() instanceof SSLHandshakeException, "Unexpected cause: " + ex.getCause());
69+
String message = ex.getCause().getMessage();
70+
assertTrue(message.contains("PKIX path building failed"), "The call should have failed because the JVM's " +
71+
"default SSL context does not have a CA certificate for the app server's certificate; " +
72+
"unexpected error: " + message);
73+
}
74+
75+
@Test
76+
void noSslContext() {
77+
DatabaseClient client = Common.newClientBuilder().build();
78+
79+
DatabaseClient.ConnectionResult result = client.checkConnection();
80+
assertEquals("Forbidden", result.getErrorMessage(), "MarkLogic is expected to return a 403 Forbidden when the " +
81+
"user tries to access an HTTPS app server using HTTP");
82+
assertEquals(403, result.getStatusCode());
83+
84+
ForbiddenUserException ex = assertThrows(ForbiddenUserException.class,
85+
() -> client.newServerEval().javascript("fn.currentDate()").evalAs(String.class));
86+
87+
assertEquals(
88+
"Local message: User is not allowed to apply resource at eval. Server Message: You have attempted to access an HTTPS server using HTTP.",
89+
ex.getMessage(),
90+
"The user should get a clear message on why the connection failed as opposed to the previous error " +
91+
"message of 'Server (not a REST instance?)'."
92+
);
6493
}
6594
}

0 commit comments

Comments
 (0)