diff --git a/agent/agent_common/src/main/java/com/intuit/tank/http/BaseResponse.java b/agent/agent_common/src/main/java/com/intuit/tank/http/BaseResponse.java index ea11fb8af..5e7d5b42a 100644 --- a/agent/agent_common/src/main/java/com/intuit/tank/http/BaseResponse.java +++ b/agent/agent_common/src/main/java/com/intuit/tank/http/BaseResponse.java @@ -29,6 +29,7 @@ public abstract class BaseResponse { protected String response; protected long responseTime = -1; + protected long proxyResponseTime = -1; protected int httpCode = -1; protected String rspMessage = ""; protected HashMap headers = new HashMap(); @@ -52,6 +53,7 @@ public String getLogMsg() { sb.append("RESPONSE HTTP CODE: ").append(this.httpCode).append(NEWLINE) .append("RESPONSE HTTP MSG: ").append(this.rspMessage).append(NEWLINE) .append("RESPONSE TIME: ").append(responseTime).append(NEWLINE) + .append("PROXY RESPONSE TIME: ").append(proxyResponseTime).append(NEWLINE) .append("RESPONSE SIZE: ").append(getResponseSize()).append(NEWLINE); for (Entry mapEntry : headers.entrySet()) { sb.append("RESPONSE HEADER: ") @@ -84,6 +86,7 @@ public String convertToCSV() { sb.append(this.httpCode).append(","); sb.append(this.rspMessage).append(","); sb.append(responseTime).append(","); + sb.append(proxyResponseTime).append(","); sb.append(getResponseSize()).append(","); headers.forEach((key, value) -> sb.append(key).append(" = ").append(value.replace(",", "")).append(",")); cookies.forEach((key, value) -> sb.append(key).append(" = ").append(value).append(",")); @@ -171,6 +174,14 @@ public void setResponseBody(String body) { this.response = body; } + public long getProxyResponseTime() { + return this.proxyResponseTime; + } + + public void setProxyResponseTime(long proxyResponseTime) { + this.proxyResponseTime = proxyResponseTime; + } + /** * * @param byteArray diff --git a/agent/agent_common/src/test/java/com/intuit/tank/http/BaseResponseTest.java b/agent/agent_common/src/test/java/com/intuit/tank/http/BaseResponseTest.java index 9519d414f..3d411d0a2 100644 --- a/agent/agent_common/src/test/java/com/intuit/tank/http/BaseResponseTest.java +++ b/agent/agent_common/src/test/java/com/intuit/tank/http/BaseResponseTest.java @@ -443,6 +443,7 @@ public void testLogResponse_1() throws Exception { BaseResponse fixture = new MockResponse(); fixture.responseTime = 1L; + fixture.proxyResponseTime = 1L; fixture.httpCode = 1; fixture.response = ""; fixture.responseByteArray = new byte[] {}; @@ -451,6 +452,7 @@ public void testLogResponse_1() assertEquals("RESPONSE HTTP CODE: 1\n" + "RESPONSE HTTP MSG: \n" + "RESPONSE TIME: 1\n" + + "PROXY RESPONSE TIME: 1\n" + "RESPONSE SIZE: 0\n" + "RESPONSE BODY: not logged because null is not a content-type.\n", fixture.getLogMsg()); } @@ -466,6 +468,7 @@ public void testLogResponse_2() { assertEquals("RESPONSE HTTP CODE: -1\n" + "RESPONSE HTTP MSG: \n" + "RESPONSE TIME: -1\n" + + "PROXY RESPONSE TIME: -1\n" + "RESPONSE SIZE: 12\n" + "RESPONSE HEADER: content-type = text/html\n" + "RESPONSE COOKIE: testHeadersKey = testHeadersValue\n" + diff --git a/agent/http_client_3/src/main/java/com/intuit/tank/httpclient3/TankHttpClient3.java b/agent/http_client_3/src/main/java/com/intuit/tank/httpclient3/TankHttpClient3.java index a38703aae..6d069ca96 100644 --- a/agent/http_client_3/src/main/java/com/intuit/tank/httpclient3/TankHttpClient3.java +++ b/agent/http_client_3/src/main/java/com/intuit/tank/httpclient3/TankHttpClient3.java @@ -386,6 +386,20 @@ private void processResponse(byte[] bResponse, long waitTime, BaseRequest reques response.setHeader(header.getName(), header.getValue()); } + // Extract proxy response/service time header + String proxyResponseTimeHeader = response.getHttpHeader("x-envoy-upstream-service-time"); + if (proxyResponseTimeHeader != null) { + try { + long proxyResponseTime = Long.parseLong(proxyResponseTimeHeader); + response.setProxyResponseTime(proxyResponseTime); + } catch (NumberFormatException e) { + LOG.warn("could not parse proxy service time header: " + proxyResponseTimeHeader); + response.setProxyResponseTime(-1); + } + } else { + response.setProxyResponseTime(-1); + } + Cookie[] cookies = httpstate.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { diff --git a/agent/http_client_4/src/main/java/com/intuit/tank/httpclient4/TankHttpClient4.java b/agent/http_client_4/src/main/java/com/intuit/tank/httpclient4/TankHttpClient4.java index 5196e9bd9..0d4151786 100644 --- a/agent/http_client_4/src/main/java/com/intuit/tank/httpclient4/TankHttpClient4.java +++ b/agent/http_client_4/src/main/java/com/intuit/tank/httpclient4/TankHttpClient4.java @@ -410,6 +410,20 @@ private void processResponse(byte[] bResponse, long waitTime, BaseRequest reques response.setHeader(header.getName(), header.getValue()); } + // Extract proxy response/service time header + String proxyResponseTimeHeader = response.getHttpHeader("x-envoy-upstream-service-time"); + if (proxyResponseTimeHeader != null) { + try { + long proxyResponseTime = Long.parseLong(proxyResponseTimeHeader); + response.setProxyResponseTime(proxyResponseTime); + } catch (NumberFormatException e) { + LOG.warn("could not parse proxy service time header: " + proxyResponseTimeHeader); + response.setProxyResponseTime(-1); + } + } else { + response.setProxyResponseTime(-1); + } + if (context.getCookieStore().getCookies() != null) { for (Cookie cookie : context.getCookieStore().getCookies()) { response.setCookie(cookie.getName(), cookie.getValue()); diff --git a/agent/http_client_jdk/src/main/java/com/intuit/tank/httpclientjdk/TankHttpClientJDK.java b/agent/http_client_jdk/src/main/java/com/intuit/tank/httpclientjdk/TankHttpClientJDK.java index b802c289e..13cedf28f 100644 --- a/agent/http_client_jdk/src/main/java/com/intuit/tank/httpclientjdk/TankHttpClientJDK.java +++ b/agent/http_client_jdk/src/main/java/com/intuit/tank/httpclientjdk/TankHttpClientJDK.java @@ -343,6 +343,20 @@ private void processResponse(byte[] bResponse, long waitTime, BaseRequest reques } } + // Extract Proxy response/service time header + List proxyResponseTimeHeaders = headers.map().get("x-envoy-upstream-service-time"); + if (proxyResponseTimeHeaders != null && !proxyResponseTimeHeaders.isEmpty()) { + try { + long proxyResponseTime = Long.parseLong(proxyResponseTimeHeaders.get(0)); + response.setProxyResponseTime(proxyResponseTime); + } catch (NumberFormatException e) { + LOG.warn("could not parse proxy service time header: " + proxyResponseTimeHeaders.get(0)); + response.setProxyResponseTime(-1); + } + } else { + response.setProxyResponseTime(-1); + } + List cookies = cookieManager.getCookieStore().getCookies(); if (!cookies.isEmpty()) { for (HttpCookie cookie : cookies) { diff --git a/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/ActionProducer.java b/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/ActionProducer.java index 7c245f71c..37741d1ae 100644 --- a/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/ActionProducer.java +++ b/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/ActionProducer.java @@ -441,7 +441,7 @@ public void setChoiceComboBoxOptions(JComboBox cb) { cb.addItem(new TankClientChoice("Apache HttpClient 3.1", "com.intuit.tank.httpclient3.TankHttpClient3")); cb.addItem(new TankClientChoice("Apache HttpClient 4.5", "com.intuit.tank.httpclient4.TankHttpClient4")); cb.addItem(new TankClientChoice("JDK Http Client", "com.intuit.tank.httpclientjdk.TankHttpClientJDK")); - cb.setSelectedIndex(2); + cb.setSelectedIndex(1); } } diff --git a/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/AgentDebuggerFrame.java b/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/AgentDebuggerFrame.java index 2231fd13d..96f8670fa 100644 --- a/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/AgentDebuggerFrame.java +++ b/tools/agent_debugger/src/main/java/com/intuit/tank/tools/debugger/AgentDebuggerFrame.java @@ -1014,14 +1014,25 @@ public RequestResponsePanel getRequestResponsePanel() { public void exportCSV(File csvOutputFile) { try (PrintWriter pw = new PrintWriter(csvOutputFile)) { - pw.println("URL,HTTP Code,HTTP Msg,Response Time,Response Size,Headers...,Cookies..."); + pw.println("URL,HTTP Code,HTTP Msg,Response Time,Proxy Response Time,Response Size,Headers...,Cookies..."); steps.stream() .filter(step -> step.getResponse() != null) - .map(step -> step.getRequest().getRequestUrl() + "," + step.getResponse().convertToCSV()) - .forEach(pw::println); + .forEach(step -> { + String escapedUrl = escapeCSV(step.getRequest().getRequestUrl()); + pw.println(escapedUrl + "," + step.getResponse().convertToCSV()); + }); } catch ( FileNotFoundException e) { LOG.error("Error exporting CSV: {}", String.valueOf(e)); } } + private String escapeCSV(String value) { + if (value == null) { + return ""; + } + if (value.contains(",") || value.contains("\"") || value.contains("\n")) { + return "\"" + value.replace("\"", "\"\"") + "\""; + } + return value; + } }