[APNs] 애플 푸쉬 서비스 개발 도중 오류 해결 (jdk8, http2, java.io.IOException: unexpected end of stream on Connection, HTTP/2 is disabled. Is alpn-boot on the boot class path?)
1. 배경
- 회사 모바일푸시 서비스는 FCM토큰방식으로 푸쉬를 발송함.
- 중국 사용자가 google도메인이 막혀 FCM토큰 값을 가져올 수 없는 상황 발생.
- 아이폰 사용자를 위한 APNs푸쉬 서비스를 따로 개발하기로 함.
2. 개발 스택
- spring + apache/tomcat8.5
- 사용 라이브러리 apns-httpd2
3. 오류 발생 배경
- 라이브러리에서 제공하는 비동기 방식으로 처리 후 로컬에서 발송 시 제대로 발송되는 것을 확인하고 개발서버에 반영.
- 개발서버에서 푸쉬가 발송되지 않는 현상 발생.
에러 로그는 아래와 같았다.
APNS FAIL : token : E4DADD3C7D83145C5EDF4B93137A8B8D9AEC2F1F280735128724C4188ADEB65E NotificationResponse{error=null, httpStatusCode=-1, responseBody='null', cause=java.io.IOException: unexpected end of stream on Connection{api.push.apple.com:443, proxy=DIRECT hostAddress=api.push.apple.com/17.188.138.185:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 protocol=http/1.1}}
java.io.IOException: unexpected end of stream on Connection{api.push.apple.com:443, proxy=DIRECT hostAddress=api.push.apple.com/17.188.138.185:443 cipherSuite=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 protocol=http/1.1}
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:205)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:147)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.EOFException: \n not found: limit=159 content=000018040000000000000100001000000300000001000500004000000600001f…
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:212)
at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
... 19 more
????
4. 오류 해결
- 바로 github issue가서 같은 오류가 난 사용자가 있나 찾아봤다.
라이브러리 설명을 제대로 읽지 않았다.
APNs는 HTTP2를 사용하기 때문에 HTTP2 가지원되어야 한다.
jdk8을 사용하기 때문에 http2를 지원하지 않아 jetty라이브러리가 추가로 필요했던 것.
java 버전을 올리는 것 보다 일단 http2를 사용하게 해주는 ALPN을 추가하기로 함.
1) maven을 사용했기에 pom에 의존성을 추가했다.
<dependency>
<groupId>org.mortbay.jetty.alpn</groupId>
<artifactId>alpn-boot</artifactId>
<version>8.1.12.v20180117</version>
</dependency>
자신의 jdk 버전과 맞는 버전은 아래의 url참고.
https://github.com/jetty-project/jetty-alpn/blob/master/docs/version_mapping.properties
2) java실행 구문에 아래와 같은 jvm옵션 추가.
-Xbootclasspath/p:{/mobilePush/WEB-INF/lib/alpn-boot-8.1.12.v20180117.jar}
({}안은 jar파일의 절대경로 추가.)
이러고 서비스를 재시작하면 APNs푸쉬가 제대로 가는 것을 확인할 수 있다.(포트는 기본 443이므로 열려있어야 함.)
+ 추가사항
- 라이브러리를 추가했는데도 아래와 같은 오류가 날 수도 있음.
org.apache.http.impl.conn.PoolingHttpClientConnectionManager [DEBUG] Connection manager shut down
09-Nov-2021 11:05:42.775 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:43.817 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:44.862 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:45.904 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:46.944 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:47.988 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:49.012 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
09-Nov-2021 11:05:50.051 INFO [OkHttp https://api.push.apple.com/...] okhttp3.internal.platform.Platform.log ALPN callback dropped: HTTP/2 is disabled. Is alpn-boot on the boot class path?
침착하게
1. jdk버전과 ALPN버전이 맞나 확인
2. jvm옵션을 제대로 적용했는지 확인
하면 된다.
끝.