2014-07-17

Spring Framework Error Handling: Accept 헤더와 무한루프

이거 때문에 그나마 없던 생활 리듬마저 개박살 나고 말았다아아아아아아~~~~


Spring Framework에는 당연히 에러 처리기가 있고, 적절한 Exception을 적절한 에러 코드로 변환해서 클라이언트에 던진다.
그리고 Spring Boot에는 기본 Error 처리 컨트롤러가 있어서, 적절하게 html 또는 json으로 에러 메세지를 뿌려준다.

그런데, 여기서 모든 문제가 시작된다!

Spring Boot를 사용하는 프로젝트를 하나 만들고, 적절한 Servlet Container Tomcat이라거나 Jetty라거나 를 사용해서 띄워본다.
그리고 (리눅스의 경우) 터미널을 열고 아래 명령을 때려봐라.

HTTP일 경우
curl -H "Accept: application/octet-stream" http://localhost:8080/<context-path>/error

HTTPS일 경우
curl -k -H "Accept: application/octet-stream" https://localhost:8443/<context-path>/error

CPU가 미친듯이 돌아가면서 StackOverflowException을 떨굴 것이다.
지옥에 입장한 것을 축하한다.

원인은 Exception에서 볼 수 있듯이 무한루프인데, Accept에 다른거 text/html이나 application/json 같은거 를 넘기면 정상 동작하는 것을 봐서는 에러 처리 로직에 문제가 있는 것이다.
그래서 디버그를 돌려본 결과...

일단 Spring Boot의 BasicErrorController는 정상적으로 탄다.
그리고 그 결과를 JSON으로 변환하는데, 문제는 Jackson은 application/octet-stream 따원 모른다네.
그래서 Jackson은 난 이런거 모른다네! 하고 에러를 던지고, Spring 에러 핸들러는 이걸 HTTP 응답 코드 406으로 변환한다.

여기까지는 좋았지.
Request에 Accept가 그대로 남아있는고로...

Request의 Accept는 끝이 없고
같은 무한루프를 반복한다.



그래서 이걸 어떻게 해결하느냐고?
안알랴줌.






이면 이 글을 안썼겠지.

Filter를 하나 추가해서 406 응답코드를 인식하면 Request를 Wrapper로 감싼다.
Wrapper는 Accept 헤더의 값을 요청할 경우 null을 떨구면 된다.

참 쉽죠?


p.s
Spring Framework로 생각하고 있었는데 spring-boot의 문제였고, 1.1.5에서 수정되었다.
(https://github.com/spring-projects/spring-boot/issues/1257)
Share: