diff options
author | jonmv <venstad@gmail.com> | 2022-12-07 13:47:00 +0100 |
---|---|---|
committer | jonmv <venstad@gmail.com> | 2022-12-07 13:47:00 +0100 |
commit | 0e810a41b6c335b418b1e8dbdfee98b883d32cef (patch) | |
tree | 27a7fdc876712e9be41679fae83a54c5b39f3786 /vespa-feed-client | |
parent | c505db644cbdc735d487de51ab23b92f7f90b3d2 (diff) |
Parse error responses
Diffstat (limited to 'vespa-feed-client')
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) { |