summaryrefslogtreecommitdiffstats
path: root/vespa-feed-client
diff options
context:
space:
mode:
authorjonmv <venstad@gmail.com>2022-12-07 13:47:00 +0100
committerjonmv <venstad@gmail.com>2022-12-07 13:47:00 +0100
commit0e810a41b6c335b418b1e8dbdfee98b883d32cef (patch)
tree27a7fdc876712e9be41679fae83a54c5b39f3786 /vespa-feed-client
parentc505db644cbdc735d487de51ab23b92f7f90b3d2 (diff)
Parse error responses
Diffstat (limited to 'vespa-feed-client')
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/ApacheCluster.java7
-rw-r--r--vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java57
-rw-r--r--vespa-feed-client/src/test/java/ai/vespa/feed/client/impl/HttpFeedClientTest.java9
3 files changed, 51 insertions, 22 deletions
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/ApacheCluster.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/ApacheCluster.java
index 6714f2bd590..3ffbaf136f2 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/ApacheCluster.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/ApacheCluster.java
@@ -188,9 +188,14 @@ class ApacheCluster implements Cluster {
}
@Override
+ public String contentType() {
+ return wrapped.getContentType().getMimeType();
+ }
+
+ @Override
public String toString() {
return "HTTP response with code " + code() +
- (body() != null ? " and body '" + new String(body(), UTF_8) + "'" : "");
+ (body() != null ? " and body '" + wrapped.getBodyText() + "'" : "");
}
}
diff --git a/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java b/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java
index 353df66f0fd..628cccf0545 100644
--- a/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java
+++ b/vespa-feed-client/src/main/java/ai/vespa/feed/client/impl/HttpFeedClient.java
@@ -18,6 +18,7 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@@ -132,11 +133,16 @@ class HttpFeedClient implements FeedClient {
cluster.dispatch(request, future);
HttpResponse response = future.get(20, TimeUnit.SECONDS);
if (response.code() != 200) {
- throw new FeedException("non-200 response: " + response);
+ String message = switch (response.contentType()) {
+ case "application/json" -> parseMessage(request.body());
+ case "text/plain" -> new String(request.body(), UTF_8);
+ default -> response.toString();
+ };
+ throw new FeedException("server responded non-OK to handshake: " + message);
}
}
catch (IOException e) {
- throw new FeedException("failed handshake with server: " + e, e);
+ throw new FeedException("failed initiating handshake with server: " + e, e);
}
catch (ExecutionException e) {
throw new FeedException("failed handshake with server: " + e.getCause(), e.getCause());
@@ -150,6 +156,15 @@ class HttpFeedClient implements FeedClient {
}
}
+ private static String parseMessage(byte[] json) {
+ try {
+ return parse(null, json).message;
+ }
+ catch (Exception e) {
+ return new String(json, UTF_8);
+ }
+ }
+
private enum Outcome { success, conditionNotMet, vespaFailure, transportFailure };
static Result.Type toResultType(Outcome outcome) {
@@ -160,22 +175,17 @@ class HttpFeedClient implements FeedClient {
};
}
- static Result toResult(HttpRequest request, HttpResponse response, DocumentId documentId) {
- Outcome outcome = switch (response.code()) {
- case 200 -> Outcome.success;
- case 412 -> Outcome.conditionNotMet;
- case 502, 504, 507 -> Outcome.vespaFailure;
- default -> Outcome.transportFailure;
- };
+ record MessageAndTrace(String message, String trace) { }
+ static MessageAndTrace parse(DocumentId documentId, byte[] json) {
String message = null;
String trace = null;
- try (JsonParser parser = factory.createParser(response.body())) {
+ try (JsonParser parser = factory.createParser(json)) {
if (parser.nextToken() != JsonToken.START_OBJECT)
throw new ResultParseException(
documentId,
"Expected '" + JsonToken.START_OBJECT + "', but found '" + parser.currentToken() + "' in: " +
- new String(response.body(), UTF_8));
+ new String(json, UTF_8));
String name;
while ((name = parser.nextFieldName()) != null) {
@@ -185,7 +195,7 @@ class HttpFeedClient implements FeedClient {
if (parser.nextToken() != JsonToken.START_ARRAY)
throw new ResultParseException(documentId,
"Expected 'trace' to be an array, but got '" + parser.currentToken() + "' in: " +
- new String(response.body(), UTF_8));
+ new String(json, UTF_8));
int start = (int) parser.getTokenLocation().getByteOffset();
int depth = 1;
while (depth > 0) switch (parser.nextToken()) {
@@ -193,7 +203,7 @@ class HttpFeedClient implements FeedClient {
case END_ARRAY -> --depth;
}
int end = (int) parser.getTokenLocation().getByteOffset() + 1;
- trace = new String(response.body(), start, end - start, UTF_8);
+ trace = new String(json, start, end - start, UTF_8);
}
default -> parser.nextToken();
}
@@ -203,22 +213,35 @@ class HttpFeedClient implements FeedClient {
throw new ResultParseException(
documentId,
"Expected '" + JsonToken.END_OBJECT + "', but found '" + parser.currentToken() + "' in: "
- + new String(response.body(), UTF_8));
+ + new String(json, UTF_8));
}
catch (IOException e) {
throw new ResultParseException(documentId, e);
}
+ return new MessageAndTrace(message, trace);
+ }
+
+ static Result toResult(HttpRequest request, HttpResponse response, DocumentId documentId) {
+ Outcome outcome = switch (response.code()) {
+ case 200 -> Outcome.success;
+ case 412 -> Outcome.conditionNotMet;
+ case 502, 504, 507 -> Outcome.vespaFailure;
+ default -> Outcome.transportFailure;
+ };
+
+ MessageAndTrace mat = parse(documentId, response.body());
+
if (outcome == Outcome.transportFailure) // Not a Vespa response, but a failure in the HTTP layer.
throw new FeedException(
documentId,
"Status " + response.code() + " executing '" + request + "': "
- + (message == null ? new String(response.body(), UTF_8) : message));
+ + (mat.message == null ? new String(response.body(), UTF_8) : mat.message));
if (outcome == Outcome.vespaFailure)
- throw new ResultException(documentId, message, trace);
+ throw new ResultException(documentId, mat.message, mat.trace);
- return new ResultImpl(toResultType(outcome), documentId, message, trace);
+ return new ResultImpl(toResultType(outcome), documentId, mat.message, mat.trace);
}
static String getPath(DocumentId documentId) {
diff --git a/vespa-feed-client/src/test/java/ai/vespa/feed/client/impl/HttpFeedClientTest.java b/vespa-feed-client/src/test/java/ai/vespa/feed/client/impl/HttpFeedClientTest.java
index e9c996ae560..f2298a322fe 100644
--- a/vespa-feed-client/src/test/java/ai/vespa/feed/client/impl/HttpFeedClientTest.java
+++ b/vespa-feed-client/src/test/java/ai/vespa/feed/client/impl/HttpFeedClientTest.java
@@ -56,10 +56,11 @@ class HttpFeedClientTest {
assertEquals("json", new String(request.body(), UTF_8));
HttpResponse response = HttpResponse.of(200,
- ("{\n" +
- " \"pathId\": \"/document/v1/ns/type/docid/0\",\n" +
- " \"id\": \"id:ns:type::0\"\n" +
- "}").getBytes(UTF_8));
+ ("""
+ {
+ "pathId": "/document/v1/ns/type/docid/0",
+ "id": "id:ns:type::0"
+ }""").getBytes(UTF_8));
return CompletableFuture.completedFuture(response);
}
catch (Throwable thrown) {