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

