상황
- 클라이언트로부터 상품 수신자 리스트 데이터를 받아 처리하는데, 수신자가 일정 수를 넘어가면 제대로 처리되지 않고 애플리케이션에서 예외 발생함
로그 추적
1. 아파치 로그
1
Connection reset by peer : ... AH01084: pass request body failed to ...
2. 애플리케이션 로그
- 컨트롤러에 파라미터가 null로 넘어옴. 이로 인해 유효성 검사에서 예외 처리됨
얻은 힌트
- Connection reset by peer (Remote Sever, 즉 톰캣 서버에서 RST 패킷 보내서 커넥션 끊김)
- pass request body failed
- request body가 제대로 전달되지 못했다. (이로 인해 파라미터가
null
로 넘어온 것 같다.)
- request body가 제대로 전달되지 못했다. (이로 인해 파라미터가
- 추측 : 톰캣쪽에서 뭔가 문제가 발생하는 것 같다.
원인 파악
(Spring Boot 내장) 톰캣 관련 설정을 살펴보자
1
2
3
4
5
6
7
8
9
server:
port: 포트번호
tomcat:
uri-encoding: UTF-8
connection-timeout: ...
min-spare-threads: ...
max-threads: ...
max-http-post-size: 3145728
max-connections: ...
왠지
max-http-post-size
가 연관있는 설정일 것 같다. 그런데, post size라는게 정확히 어떤 것의 크기를 의미하는걸까 ? (request header를 포함한 전체 사이즈?)
- 톰캣 공식문서에서는
maxPostSize
속성을 다음과 같이 정의1 2 3 4
The maximum size in bytes of the POST which will be handled by the container FORM URL parameter parsing. The limit can be disabled by setting this attribute to a value less than zero. If not specified, this attribute is set to 2097152 (2 megabytes). Note that the FailedRequestFilter can be used to reject requests that exceed this limit.
- 소스를 찾아보면
Content-Length
(Request Body의 크기)라는 것을 알 수 있다.- 내장 톰캣 라이브러리(
tomcat-embed-core-8.5.29.jar
)의org.apache.catalina.connector.Request#parseParameters
- 내장 톰캣 라이브러리(
- 또한,
Content-Type
이multipart/form-data
이거나application/x-www-form-urlencoded
인 경우에만 사이즈를 제한하는 로직을 타게된다.- 확인 결과, 수신자 정보 세팅 요청의
Content-Type
은application/x-www-form-urlencoded
이었음
- 확인 결과, 수신자 정보 세팅 요청의
- (이름에서 유추할 수 있듯이)
POST
요청인 경우에만 제한된다.org.springframework.web.filter.HiddenHttpMethodFilter#doFilterInternal
cf) 내장 톰캣 아닌 경우 (server.xml
에 maxPostSize
지정)
1
<Connector port="8080" protocol="HTTP/1.1" redirectPort="8443" maxPostSize="-1"/>
문제 해결
1. 수신자 정보 데이터 송신 구조 변경
불필요하게 중복되는 데이터 제거하여 RequestBody 크기 작게 만들기 (아래에서는 설명을 위해 json으로 표시. 실제로는
x-www-form-urlencoded
, 즉 key-value 형태로 넘어감)
- before : 동일한 발신메세지 중복 (상품 발송시 발신메세지는 모두 동일)
1 2 3 4 5 6 7 8
{ "상품코드" : ... , "송신자이름" : ... , "수신자목록" : [ {"예약일시" : ... , "휴대폰번호" : ... , "발신메세지" : ...}, {"예약일시" : ... , "휴대폰번호" : ... , "발신메세지" : ...}, ] }
- after : 발신메세지 항목 따로 분리
1 2 3 4 5 6 7 8 9
{ "상품코드" : ... , "송신자이름" : ... , "발신메세지" : ... , "수신자목록" : [ {"예약일시" : ... , "휴대폰번호" : ... }, {"예약일시" : ... , "휴대폰번호" : ... }, ] }
2. max-http-post-size 변경
- 개선된 송신 구조 기준으로 10000건 송신시 약 3.8MB인 것을 감안하여 4194304(bytes, 4MB)로 변경
알게된 것
form
태그에enctype
따로 지정하지 않으면, form submit시 default content-type은x-www-form-urlencoded
- enctype 목록
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- enctype 목록
POST 요청이면서
Content-Type
이x-www-form-urlencoded
또는multipart/form-data
인 경우에만http-max-post-size
옵션이 적용됨 (json은 해당되지 않음)- spring-boot의
max-http-post-size
속성은 2.1.x 버전에서부터 Deprecated 되었음 (max-http-form-post-size
로 변경)- 제기됐던 이슈
- html form 요청과 관련해서 적용되는건데
http-max-post-size
는 의미가 명확하지 않다.
- html form 요청과 관련해서 적용되는건데
- 2.0.x 버전 Document
- 2.1.x 버전 Document
- 제기됐던 이슈
cf) 운영하는 서비스는 spring-boot v2.0.3
사용중이었음