fix body diagnostics for http driver

This commit is contained in:
Jonathan Shook 2021-04-26 17:37:21 -05:00
parent a6e374a5f1
commit 6281c8c91d
2 changed files with 77 additions and 53 deletions

View File

@ -113,6 +113,7 @@ public class HttpAction implements SyncAction {
httpActivity.resultSuccessTimer.update(nanos, TimeUnit.NANOSECONDS);
}
if (httpActivity.isDiagnosticMode()) {
httpActivity.console.summarizeRequest("request", error, httpOp.request, System.out, cycle, nanos);
if (response != null) {
httpActivity.console.summarizeResponseChain(error, response, System.out, cycle, nanos);
} else {

View File

@ -40,7 +40,6 @@ public class HttpConsoleFormats {
private final static long _DATA100 = 1L << 7;
private final static long _DATA1000 = 1L << 8;
private final static Set<String> PRINTABLE_TYPES = Set.of("text","html","json","xhtml");
enum Diag {
headers(_HEADERS),
@ -180,7 +179,7 @@ public class HttpConsoleFormats {
out.println(COMPONENT_CUE + request.method() + " " + request.uri() + " " + request.version().orElse(HttpClient.Version.HTTP_2));
summariseHeaders(request.headers(), out);
out.println(DETAIL_CUE + "body length:" + request.bodyPublisher().get().contentLength());
out.println(DETAIL_CUE + "body length: " + request.bodyPublisher().get().contentLength());
summarizeRequestContent(request, out);
}
@ -198,12 +197,24 @@ public class HttpConsoleFormats {
}
summariseHeaders(response.headers(), out);
summarizeContent(response, out);
summarizedResponseContent(response, out);
}
private final static Set<String> PRINTABLE = Set.of("text", "html", "json", "xhtml");
private void summarizeContent(HttpResponse<String> response, PrintStream out) {
private boolean isPrintableContentType(String contentType) {
if (contentType == null) {
return false;
}
if (contentType.toLowerCase().startsWith("text")) {
return true;
}
return PRINTABLE.contains(contentType.split("/")[0].toLowerCase());
}
private void summarizedResponseContent(HttpResponse<String> response, PrintStream out) {
if (Diag.anyIncluded(mask, Diag.data, Diag.data10, Diag.data100, Diag.data1000)) {
String contentLenStr = response.headers().map().getOrDefault("content-length", List.of("0")).get(0);
@ -214,35 +225,36 @@ public class HttpConsoleFormats {
System.out.println(PAYLOAD_CUE);
List<String> contentTypeList = response.headers().map().getOrDefault("content-type", List.of("text/html"));
String printable = "<non-printable>";
String toprint = "<non-printable>";
if (contentTypeList.size() > 1) {
printable = "non-printable/multiple content types provided";
toprint = "non-printable/multiple content types provided";
} else {
String contentType = contentTypeList.get(0).toLowerCase();
if (!PRINTABLE_TYPES.contains(contentType.toLowerCase())) {
printable = "non-printable content type:" + contentTypeList.get(0);
} else {
printable = response.body();
if (printable == null) {
printable = "content-length was " + contentLength + ", but body was null";
if (isPrintableContentType(contentType)) {
toprint = response.body();
if (toprint == null) {
toprint = "content-length was " + contentLength + ", but body was null";
}
if (Diag.data1000.includedIn(mask)) {
if (printable.length() > 1000) {
printable = printable.substring(0, 1000) + "\n--truncated at 1000 characters--\n";
if (toprint.length() > 1000) {
toprint = toprint.substring(0, 1000) + "\n--truncated at 1000 characters--\n";
}
} else if (Diag.data100.includedIn(mask)) {
if (printable.length() > 100) {
printable = printable.substring(0, 100) + "\n--truncated at 100 characters--\n";
if (toprint.length() > 100) {
toprint = toprint.substring(0, 100) + "\n--truncated at 100 characters--\n";
}
} else if (Diag.data10.includedIn(mask)) {
if (printable.length() > 10) {
printable = printable.substring(0, 10) + "\n--truncated at 10 characters--\n";
if (toprint.length() > 10) {
toprint = toprint.substring(0, 10) + "\n--truncated at 10 characters--\n";
}
}
} else {
toprint = "non-printable content type:" + contentTypeList.get(0);
}
}
System.out.println(printable);
System.out.println(toprint);
}
}
@ -251,74 +263,85 @@ public class HttpConsoleFormats {
if (request.bodyPublisher().isEmpty()) {
sb.append(PAYLOAD_CUE).append("\n--no body publisher is defined for this request--\n");
} else {
String charset = "UTF-8";
String contentType = "text/html";
List<String> contentTypeHeaders = request.headers().allValues("content-type");
if (contentTypeHeaders.size()==0) {
sb.append("\n--WARNING: content-type header assumed as 'text/html; charset=UTF-8--\n");
} else if (contentTypeHeaders.size()>1) {
sb.append("--non-printable/multiple content types provided--");
} else {
String cth = contentTypeHeaders.get(0);
String[] fields = cth.split("; *",2);
switch(fields.length) {
case 2: charset = fields[1].split("=")[1].trim();
case 1: contentType = fields[0].trim();
}
HttpRequest.BodyPublisher publisher = request.bodyPublisher().get();
long contentLength = publisher.contentLength();
if (contentLength > 0) {
String charset = "UTF-8";
String contentType = "text/html";
List<String> contentTypeHeaders = request.headers().allValues("content-type");
if (!PRINTABLE_TYPES.contains(contentType.toLowerCase())) {
sb.append("--non-printable content type:").append(contentType).append("--");
if (contentTypeHeaders.size() == 0) {
sb.append(DETAIL_CUE).append("BODY (WARNING: content-type header MISSING, assumed as 'text/html; charset=UTF-8)\n");
} else if (contentTypeHeaders.size() > 1) {
sb.append(DETAIL_CUE).append("BODY is non-printable or multiple content types provided\n");
} else {
HttpRequest.BodyPublisher publisher = request.bodyPublisher().get();
long contentLength = publisher.contentLength();
if (contentLength > 0) {
List<ByteBuffer> byteBuffers = new BodyReceiver()
.subscribe(publisher)
.awaitBuffers();
for (ByteBuffer buf : byteBuffers) {
sb.append(new String(buf.array(), Charset.forName(charset)));
}
} else {
sb.append("\n<no-content>\n");
String cth = contentTypeHeaders.get(0);
String[] fields = cth.split("; *", 2);
switch (fields.length) {
case 2:
charset = fields[1].split("=")[1].trim();
case 1:
contentType = fields[0].trim();
}
sb.append(DETAIL_CUE).append("BODY (content type detected as ").append(contentType).append(")\n");
}
if (isPrintableContentType(contentType)) {
List<ByteBuffer> byteBuffers = new BodyReceiver()
.subscribe(publisher)
.awaitBuffers();
for (ByteBuffer buf : byteBuffers) {
sb.append(new String(buf.array(), Charset.forName(charset)));
}
} else {
sb.append("--non-printable content type:").append(contentType).append("--");
}
} else {
sb.append("\n<no-content>\n");
}
}
out.println(sb);
}
private final static class BodyReceiver implements Flow.Subscriber<ByteBuffer> {
public List<ByteBuffer> buffers = new LinkedList<>();
public List<ByteBuffer> buffers = null;
public List<ByteBuffer> completed = null;
public BodyReceiver subscribe(Flow.Publisher<ByteBuffer> flowpub) {
public synchronized BodyReceiver subscribe(Flow.Publisher<ByteBuffer> flowpub) {
flowpub.subscribe(this);
return this;
}
@Override
public void onSubscribe(Flow.Subscription subscription) {
public synchronized void onSubscribe(Flow.Subscription subscription) {
buffers = new ArrayList<>();
subscription.request(Long.MAX_VALUE);
}
@Override
public void onNext(ByteBuffer item) {
public synchronized void onNext(ByteBuffer item) {
buffers.add(item);
}
@Override
public void onError(Throwable throwable) {
public synchronized void onError(Throwable throwable) {
throw new RuntimeException(throwable);
}
@Override
public synchronized void onComplete() {
completed = buffers;
notifyAll();
}
public synchronized List<ByteBuffer> awaitBuffers() {
try {
wait();
while (completed==null) {
wait(1000);
}
return buffers;
} catch (InterruptedException e) {
throw new RuntimeException(e);