Skip to content

Comments

[Http] HttpRequest.body 관련 기능 개발#35

Merged
codingbaraGo merged 6 commits intomainfrom
feat/http/requestbody/#34
Jan 8, 2026
Merged

[Http] HttpRequest.body 관련 기능 개발#35
codingbaraGo merged 6 commits intomainfrom
feat/http/requestbody/#34

Conversation

@codingbaraGo
Copy link
Owner

💻 작업 내용

  • HttpRequest.body 생성 로직 개발
  • Socket의 input stream -> HttpRequest.body 변환 컨버터 개발
  • byte 배열 -> content type에 맞는 객체로 변환하는 컨버터 개발

🎯 관련 이슈

closed #34

- Socker->HttpRequest.body 변환 converter 개발
- DI 반영
- 테스트 반영
- HttpRequest 객체는 URI와 byte[]로 이루어진 body만 저장
- 필요에 따라 argument resolver에서 변환해서 사용
- web.dispatch 하위를 adapter와 argument로 분리
@codingbaraGo codingbaraGo self-assigned this Jan 8, 2026
@codingbaraGo codingbaraGo added the feature New feature or request label Jan 8, 2026
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체 검토 의견

이 PR은 HttpRequest의 body 파싱과 처리 로직을 구현하는 좋은 구조 개선입니다. 패키지 리팩토링(web.dispatch → web.dispatch.argument/resolver)과 책임 분리도 적절합니다. 다만 몇 가지 안정성 관련 이슈가 있습니다:

주요 개선사항 필요

  1. readLine() 구현의 \r\n 처리 - 현재 로직이 엣지 케이스에서 불안정할 수 있습니다.
  2. Null 참조 예외 - QueryParamsResolver에서 getContentType()과 getBody()가 null일 때의 처리 부재
  3. 예외 처리의 명확성 - EOF 발생 시 읽은 바이트 수 정보 추가로 디버깅 개선 권장
  4. QueryParameters.getQueryValue()의 범위 체크 - 키 존재 여부와 리스트 크기 검증 필요
  5. Content-Type 동기화 문제 - contentType 필드가 headers와 불일치할 가능성

일관성 문제

BufferedReaderHttpRequestConverter와 InputStreamHttpRequestConverter의 용도 구분이 모호합니다. 현재 설정상 InputStreamHttpRequestConverter를 사용하므로, BufferedReader 버전은 정리하거나 명확한 이유를 문서화하세요.

위 이슈들을 해결하면 안정성과 유지보수성이 크게 향상될 것입니다.

Comment on lines 80 to 102
request.setBody(body);
}

private String readLine(BufferedInputStream in) throws IOException {
ByteArrayOutputStream lineBuffer = new ByteArrayOutputStream(128);

int prev = -1;
while (true) {
int cur = in.read();
if (cur == -1) {
if (lineBuffer.size() == 0) return null;
return lineBuffer.toString();
}

if (prev == '\r' && cur == '\n') {
byte[] raw = lineBuffer.toByteArray();
int len = raw.length;
if (len > 0 && raw[len - 1] == '\r') {
return new String(raw, 0, len - 1);
}
return lineBuffer.toString();
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readLine() 로직에서 \r\n 처리 오류 위험

현재 로직은 바이트 단위로 읽으면서 prev == '\r' && cur == '\n'일 때를 줄의 끝으로 판단합니다. 하지만 문제점:

  1. lineBuffer\r 문자가 포함되어 있는 상태에서 확인하므로, 마지막 \r을 제거하는 조건(if (len > 0 && raw[len - 1] == '\r'))이 불완전합니다.
  2. 만약 버퍼에 여러 개의 \r이 있거나 예상치 못한 입력이 들어오면 정확하게 처리되지 않을 수 있습니다.

개선 방안:

if (prev == '\r' && cur == '\n') {
    byte[] raw = lineBuffer.toByteArray();
    // 마지막 \r 제거
    String line = raw.length > 0 && raw[raw.length - 1] == '\r' 
        ? new String(raw, 0, raw.length - 1) 
        : new String(raw);
    return line;
}
```"

Comment on lines 14 to 18
if(queryString==null) {
if (request.getContentType().strip().equalsIgnoreCase("application/x-www-form-urlencoded")) {
queryString = new String(request.getBody(), StandardCharsets.UTF_8);
} else throw new ErrorException("QueryParameterError: no query params"); //TODO: 이 부분을 ErrorException으로 처리해야할지, ServiceException으로 처리해야 할지 고민
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null 참조 오류 위험

request.getContentType()이 null을 반환할 수 있는데, 바로 .strip().equalsIgnoreCase()를 호출하면 NullPointerException이 발생합니다.

개선 방안:

String contentType = request.getContentType();
if(contentType != null && contentType.strip().equalsIgnoreCase("application/x-www-form-urlencoded")) {
    queryString = new String(request.getBody(), StandardCharsets.UTF_8);
}
```"

Comment on lines 24 to 25
public Optional<String> getQueryValue(String key){
return Optional.ofNullable(params.get(key).get(0));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IndexOutOfBoundsException 위험

params.get(key).get(0)은 해당 key가 없거나 리스트가 비어있을 때 예외를 발생시킵니다. 현재 parseQueryToMap은 항상 값이 추가되도록 하지만, API 설계상 명시적으로 확인하는 것이 안전합니다.

개선 방안:

public Optional<String> getQueryValue(String key){
    List<String> values = params.get(key);
    if(values == null || values.isEmpty()) return Optional.empty();
    return Optional.of(values.get(0));
}
```"

Comment on lines 51 to 69
}
}

private void readBody(BufferedInputStream in, HttpRequest request) throws IOException {
String contentLength = request.getHeader("Content-Length");
if (contentLength == null) {
return; // body 없음
}

int length;
try {
length = Integer.parseInt(contentLength.trim());
} catch (NumberFormatException e) {
throw new ErrorException("Invalid Content-Length: " + contentLength, e);
}

if (length <= 0) return;

byte[] body = new byte[length];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

body 읽기 중 예상치 못한 EOF 발생 시 정보 손실

Unexpected EOF while reading body 예외 메시지가 사용자 입력의 불완전성으로 인한 일반적인 오류인지, 서버 버그인지 구분하기 어렵습니다. 또한 읽은 바이트 수를 로깅하면 디버깅에 도움이 됩니다.

개선 방안:

if (n < 0) {
    throw new ErrorException(
        String.format("HttpRequestParseError: Unexpected EOF while reading body. Expected=%d, Got=%d", 
            length, offset), e);
}
```"

Comment on lines +39 to 44
}
public String getHeader(String key){
return headers.get(key.toLowerCase());
}

public void setHeader(String key, String value){
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Content-Type 설정 로직의 일관성 문제

setHeader에서만 Content-Type을 별도로 저장하므로, 만약 나중에 헤더를 직접 수정하면 contentType 필드와 실제 헤더 값이 불일치할 수 있습니다.

개선 방안:
getter에서 매번 headers에서 읽거나, 모든 헤더 수정을 통제하는 방식으로 통일하는 것을 권장합니다."

Comment on lines 16 to 17
queryString = new String(request.getBody(), StandardCharsets.UTF_8);
} else throw new ErrorException("QueryParameterError: no query params"); //TODO: 이 부분을 ErrorException으로 처리해야할지, ServiceException으로 처리해야 할지 고민
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요청 본문이 null일 때 NullPointerException 위험

POST 요청에서 Content-Type은 설정했으나 body가 null일 수 있습니다. request.getBody()가 null을 반환할 수 있으므로 검증이 필요합니다.

개선 방안:

byte[] body = request.getBody();
if(body == null) {
    throw new ErrorException("QueryParameterError: Content-Type is form-urlencoded but body is null");
}
queryString = new String(body, StandardCharsets.UTF_8);
```"

Comment on lines +33 to +40
private void setHeaders(BufferedReader bufferedReader, HttpRequest request) throws IOException {
String line;
while ((line = bufferedReader.readLine())!= null) {
if(line.isEmpty()) break;
int idx = line.indexOf(':');
if(idx<0) throw new ErrorException("HttpRequestHeaderParseError");
request.setHeader(line.substring(0, idx).strip(), line.substring(idx+1).strip());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일관되지 않은 에러 처리

BufferedReaderHttpRequestConverter는 body 파싱을 구현하지 않았으면서 InputStreamHttpRequestConverter는 전체 body를 파싱합니다. 두 converter 중 어느 것을 실제 사용하는지(AppConfig에서 inputStreamHttpRequestConverter()로 설정) 명확하지 않으면 향후 유지보수 시 혼동이 발생할 수 있습니다.

BufferedReaderHttpRequestConverter는 더 이상 필요 없다면 제거하거나, 명확한 용도 구분을 문서화하세요."

@codingbaraGo codingbaraGo merged commit 3718d97 into main Jan 8, 2026
1 check passed
@codingbaraGo codingbaraGo deleted the feat/http/requestbody/#34 branch January 8, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Http] - HttpRequest body parser, resolver 개발

1 participant