- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다 - 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5 (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를 techviz 로 만들었다 - 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs 돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다. Concept 이 인용한 코드가 SSOT 에 없어 뺐다 - candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
123 lines
5.0 KiB
Markdown
123 lines
5.0 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: analysis-finding-a13-f007
|
|
title: '"상한을 두고 읽는다"는 본문 핸들러가 전부 읽은 뒤에 자른다'
|
|
topic: notification-and-delivery
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:analysis-finding-a13-f007
|
|
evidenceCapturedOn: 2026-09-01
|
|
body: case-analysis-finding-a13-f007.body.md
|
|
assets:
|
|
- key: analysis-finding-a13-f007
|
|
file: ../../../final/evidence/rendered/analysis-finding-a13-f007.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/analysis-finding-a13-f007.txt
|
|
source:
|
|
- 원본 분석 절은 final/document.md#a13#L795 이다.
|
|
---
|
|
|
|
# "상한을 두고 읽는다"는 본문 핸들러가 전부 읽은 뒤에 자른다
|
|
|
|
자바독이 상한에서 읽기를 멈춘다고 적는다. 구현은 무제한 상류 구독자의 결과에 자르기 함수를 얹는다. 자르기는 전체가 힙에 모인 다음에 일어난다. 호출부 주석이 쓰지 말라고 명시한 바로 그 상류를 두 줄 아래 함수가 쓴다.
|
|
|
|
## 관계
|
|
|
|
- **음수 Retry-After 가 확정 거절을 영구 중지되는 모호한 제출로 바꾼다**
|
|
같은 리프의 다른 원격 입력 사례다.
|
|
- **이름이 상한을 말한다고 상한이 걸리는 것은 아니다**
|
|
이 사례가 그 규칙의 형태다.
|
|
- **두 연산자가 이름만 있고 아무것도 하지 않는다**
|
|
같은 계열의 서술과 코드 어긋남이다.
|
|
|
|
## 문제
|
|
|
|
응답 본문을 읽는 처리기가 있다. 자바독이 상한에서 읽기를 멈추는 본문 처리기라고 적는다.
|
|
|
|
호출부 주석도 이유를 길게 적는다.
|
|
|
|
제공자 응답은 진단이라는 것이다. 상태와 헤더 몇 개와 오류 문서라는 것이다. 상한 없이 읽으면 보내는 쪽의 힙이 저쪽이 보내기로 한 양의 함수가 된다는 것이다. 끝나지 않는 조각 응답은 요청 하나짜리 장애라는 것이다.
|
|
|
|
## 결론
|
|
|
|
구현이 그것을 하지 않는다.
|
|
|
|
바이트 배열 구독자를 상류로 두고 그 결과에 자르기 함수를 얹는다.
|
|
|
|
사상 구독자의 마무리 함수는 상류가 완료된 뒤 그 결과에 적용된다.
|
|
|
|
상류는 무제한으로 요청하여 본문 전체를 힙에 모은다. 자르기는 그다음이다.
|
|
|
|
호출부 주석이 막겠다고 선언한 것과 정확히 반대다.
|
|
|
|
주석은 그 상류를 쓰지 말라고 쓰여 있고, 두 줄 아래 함수가 그것을 상류로 쓴다.
|
|
|
|
실패 시나리오는 이렇다.
|
|
|
|
제공자나 그 자리에 들어온 무엇이든 응답으로 큰 본문을 빠르게 보낸다.
|
|
|
|
요청 시간 제한이 시간은 제한하므로 끝나지 않는 조각 응답은 시간 제한에서 끊긴다.
|
|
|
|
그러나 주석이 두 번째로 든 위험은 그대로다. 보내는 쪽의 힙이 저쪽이 보내기로 한 양의 함수가 되는 상황이다.
|
|
|
|
시간 제한 안에 수 기가바이트를 받을 수 있는 연결에서는 그만큼이 전부 배열로 쌓인 뒤 상한 크기로 잘린다.
|
|
|
|
동시 발송이 많을수록 배수로 늘어난다. 이 리프는 발송 작업자가 가상 스레드로 퍼져 나간다.
|
|
|
|
판정은 P2 다.
|
|
|
|
## 검증 환경
|
|
|
|
OpenJDK : 21.0.12
|
|
확인 방식 : 구현과 자바독, 호출부 주석 대조
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
원문은 final/evidence/raw/173 계열에 있다.
|
|
|
|
1. 본문 처리기의 자바독을 읽는다.
|
|
2. 구현이 어떤 상류 구독자를 쓰는지 확인한다.
|
|
3. 사상 구독자의 마무리 함수가 언제 적용되는지 확인한다.
|
|
4. 호출부 주석을 읽는다.
|
|
5. 주석이 배제한 상류와 구현의 상류를 대조한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
본문 핸들러는 다음이다.
|
|
|
|
```java
|
|
// JdkNotificationHttpGateway.java:116-128
|
|
/**
|
|
* A body handler that stops reading at the cap.
|
|
*/
|
|
private static HttpResponse.BodyHandler<byte[]> boundedBody(int maxBytes) {
|
|
return responseInfo ->
|
|
HttpResponse.BodySubscribers.mapping(
|
|
HttpResponse.BodySubscribers.ofByteArray(),
|
|
body -> body.length <= maxBytes ? body : java.util.Arrays.copyOf(body, maxBytes));
|
|
}
|
|
```
|
|
|
|
## 본문 핸들러가 도는 순서
|
|
|
|
:::evidence key="analysis-finding-a13-f007" alt="분석 문서 final/document.md#a13 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md#a13 발췌 — 15줄" zoom="true"
|
|
:::
|
|
|
|
## finisher는 upstream이 끝난 뒤에 돈다
|
|
|
|
`BodySubscribers.mapping(upstream, finisher)`의 finisher는 upstream이 완료된 뒤 그 결과에 적용된다. upstream은 `ofByteArray()`이고, 그것은 무제한으로 요청하여 본문 **전체를 힙에 모은다**. 잘라내기는 그 다음이다.
|
|
|
|
## 호출부 주석이 선언한 것과 정확히 반대다
|
|
|
|
`:60-62` — "Bounded, not ofByteArray(). A provider response is diagnostic — a status, some headers, an…". P2.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
큰 응답을 보내는 가짜 제공자로 힙 증가를 측정하지 않았다. 구독자 규약상 그 결과가 나온다.
|
|
|
|
<!-- body:end -->
|