From 34289dedb9bb5a3a391ecec42253441d672486ec Mon Sep 17 00:00:00 2001 From: Yashwant Date: Tue, 22 Jul 2025 22:24:08 +0530 Subject: [PATCH] adding client servicename as request header --- .../apache-httpclient-4.0/build.gradle.kts | 2 + .../v4_0/ApacheHttpClientUtils.java | 10 +++ instrumentation/grpc-1.6/build.gradle.kts | 1 + .../v1_6/client/GrpcClientInterceptor.java | 8 ++ .../netty/netty-4.0/build.gradle.kts | 1 + .../HttpClientRequestTracingHandler.java | 8 ++ .../netty/netty-4.1/build.gradle.kts | 1 + .../HttpClientRequestTracingHandler.java | 8 ++ .../okhttp/okhttp-3.0/build.gradle.kts | 1 + .../okhttp/v3_0/OkHttpTracingInterceptor.java | 17 ++++ .../vertx/vertx-web-3.0/build.gradle.kts | 1 + .../vertx/HttpRequestInstrumentation.java | 6 ++ .../config/ServiceNameHeaderUtils.java | 38 +++++++++ .../agent/testing/AbstractHttpClientTest.java | 77 +++++++++++++++++++ .../agent/testing/TestHttpServer.java | 55 +++++++++++++ 15 files changed, 234 insertions(+) create mode 100644 otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/config/ServiceNameHeaderUtils.java diff --git a/instrumentation/apache-httpclient-4.0/build.gradle.kts b/instrumentation/apache-httpclient-4.0/build.gradle.kts index 9c4503a4..0cc1f9bc 100644 --- a/instrumentation/apache-httpclient-4.0/build.gradle.kts +++ b/instrumentation/apache-httpclient-4.0/build.gradle.kts @@ -41,6 +41,8 @@ val versions: Map by extra dependencies { api(project(":instrumentation:java-streams")) + api(project(":otel-extensions")) + library("org.apache.httpcomponents:httpclient:4.0") testImplementation(project(":testing-common")) } diff --git a/instrumentation/apache-httpclient-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/apachehttpclient/v4_0/ApacheHttpClientUtils.java b/instrumentation/apache-httpclient-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/apachehttpclient/v4_0/ApacheHttpClientUtils.java index 77cec83e..637be8ff 100644 --- a/instrumentation/apache-httpclient-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/apachehttpclient/v4_0/ApacheHttpClientUtils.java +++ b/instrumentation/apache-httpclient-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/apachehttpclient/v4_0/ApacheHttpClientUtils.java @@ -40,6 +40,7 @@ import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeCharsetUtils; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,6 +59,12 @@ public static void addRequestHeaders(Span span, HeaderIterator headerIterator) { addHeaders(span, headerIterator, HypertraceSemanticAttributes::httpRequestHeader); } + public static void addClientServiceNameHeader(HttpMessage request) { + request.addHeader( + ServiceNameHeaderUtils.getClientServiceKey(), + ServiceNameHeaderUtils.getClientServiceName()); + } + private static void addHeaders( Span span, HeaderIterator headerIterator, @@ -73,6 +80,9 @@ public static void traceRequest(Span span, HttpMessage request) { ApacheHttpClientUtils.addRequestHeaders(span, request.headerIterator()); } + // Add service name header to outgoing requests + ApacheHttpClientUtils.addClientServiceNameHeader(request); + if (instrumentationConfig.httpBody().request() && request instanceof HttpEntityEnclosingRequest) { HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request; diff --git a/instrumentation/grpc-1.6/build.gradle.kts b/instrumentation/grpc-1.6/build.gradle.kts index 3ee2adb2..4606f84c 100644 --- a/instrumentation/grpc-1.6/build.gradle.kts +++ b/instrumentation/grpc-1.6/build.gradle.kts @@ -59,6 +59,7 @@ val grpcVersion = "1.6.0" dependencies { api("io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:${versions["opentelemetry_java_agent"]}") + api(project(":otel-extensions")) implementation(project(":instrumentation:grpc-common")) implementation(project(":shaded-protobuf-java-util", "shadow")) diff --git a/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/client/GrpcClientInterceptor.java b/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/client/GrpcClientInterceptor.java index 3c59e306..5e587102 100644 --- a/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/client/GrpcClientInterceptor.java +++ b/instrumentation/grpc-1.6/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/grpc/v1_6/client/GrpcClientInterceptor.java @@ -30,11 +30,16 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.grpc.v1_6.GrpcSpanDecorator; import org.hypertrace.agent.core.config.InstrumentationConfig; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GrpcClientInterceptor implements ClientInterceptor { + private static final Metadata.Key SERVICE_NAME_METADATA_KEY = + Metadata.Key.of( + ServiceNameHeaderUtils.getClientServiceKey(), Metadata.ASCII_STRING_MARSHALLER); + private static final Logger log = LoggerFactory.getLogger(GrpcClientInterceptor.class); @Override @@ -69,6 +74,9 @@ static final class TracingClientCall @Override public void start(Listener responseListener, Metadata headers) { + // Add service name header to outgoing requests + headers.put(SERVICE_NAME_METADATA_KEY, ServiceNameHeaderUtils.getClientServiceName()); + super.start(new TracingClientCallListener<>(responseListener, span), headers); try { diff --git a/instrumentation/netty/netty-4.0/build.gradle.kts b/instrumentation/netty/netty-4.0/build.gradle.kts index b8cab061..31c9a1f7 100644 --- a/instrumentation/netty/netty-4.0/build.gradle.kts +++ b/instrumentation/netty/netty-4.0/build.gradle.kts @@ -46,6 +46,7 @@ val versions: Map by extra val nettyVersion = "4.0.38.Final" dependencies { + api(project(":otel-extensions")) implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4.0:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-netty-4-common:${versions["opentelemetry_java_agent"]}") diff --git a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientRequestTracingHandler.java b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientRequestTracingHandler.java index 4c4af06b..db12ff1d 100644 --- a/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_0/client/HttpClientRequestTracingHandler.java @@ -40,6 +40,7 @@ import org.hypertrace.agent.core.instrumentation.utils.ContentLengthUtils; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeCharsetUtils; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapter { @@ -68,6 +69,13 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { headersMap.forEach(span::setAttribute); } + // Add service name header to outgoing requests + httpRequest + .headers() + .add( + ServiceNameHeaderUtils.getClientServiceKey(), + ServiceNameHeaderUtils.getClientServiceName()); + CharSequence contentType = DataCaptureUtils.getContentType(httpRequest); if (instrumentationConfig.httpBody().request() && contentType != null diff --git a/instrumentation/netty/netty-4.1/build.gradle.kts b/instrumentation/netty/netty-4.1/build.gradle.kts index 43fb115c..5bacf427 100644 --- a/instrumentation/netty/netty-4.1/build.gradle.kts +++ b/instrumentation/netty/netty-4.1/build.gradle.kts @@ -44,6 +44,7 @@ afterEvaluate{ val versions: Map by extra dependencies { + api(project(":otel-extensions")) implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-netty-4-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-netty-common:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.instrumentation:opentelemetry-netty-4-common:${versions["opentelemetry_java_agent"]}") diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientRequestTracingHandler.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientRequestTracingHandler.java index b6775666..74ecb043 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientRequestTracingHandler.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/client/HttpClientRequestTracingHandler.java @@ -40,6 +40,7 @@ import org.hypertrace.agent.core.instrumentation.utils.ContentLengthUtils; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeCharsetUtils; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapter { @@ -67,6 +68,13 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise prm) { headersMap.forEach(span::setAttribute); } + // Add service name header to outgoing requests + httpRequest + .headers() + .add( + ServiceNameHeaderUtils.getClientServiceKey(), + ServiceNameHeaderUtils.getClientServiceName()); + CharSequence contentType = DataCaptureUtils.getContentType(httpRequest); if (instrumentationConfig.httpBody().request() && contentType != null diff --git a/instrumentation/okhttp/okhttp-3.0/build.gradle.kts b/instrumentation/okhttp/okhttp-3.0/build.gradle.kts index 3c6371dc..b3828543 100644 --- a/instrumentation/okhttp/okhttp-3.0/build.gradle.kts +++ b/instrumentation/okhttp/okhttp-3.0/build.gradle.kts @@ -26,6 +26,7 @@ afterEvaluate{ val versions: Map by extra dependencies { + api(project(":otel-extensions")) compileOnly("com.squareup.okhttp3:okhttp:3.0.0") testImplementation(project(":testing-common")) } diff --git a/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttpTracingInterceptor.java b/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttpTracingInterceptor.java index 9ba992a6..7281c95d 100644 --- a/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttpTracingInterceptor.java +++ b/instrumentation/okhttp/okhttp-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/okhttp/v3_0/OkHttpTracingInterceptor.java @@ -36,6 +36,7 @@ import org.hypertrace.agent.core.config.InstrumentationConfig; import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,6 +58,10 @@ public Response intercept(Chain chain) throws IOException { if (instrumentationConfig.httpHeaders().request()) { captureHeaders(span, request.headers(), HypertraceSemanticAttributes::httpRequestHeader); } + + // Add service name header to outgoing requests + request = addClientSeriveNameHeader(request); + captureRequestBody(span, request.body()); Response response = chain.proceed(request); @@ -149,4 +154,16 @@ private static void captureHeaders( } } } + + private static Request addClientSeriveNameHeader(Request request) { + // Add service name header to outgoing requests + request = + request + .newBuilder() + .addHeader( + ServiceNameHeaderUtils.getClientServiceKey(), + ServiceNameHeaderUtils.getClientServiceName()) + .build(); + return request; + } } diff --git a/instrumentation/vertx/vertx-web-3.0/build.gradle.kts b/instrumentation/vertx/vertx-web-3.0/build.gradle.kts index 942e1e73..bf2cce05 100644 --- a/instrumentation/vertx/vertx-web-3.0/build.gradle.kts +++ b/instrumentation/vertx/vertx-web-3.0/build.gradle.kts @@ -27,6 +27,7 @@ val versions: Map by extra val nettyVersion = "4.0.28.Final" dependencies { + api(project(":otel-extensions")) implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-vertx-web-3.0:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-vertx-http-client-3.0:${versions["opentelemetry_java_agent"]}") implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-vertx-http-client-common:${versions["opentelemetry_java_agent"]}") diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java index 02c60d0f..38d6c85d 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java @@ -43,6 +43,7 @@ import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; import org.hypertrace.agent.core.instrumentation.buffer.BoundedCharArrayWriter; import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.otel.extensions.config.ServiceNameHeaderUtils; public class HttpRequestInstrumentation implements TypeInstrumentation { @@ -93,6 +94,11 @@ public static void enter(@Advice.This HttpClientRequest request) { return; } + request + .headers() + .add( + ServiceNameHeaderUtils.getClientServiceKey(), + ServiceNameHeaderUtils.getClientServiceName()); Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { return; diff --git a/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/config/ServiceNameHeaderUtils.java b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/config/ServiceNameHeaderUtils.java new file mode 100644 index 00000000..96e33617 --- /dev/null +++ b/otel-extensions/src/main/java/org/hypertrace/agent/otel/extensions/config/ServiceNameHeaderUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.hypertrace.agent.otel.extensions.config; + +/** Utility class for adding service name header to outgoing requests (exit calls). */ +public class ServiceNameHeaderUtils { + + private static final String SERVICE_NAME_HEADER = "ta-client-servicename"; + + private ServiceNameHeaderUtils() {} + + public static String getClientServiceKey() { + return SERVICE_NAME_HEADER; + } + + /** + * Gets the service name from HypertraceConfig. + * + * @return the service name configured in the agent + */ + public static String getClientServiceName() { + return HypertraceConfig.get().getServiceName().getValue(); + } +} diff --git a/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java index d6b7fad7..16e21d21 100644 --- a/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java +++ b/testing-common/src/main/java/org/hypertrace/agent/testing/AbstractHttpClientTest.java @@ -40,6 +40,7 @@ public abstract class AbstractHttpClientTest extends AbstractInstrumenterTest { private static final String GET_NO_CONTENT_PATH_FORMAT = "http://localhost:%d/get_no_content"; private static final String GET_JSON_PATH_FORMAT = "http://localhost:%d/get_json"; private static final String GET_GZIP_FORMAT = "http://localhost:%d/gzip"; + private static final String VERIFY_HEADERS_FORMAT = "http://localhost:%d/verify_headers"; private static final String HEADER_NAME = "headername"; private static final String HEADER_VALUE = "headerValue"; private static final Map headers; @@ -358,6 +359,82 @@ public void getGzipResponse() } } + @Test + public void verifyServiceNameHeader() + throws IOException, TimeoutException, InterruptedException, ExecutionException { + String uri = String.format(VERIFY_HEADERS_FORMAT, testHttpServer.port()); + + Response response = doGetRequest(uri, headers); + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertTrue( + response.body.contains(TestHttpServer.HeaderVerificationHandler.SERVICE_NAME_HEADER_KEY), + "Response should indicate that the service name header was received"); + // The response format is + // {"received_header":"ta-client-servicename","header_value":"ACTUAL_VALUE"} + Assertions.assertTrue( + response.body.contains("received_header"), + "Response should confirm that headers were processed"); + Assertions.assertTrue( + response.body.contains("header_value"), + "Response should include the header value that was sent"); + + TEST_WRITER.waitForTraces(1); + List> traces; + if (hasResponseBodySpan) { + traces = + TEST_WRITER.waitForSpans( + 2, span -> span.getKind().equals(Span.SpanKind.SPAN_KIND_SERVER)); + } else { + traces = + TEST_WRITER.waitForSpans( + 1, + span -> + !span.getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT) + || span.getAttributesList().stream() + .noneMatch( + keyValue -> + keyValue.getKey().equals("http.url") + && keyValue + .getValue() + .getStringValue() + .contains("/verify_headers"))); + } + + Assertions.assertEquals(1, traces.size()); + Span clientSpan = traces.get(0).get(0); + if (hasResponseBodySpan) { + Assertions.assertEquals(2, traces.get(0).size()); + Span responseBodySpan = traces.get(0).get(1); + if (traces.get(0).get(1).getKind().equals(Span.SpanKind.SPAN_KIND_CLIENT)) { + responseBodySpan = traces.get(0).get(0); + clientSpan = traces.get(0).get(1); + } + + Assertions.assertNull(TEST_WRITER.getAttributesMap(clientSpan).get("http.response.body")); + + Assertions.assertTrue( + TEST_WRITER + .getAttributesMap(responseBodySpan) + .get("http.response.body") + .getStringValue() + .contains("received_header"), + "Response body should confirm that echo headers reached server"); + + } else { + String clientServiceNameResponseHeaderKey = + "http.response.header." + + TestHttpServer.HeaderVerificationHandler.HEADER_ECHO_PREFIX + + TestHttpServer.HeaderVerificationHandler.SERVICE_NAME_HEADER_KEY; + Assertions.assertEquals( + TEST_WRITER + .getAttributesMap(clientSpan) + .get(clientServiceNameResponseHeaderKey) + .getStringValue(), + "unknown"); + } + } + private void assertHeaders(Span span) { Assertions.assertEquals( TestHttpServer.RESPONSE_HEADER_VALUE, diff --git a/testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java b/testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java index 7e49ab1c..32f30d25 100644 --- a/testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java +++ b/testing-common/src/main/java/org/hypertrace/agent/testing/TestHttpServer.java @@ -19,6 +19,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; @@ -45,6 +48,7 @@ public void start() throws Exception { handlerList.addHandler(new PostRedirect()); handlerList.addHandler(new EchoHandler()); handlerList.addHandler(new GzipHandler()); + handlerList.addHandler(new HeaderVerificationHandler()); server.setHandler(handlerList); server.start(); } @@ -215,4 +219,55 @@ public void handle( } } } + + public static class HeaderVerificationHandler extends ResponseTestHeadersHandler { + + public static final String VERIFY_HEADER_PATH = "/verify_headers"; + public static final String HEADER_ECHO_PREFIX = "echo-header-"; + public static final String SERVICE_NAME_HEADER_KEY = "ta-client-servicename"; + public static final String RECEIVED_HEADER_JSON_FORMAT = + "{\"received_header\":\"%s\",\"header_value\":\"%s\"}"; + + @Override + public void handle( + String target, + Request baseRequest, + HttpServletRequest request, + HttpServletResponse response) + throws IOException { + + if (target.equals(VERIFY_HEADER_PATH)) { + response.setStatus(200); + response.setContentType("application/json"); + + // Get all headers from the request + Map headers = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + String headerValue = request.getHeader(headerName); + headers.put(headerName, headerValue); + + // Echo back the headers in the response with a prefix + response.setHeader(HEADER_ECHO_PREFIX + headerName, headerValue); + } + + // Check specifically for our service name header + String serviceNameHeaderValue = request.getHeader(SERVICE_NAME_HEADER_KEY); + if (serviceNameHeaderValue != null) { + response + .getWriter() + .print( + String.format( + RECEIVED_HEADER_JSON_FORMAT, + SERVICE_NAME_HEADER_KEY, + serviceNameHeaderValue)); + } else { + response.getWriter().print("{\"received_header\":\"none\"}"); + } + + baseRequest.setHandled(true); + } + } + } }