Sub-scope 08 (inbound-web) observability + proxy + filter + mvc/webflux remainder revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 generatedAt=2026-08-30T07:37:35+00:00 === OWNED FILES === ad29fda6cf666e83673267bf4a5e9f8e1398f9c1 main/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/MvcDisconnectDetector.java 86 479a4cb2cd2cd52a9c4209a55ced6c892f2f6b50 main/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/MvcStreamWriter.java 117 5e11dd088b32542688596629d9ba2bda45d266f5 main/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/MvcStreamingExecutorConfiguration.java 86 9bec0eae7bdf9aed96fdbe07eb34b0c60fb4209a main/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/VirtualThreadMvcConfiguration.java 77 e53c1995fe4995c04af32faafd868735c2f9f8f2 main/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/VirtualThreadSettings.java 103 ee140492d372bea76d992f1058c6b244f6da6346 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/ControlledBlockingBridge.java 70 857177965a147a7debc03b0e60fb4828b6520631 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/SlowConsumerClosedException.java 32 31fa7e1b220c27fcff7709efc086a83dbe304e91 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxDisconnectDetector.java 72 64cb4db2c9e741b3f780a63b0e3e9e9d25466398 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxSseAdapter.java 117 77d744ec0b05c69a27f3f30e6fab7e6d428d045e main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxSseHeartbeat.java 40 be63236a6dfaaee182f394c2edadce3a03427b34 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxStreamAdmission.java 124 f88258c1b19569a1765ed563e0866e062772b0e5 main/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxStreamWriter.java 70 841c398a94920699fde61451184f61c243a4a7e1 main/java/dev/caskeleton/adapter/inbound/web/filter/CacheControlFilter.java 35 0032f474ad554cd7276b3da819ad4d091d9c8d11 main/java/dev/caskeleton/adapter/inbound/web/filter/RequestLoggingFilter.java 147 4a2ddb15de9ee9d6d7ecb6c9d3d5386fc36686f0 main/java/dev/caskeleton/adapter/inbound/web/mvc/context/WebMvcRequestContextArgumentResolver.java 42 2a32453e80f6539a5427be10dbcf6634df561586 main/java/dev/caskeleton/adapter/inbound/web/mvc/context/WebMvcRequestContextHolder.java 66 250ebee6de1c046ea069d6b19e8927d77506fe49 main/java/dev/caskeleton/adapter/inbound/web/mvc/filter/WebMvcEvidenceFilter.java 114 13aff34a24f8c0224de66bb6bbe528411bfa495d main/java/dev/caskeleton/adapter/inbound/web/mvc/filter/WebMvcRequestIdFilter.java 146 dd75b418a205c76415f14ee546493e6b077739de main/java/dev/caskeleton/adapter/inbound/web/observability/HeaderSanitizer.java 25 ef9f1d3afe0b5e3f9cf29492c01ea0fdfbbb1695 main/java/dev/caskeleton/adapter/inbound/web/observability/MdcCorrelationIdPortAdapter.java 16 5e4cfcea7b360e37b132f51c9ddbb532511d753b main/java/dev/caskeleton/adapter/inbound/web/observability/MdcKeys.java 13 a790a5ee999056de8a0238ada60a319a61b3b45b main/java/dev/caskeleton/adapter/inbound/web/observability/ResponseMetaFactory.java 18 e18414ead91ed2945a8d32cd221b09754b2942a0 main/java/dev/caskeleton/adapter/inbound/web/observability/RetryAfterAdvisor.java 43 7a8ab4e5f306fe176e4fe503bc9ae25e59b97b5e main/java/dev/caskeleton/adapter/inbound/web/observability/WebAccessLogEvent.java 65 fd84d9ed27536784f87a92a247b7845fa524b4f0 main/java/dev/caskeleton/adapter/inbound/web/observability/WebAccessLogger.java 19 9a3056466bd7efac7ebf88e459a51c4bddbdc050 main/java/dev/caskeleton/adapter/inbound/web/observability/WebAuditAction.java 41 7118a82494a8b306838c92da8a181482a512cb7c main/java/dev/caskeleton/adapter/inbound/web/observability/WebAuditEvent.java 55 43cca5eb139a9ce6ccd417b9dbbab1b624f4b57b main/java/dev/caskeleton/adapter/inbound/web/observability/WebAuditPublisher.java 17 3c8f55205e241c8b9f6a95e76f9046dd78612080 main/java/dev/caskeleton/adapter/inbound/web/observability/WebMetricCardinalityPolicy.java 112 de2b95f45330d7c180edd647222741f8cc5103c5 main/java/dev/caskeleton/adapter/inbound/web/observability/WebObservationConvention.java 72 b9f52ddfb6d07054753d320b4bc5888a7011390a main/java/dev/caskeleton/adapter/inbound/web/proxy/ForwardedHeaderSanitizer.java 72 086d1b881e91c24e5e043fd0a7a7d7e263bb4f35 main/java/dev/caskeleton/adapter/inbound/web/proxy/NormalizedForwardedHeaders.java 158 f39d3d998e2851c95a2a1cd0bcda239ee751e4f1 main/java/dev/caskeleton/adapter/inbound/web/proxy/TrustedProxyPolicy.java 161 e1b3b88b46aecb7ccb502b9202623bbde9507549 main/java/dev/caskeleton/adapter/inbound/web/proxy/UntrustedForwardedHeaderException.java 30 d2ffecb95fce15ef375f368f706a8ff834e6368b main/java/dev/caskeleton/adapter/inbound/web/webflux/context/WebFluxRequestContextAccessor.java 73 88a2668dc8cd7323a80ef1cccedc6467c0df56c5 main/java/dev/caskeleton/adapter/inbound/web/webflux/context/WebFluxRequestContextFilter.java 147 1afebbb62bb9d3a468fe7dbc325873d889b5eeda main/java/dev/caskeleton/adapter/inbound/web/webflux/guard/BlockingCallDetectedException.java 26 b467ec7a0458c29b875e6eee1559b835e80223b5 main/java/dev/caskeleton/adapter/inbound/web/webflux/guard/BlockingDependencyGuard.java 79 cba639f6f927be1139777ac2295ed5228fc95f58 test/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/MvcAdvancedConfigurationTest.java 145 a3bd77e8e99c29eb47ee968fa02f4d71ae373148 test/java/dev/caskeleton/adapter/inbound/web/advanced/mvc/MvcStreamingWiringTest.java 175 f200ed3eb0fe0db3b5c3fdc7f905f826d8e51b61 test/java/dev/caskeleton/adapter/inbound/web/advanced/webflux/WebFluxStreamingTest.java 309 c9a604d350f8de1b7884aa2558bc0f02cbb4bfc3 test/java/dev/caskeleton/adapter/inbound/web/filter/CacheControlFilterTest.java 52 dd6e28c879899589a97a0fe2227935b6a25c4615 test/java/dev/caskeleton/adapter/inbound/web/filter/RequestLoggingFilterTest.java 296 0be7101fe31a3fba785072a2a84aa39bdd5bfb8a test/java/dev/caskeleton/adapter/inbound/web/mvc/filter/WebMvcEvidenceFilterTest.java 78 4810257df962a1b5d914e13cbb51a1e774f4003e test/java/dev/caskeleton/adapter/inbound/web/mvc/filter/WebMvcRequestIdFilterTest.java 71 793dbbbb7030d97017fa2ceb879b94b4622cda5e test/java/dev/caskeleton/adapter/inbound/web/observability/HeaderSanitizerTest.java 39 bdfb21f0cc2ff54ef55b1616067c5ce72131e1fc test/java/dev/caskeleton/adapter/inbound/web/observability/MdcCorrelationIdPortAdapterTest.java 36 4e01d57fc524f6ce9e529e3c67a24ab303c33185 test/java/dev/caskeleton/adapter/inbound/web/observability/ResponseMetaFactoryTest.java 34 9d8bfb0ccfdd2510d49e6b044b9bc690fdd6029e test/java/dev/caskeleton/adapter/inbound/web/observability/RetryAfterAdvisorTest.java 85 b6d0c36a1538ddd23daa94b47eb6af86a4acdbb5 test/java/dev/caskeleton/adapter/inbound/web/observability/WebObservabilityPolicyTest.java 188 bc4a4a26a90697c783240dcf4f70d2cd3a3fa4bc test/java/dev/caskeleton/adapter/inbound/web/proxy/ForwardedHeaderSanitizerTest.java 129 a9023edbbe9bae94c9539ec423a354af82467382 test/java/dev/caskeleton/adapter/inbound/web/webflux/context/WebFluxRequestContextAccessorTest.java 93 6dd0ac4a3cddcf083a07d1132211e209933d169d test/java/dev/caskeleton/adapter/inbound/web/webflux/guard/BlockingDependencyGuardTest.java 92 file_count=53 === 8.2 X-Request-Id 를 쓰는 두 배선 필터 === $ grep -n 'trustInboundRequestId\|REQUEST_ID_HEADER\|setHeader' adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/mvc/filter/WebMvcRequestIdFilter.java 42: public static final String REQUEST_ID_HEADER = "X-Request-Id"; 54: private final boolean trustInboundRequestId; 65: * @param trustInboundRequestId whether a client may choose the request id, which only a trusted 69: public WebMvcRequestIdFilter(boolean trustInboundRequestId, int order) { 70: this.trustInboundRequestId = trustInboundRequestId; 85: response.setHeader(REQUEST_ID_HEADER, requestId.value()); 106: if (!trustInboundRequestId) { 109: return sanitized(request.getHeader(REQUEST_ID_HEADER)) exit=0 $ grep -n 'HEADER_REQUEST_ID\|resolveOrGenerate\|setHeader\|MDC.put' adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/filter/RequestLoggingFilter.java | head -12 40: private static final String HEADER_REQUEST_ID = "X-Request-Id"; 56: String requestId = resolveOrGenerate(req.getHeader(HEADER_REQUEST_ID)); 57: String correlationId = resolveOrGenerate(req.getHeader(HEADER_CORRELATION_ID)); 58: res.setHeader(HEADER_REQUEST_ID, requestId); 59: res.setHeader(HEADER_CORRELATION_ID, correlationId); 61: MDC.put(MdcKeys.REQUEST_ID, requestId); 62: MDC.put(MdcKeys.CORRELATION_ID, correlationId); 65: TraceParent traceParent = resolveOrGenerateTraceParent(req.getHeader(HEADER_TRACEPARENT)); 66: MDC.put(MdcKeys.TRACE_ID, traceParent.traceId()); 67: MDC.put(MdcKeys.SPAN_ID, traceParent.spanId()); 68: res.setHeader(HEADER_TRACEPARENT, traceParent.toHeader()); 91: private static String resolveOrGenerate(String inbound) { exit=0 $ grep -n 'class RequestLoggingFilter' -B 3 adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/filter/RequestLoggingFilter.java 33- * trace_id} when a real tracer is later wired in. 34- */ 35-@Component 36:public class RequestLoggingFilter extends OncePerRequestFilter { exit=0 $ cat adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/observability/HeaderSanitizer.java | grep -vE '^import|^package' /** * Neutralizes inbound header values before they reach MDC / logs by stripping every {@code \r}, * {@code \n} and ASCII control char ({@code < 0x20}) and length-capping the result. See README for * the design rationale. */ public final class HeaderSanitizer { private HeaderSanitizer() {} public static String sanitize(String raw, int maxLength) { if (raw == null) { return null; } StringBuilder sb = new StringBuilder(Math.min(raw.length(), maxLength)); for (int i = 0; i < raw.length() && sb.length() < maxLength; i++) { char c = raw.charAt(i); if (c >= 0x20) { sb.append(c); } } return sb.toString(); } } exit=0 === 8.1 proxy 패키지 도달성 === TrustedProxyPolicy 1 main 1 test 1 testkit NormalizedForwardedHeaders 2 main 2 test 1 testkit ForwardedHeaderSanitizer 1 test 1 testkit WebAuditPublisher WebAccessLogger 1 main $ grep -rn 'forward-headers-strategy' app-bootstrap/src/main/resources/application.yml 321: forward-headers-strategy: ${APP_SERVER_FORWARD_HEADERS_STRATEGY:framework} exit=0 $ grep -n 'void ' adapter/inbound/web/src/nginxProxyTest/java/dev/caskeleton/adapter/inbound/web/testkit/proxy/NginxProxyContractIT.java | head -12 37: static void requireDocker() { 47: static void stopProxy() { 63: void attackerCannotOverrideForwardedHost() { 79: void attackerCannotDowngradeTheForwardedScheme() { 93: void attackerCannotForgeTheClientAddress() { 107: void standardForwardedHeaderIsCleared() { 123: void prefixIsAppliedExactlyOnce() { 134: void publishedUriCarriesTheIncomingPrefix() { 143: void clientCannotInjectAPrefix() { 157: void bodyWithinTheProxyLimitArrives() { 167: void oversizedBodyIsRefusedAtTheProxy() { 180: void unknownPrefixIsNotProxied() { exit=0 $ grep -rn 'TrustedProxyPolicy.of' adapter/inbound/web/src/testkit adapter/inbound/web/src/testkit/java/dev/caskeleton/adapter/inbound/web/testkit/proxy/ProxyFixtureController.java:53: TrustedProxyPolicy.of("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8")); exit=0 === 8.4 ExternalRequestContext 생성 === $ sed -n '140,146p' adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/webflux/context/WebFluxRequestContextFilter.java private ExternalRequestContext externalRequest(ServerHttpRequest request) { var uri = request.getURI(); String scheme = uri.getScheme() == null ? "http" : uri.getScheme(); int port = uri.getPort() > 0 ? uri.getPort() : ("https".equalsIgnoreCase(scheme) ? 443 : 80); String host = uri.getHost() == null ? "localhost" : uri.getHost(); return new ExternalRequestContext(scheme, host, port, ""); } exit=0