Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/175-httpclient-suite-verification.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

87 lines
4.3 KiB
Plaintext

httpclient 모듈 완료 검증 — lane 실행과 실패 진단
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
generatedAt=2026-08-30T05:40:58+00:00
=== 1. :check 는 archive-hygiene 게이트에서 실패한다 (소스와 무관) ===
$ ./gradlew :adapter:outbound:httpclient:check → BUILD FAILED
원인: build/libs 에 이전 리비전 JAR 이 남아 있음
$ ls src/adapter/outbound/httpclient/build/libs
httpclient-0.0.1+0137263441f6.jar
httpclient-0.0.1+a24ece9cf797.jar
httpclient-0.0.1+e98b56eb03ec.jar
exit=0
게이트 정의: src/gradle/archive-hygiene.gradle — 저장소가 remedy 태스크 cleanStaleTraceableJars 를 제공한다.
이 분석은 사용자 워크스페이스의 빌드 산출물을 삭제하지 않았다.
=== 2. 다섯 lane 개별 실행 ===
$ ./gradlew :adapter:outbound:httpclient:test :…:httpClientStableContractTest :…:httpClientSecurityTest :…:httpClientBlockHoundTest :…:spring62ApiSurfaceScan
GRADLE_EXIT=1 — :test 에서 283 tests completed, 3 failed
=== 3. JUnit 집계 ===
test: classes=61 tests=283 failures=3 errors=0 skipped=0
httpClientStableContractTest: classes=7 tests=24 failures=0 errors=0 skipped=0
httpClientSecurityTest: classes=2 tests=9 failures=0 errors=0 skipped=0
httpClientBlockHoundTest: classes=1 tests=3 failures=0 errors=0 skipped=0
spring62ApiSurfaceScan: classes=1 tests=6 failures=0 errors=0 skipped=0
=== 4. 실패 3건의 정확한 내용 ===
anUntrustedAuthorityIsAPermanentTlsFailure() -> org.opentest4j.AssertionFailedError: expected: TLS_HANDSHAKE but was: CONNECT
anExpiredCertificateIsAPermanentTlsFailure() -> org.opentest4j.AssertionFailedError: expected: TLS_HANDSHAKE but was: CONNECT
aHostnameMismatchIsAPermanentTlsFailure() -> org.opentest4j.AssertionFailedError: expected: TLS_HANDSHAKE but was: CONNECT
=== 5. 원인: 원인 사슬을 바깥부터 훑고 CONNECT 분기가 TLS 분기보다 앞에 있다 ===
$ sed -n '30,42p' src/adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/apache/ApacheFailureClassifier.java
@Override
public TransportFailure classify(Throwable failure, AttemptStage lastObservedStage) {
// The whole cause chain is inspected, not just the outermost throwable: Spring wraps engine
// exceptions, and a wrapped ConnectException still proves the request was never sent. Matching
// only the outer type would downgrade a provable NOT_SENT to an ambiguous SENT_NO_RESPONSE.
for (Throwable cause : chain(failure)) {
TransportFailure recognized = recognize(cause, lastObservedStage);
if (recognized != null) {
return recognized;
}
}
return conservative(lastObservedStage, FailureCategory.UNKNOWN, "TRANSPORT_FAILURE");
exit=0
$ sed -n '58,73p' src/adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/apache/ApacheFailureClassifier.java
if (cause instanceof ConnectTimeoutException
|| cause instanceof HttpHostConnectException
|| cause instanceof ConnectException
|| cause instanceof NoRouteToHostException) {
return TransportFailure.notSent(
AttemptStage.CONNECT, FailureCategory.CONNECT, "CONNECT_FAILED");
}
if (cause instanceof SSLPeerUnverifiedException || cause instanceof CertificateException) {
return TransportFailure.notSent(
AttemptStage.TLS_HANDSHAKE, FailureCategory.TLS_PERMANENT, "TLS_TRUST_FAILED");
}
if (cause instanceof SSLHandshakeException) {
return TransportFailure.notSent(
AttemptStage.TLS_HANDSHAKE, FailureCategory.TLS_PERMANENT, "TLS_HANDSHAKE_FAILED");
}
if (cause instanceof SSLException
exit=0
--- 재시도 결과 대비 ---
$ grep -n 'case CONNECT\|case TLS_TRANSIENT\|permanent()' src/adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java
37: if (context.failureCategory().permanent()) {
107: case CONNECT -> RetryAllowed.of("CONNECT");
109: case TLS_TRANSIENT -> RetryAllowed.of("TLS_TRANSIENT");
exit=0
$ grep -n 'TLS_PERMANENT' src/adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/api/operation/FailureCategory.java
18: TLS_PERMANENT,
38: || this == TLS_PERMANENT
exit=0
=== 6. 작업 트리 ===
$ git status --short | wc -l
0
exit=0