Translate

2015년 6월 26일 금요일

[Android] java.net.ProtocolException: unexpected end of stream 에러문제



증상

이미지를 다운로드 시 1분이상 로직이 진행되지 않고 멈추다가 아래와 같은 로그가 발생하며 다음 로직으로 넘어간다.


Source
URL url = new URL(imageDownUrl);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();

conn.setConnectTimeout(5000);
conn.setUseCaches(false);
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){
 File file = new File(getCacheDir()/*Environment.getExternalStorageDirectory().getPath()*/, imageName);
 FileOutputStream fileOutput = new FileOutputStream(file);
 InputStream inputStream = conn.getInputStream();
 byte[] buffer = new byte[4096];
 int bufferLength = 0; 

 while ((bufferLength = inputStream.read(buffer)) > 0 ) {
  fileOutput.write(buffer, 0, bufferLength);
 }
 fileOutput.close();
}



Log
06-24 12:14:38.367 17879 17920 W System.err: java.net.ProtocolException: unexpected end of stream
06-24 12:14:38.372 17879 17920 W System.err: at com.android.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:472)
06-24 12:14:38.372 17879 17920 W System.err: at com.android.okio.RealBufferedSource$1.read(RealBufferedSource.java:174)
06-24 12:14:38.372 17879 17920 W System.err: at java.io.InputStream.read(InputStream.java:162)
06-24 12:14:38.372 17879 17920 W System.err: at com.?????$9.run(?????.java:547)
06-24 12:14:38.372 17879 17920 W System.err: at java.lang.Thread.run(Thread.java:818)
06-24 12:14:38.377 17879 17879 I Timeline: Timeline: Activity_launch_request id:com.????? time:1022021








원인 및 분석

관련 이슈를 찾아보니 비슷한 이슈가 있었다.

https://github.com/square/okhttp/issues/1490
(전직 구글 HTTP 마스터이자 현직 OkHTTP 개발자인 Jesse Wilson가 OkHTTP 이슈에 올라온 글에 대해서 답변을 한 글이다.)

내용 중간에 보면
java.net.ProtocolException: unexpected end of stream
라는 Exception 발생 부분에 대해서 이야기를 한다.

URL: https://github.com/square/okhttp/blob/96dfaaa817816b2dc191d4d075a07aeb0c808694/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpConnection.java#L382

Source
@Override public long read(Buffer sink, long byteCount) throws IOException {
  if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount);
  if (closed) throw new IllegalStateException("closed");
  if (bytesRemaining == 0) return -1;

  long read = source.read(sink, Math.min(bytesRemaining, byteCount));
  if (read == -1) {
    unexpectedEndOfInput(); // The server didn't supply the promised content length.
    throw new ProtocolException("unexpected end of stream");
  }

  bytesRemaining -= read;
  if (bytesRemaining == 0) {
    endOfInput(true);
  }
  return read;
}


내용을 보면 서버의 Response header에 기술된 Content-Length 만큼 Body를 보내주지 않아서 오류가 발생을 했다고 한다.




위의 내용으로 보아 처음에는 다음과 같이 추측을 했다.
1. Server(Nginx) 의 문제
2. Server에 연결된 Network 문제 (보안관련 솔루션 의심)
3. 비정상적인 이미지



결국 서버 부분은 다음과 같이 원인분석 및 해결을 했다.
- [Nginx] net::ERR_CONTENT_LENGTH_MISMATCH 에러
  (http://1004lucifer.blogspot.kr/2015/06/nginx-neterrcontentlengthmismatch.html)



[리뷰][서적] 소프트웨어, 누가 이렇게 개떡같이 만든 거야 - 사용자의 관점에서 많이 생각하게 만드는 서적..



소프트웨어, 누가 이렇게 개떡같이 만든 거야 - 8점
데이비드 플랫 지음, 윤성준 옮김/인사이트


첫 회사를 퇴사 할 때 같은 팀의 디자이너 동료에게 선물받은 책을 최근에서야 제대로 읽기 시작하여 다 읽어 보았다.

다른 컴퓨터 서적을 볼 것도 많아서 영어도 공부 못하는데 당장 도움되지 않는 이런 걸 언제 읽어야 하나.. 싶었는데..
최근 출퇴근 시간에 짬짬히 다 읽고나서야 '아.. 정말 좋은 책을 선물해 주셨었구나..' 하고 다시한번 감사하게 된다.


이 책에 맞는 독자는 다음과 같다.
1. 프로그래머
2. 디자이너
3. 프로그램&디자인을 보고 빡친 사용자

프로그램이나 디자인을 하나도 모르더라도 책을 보는데 아무 지장이 없으며 평소 프로그램&디자인을 보고 빡이 친 사람이라면 공감을 하며 재미있게 읽을 수 있을 듯 하다.


책을 읽으며 정말 공감이 되었던 부분은 대부분의 개발자들이 자신의 기준에서 프로그램을 제작한다는 내용이었다.

SI와 같이 요구사항이 있고 그것에 따라 개발하는게 아니라
솔루션&서비스를 만들어야 하는 상황에서 기획자는 기획을 할 때 너무 당연한 사항이나 사소한 부분은 빠뜨릴 수 도 있다.
어느정도는 개발자가 상식선에서 작업을 해야 하는 경우가 발생하기도 하는데 문제는 그 상식 이라는게 사람마다 조금씩은 차이가 있다는 것이다.

개발자가 개발을 할 당시 '나라면 이렇게 보여주길 원할꺼야' 라던지..
'이렇게 만들면 사용하기 편리하겠지?' 라고 나름대로 생각 할 수 있지만
(시간없으니 작동만 하게 빨리 만들자 라는 사람도 종종 있지만..;;)

실제로 사용자가 정말 편리하다고 느낄 수 있게 만드는 것은 무척이나 어려운 문제다.
개발자&디자이너 모두 깊이 고민해보고 다른사람에게 조언도 구해보며 UX에 대해 점점 스킬을 늘려야 하지 않을까 싶다.
(기획자 또한 처음부터 기획을 잘 한게 아니라 꾸준한 노력을 통해 이루어 낸 것이라 생각한다.)



책을 읽고 한번씩 자기반성을 해보며 조금 더 사용자에게 이로운 프로그램&디자인을 만들 수 있게 이 책이 조금은 도움이 될 것 같다.


[Nginx] net::ERR_CONTENT_LENGTH_MISMATCH 에러



서버에 리소스를 요청 시 정상적으로 Response를 할 수 없는 이슈가 있어서 확인을 해봤다.



증상

1. WebServer(Nginx) 에 리소스를 요청 시 일단 Response Headers 를 정상적으로 받아온다.





2. 본문은 계속 받아오는 중.. (CAUTION: request is not finished yet!)





3. 약 1분 정도 시간이 지나면 아래와 같이 오류가 발생하며 이미지의 일부분만 다운받은채 Response 가 종료된다.
(net::ERR_CONTENT_LENGTH_MISMATCH 메시지 출력됨)







원인분석


1. WebServer 요청량(Request) 대비 동시처리 가능한 임계치 설정 확인

서버에 너무 많은 Request가 몰렸을 경우에 WebServer 에서 동시에 처리 가능한 임계치 설정 확인 및 필요하다면 늘려 주어야 한다.

Nginx 프로세스에서 처리하고 있는 커넥션 갯수가 많지 않음을 알 수 있다.
(따로 처리가 필요하지 않음)

[root@WebServer1 ~]# ps -ef | grep nginx
root      4634  4542  0 14:03 pts/2    00:00:00 grep nginx
root     16272     1  0 Feb13 ?        00:00:00 nginx: master process ./nginx
nobody   16273 16272  0 Feb13 ?        00:34:15 nginx: worker process
nobody   16274 16272  0 Feb13 ?        00:34:34 nginx: worker process
nobody   16275 16272  0 Feb13 ?        00:34:37 nginx: worker process
nobody   16276 16272  0 Feb13 ?        00:34:17 nginx: worker process
nobody   16277 16272  0 Feb13 ?        00:34:18 nginx: worker process
nobody   16278 16272  0 Feb13 ?        00:34:18 nginx: worker process
nobody   16279 16272  0 Feb13 ?        00:34:55 nginx: worker process
nobody   16280 16272  0 Feb13 ?        00:34:31 nginx: worker process
[root@WebServer1 ~]# 
[root@WebServer1 ~]# cd /proc/4616272
[root@WebServer1 16272]# ls -l
 0
dr-xr-xr-x 2 root sys  0  6 26 14:04 attr
-r-------- 1 root root 0  6 26 14:04 auxv
-r--r--r-- 1 root root 0  5 26 07:50 cmdline
-rw-r--r-- 1 root root 0  6 26 14:04 coredump_filter
-r--r--r-- 1 root root 0  6 26 14:04 cpuset
lrwxrwxrwx 1 root root 0  6 26 14:04 cwd -> /app/nginx-1.2.8/sbin
-r-------- 1 root root 0  6 26 14:04 environ
lrwxrwxrwx 1 root root 0  5 26 07:53 exe -> /app/nginx-1.2.8/sbin/nginx
dr-x------ 2 root root 0  6 26 14:04 fd
dr-x------ 2 root root 0  6 26 14:04 fdinfo
-r-------- 1 root root 0  6 26 14:04 io
-r--r--r-- 1 root root 0  6 26 14:04 limits
-rw-r--r-- 1 root root 0  6 26 14:04 loginuid
-r--r--r-- 1 root root 0  6 26 14:04 maps
-rw------- 1 root root 0  6 26 14:04 mem
-r--r--r-- 1 root root 0  6 26 14:04 mounts
-r-------- 1 root root 0  6 26 14:04 mountstats
-r--r--r-- 1 root root 0  6 26 14:04 numa_maps
-rw-r--r-- 1 root root 0  6 26 14:04 oom_adj
-r--r--r-- 1 root root 0  6 26 14:04 oom_score
lrwxrwxrwx 1 root root 0  6 26 14:04 root -> /
-r--r--r-- 1 root root 0  6 26 14:04 schedstat
-r--r--r-- 1 root root 0  6 26 14:04 smaps
-r--r--r-- 1 root root 0  5 25 19:49 stat
-r--r--r-- 1 root root 0  5 25 19:49 statm
-r--r--r-- 1 root root 0  5 31 08:50 status
dr-xr-xr-x 3 root sys  0  6 26 14:04 task
-r--r--r-- 1 root root 0  5 26 07:53 wchan
[root@WebServer1 16272]# 
[root@WebServer1 16272]# cd fd
[root@WebServer1 fd]# ls -l
 0
lrwx------ 1 root root 64  6 26 14:04 0 -> /dev/null
lrwx------ 1 root root 64  6 26 14:04 1 -> /dev/null
lrwx------ 1 root root 64  6 26 14:04 10 -> socket:[58331]
lrwx------ 1 root root 64  6 26 14:04 11 -> socket:[58333]
lrwx------ 1 root root 64  6 26 14:04 12 -> socket:[58334]
lrwx------ 1 root root 64  6 26 14:04 13 -> socket:[58336]
lrwx------ 1 root root 64  6 26 14:04 14 -> socket:[58337]
lrwx------ 1 root root 64  6 26 14:04 15 -> socket:[58338]
lrwx------ 1 root root 64  6 26 14:04 16 -> socket:[58339]
lrwx------ 1 root root 64  6 26 14:04 17 -> socket:[58342]
lrwx------ 1 root root 64  6 26 14:04 18 -> socket:[58343]
lrwx------ 1 root root 64  6 26 14:04 19 -> socket:[58345]
l-wx------ 1 root root 64  6 26 14:04 2 -> /logs/nginx/error.log
lrwx------ 1 root root 64  6 26 14:04 20 -> socket:[58346]
lrwx------ 1 root root 64  6 26 14:04 21 -> socket:[58348]
lrwx------ 1 root root 64  6 26 14:04 22 -> socket:[58349]
lrwx------ 1 root root 64  6 26 14:04 3 -> socket:[58327]
l-wx------ 1 root root 64  6 26 14:04 4 -> /logs/nginx/error.log
lr-x------ 1 root root 64  6 26 14:04 5 -> /usr/src/pam/shadow/root (deleted)
l-wx------ 1 root root 64  6 26 14:04 6 -> /logs/nginx/access.log
lrwx------ 1 root root 64  6 26 14:04 7 -> socket:[58324]
lrwx------ 1 root root 64  6 26 14:04 8 -> socket:[58328]
lrwx------ 1 root root 64  6 26 14:04 9 -> socket:[58330]
[root@WebServer1 fd]# 
[root@WebServer1 fd]# ls -l | wc -l
24
[root@WebServer1 fd]# 




2. Nginx 의 Error 로그 확인

Error Log 확인 시 아래와 같이 출력이 되어 디스크 용량을 확인했다.
루트(/)영역의 사용가능용량이 없는 것을 확인했다.

[root@WebServer1 fd]# tail -f /logs/nginx/error.log
2015/06/26 14:04:44 [crit] 16279#0: *74928418 writev() "/app/nginx-1.2.8/proxy_temp/3/04/0011857043" failed (28: No space left on device) while reading upstream, client: ???.???.???.???, server: localhost, request: "GET /web/app/images/1415324940039_5228.png HTTP/1.1", upstream: "http://127.0.0.1:8080/web/app/images/1415324940039_5228.png", host: "example.co.kr"
2015/06/26 14:04:48 [crit] 16279#0: *74928441 writev() "/app/nginx-1.2.8/proxy_temp/8/04/0011857048" failed (28: No space left on device) while reading upstream, client: ???.???.???.???, server: localhost, request: "GET /web/????.jpg HTTP/1.1", upstream: "http://127.0.0.1:8080/web/????.jpg", host: "example.co.kr", referrer: "http://example.co.kr/web/????/index.html"

[root@WebServer1 fd]# 
[root@WebServer1 fd]# df -k
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda3             10154020   9757312         0 100% /
/dev/sda8             10153988    685052   8944820   8% /home
/dev/sda7             10153988    538692   9091180   6% /var
/dev/sda6             10153988    154240   9475632   2% /var/crash
/dev/sda5             10153988   4997928   4631944  52% /usr
/dev/sda2             20315844    176244  19090960   1% /osbackup
/dev/sda1              1019208     44724    921876   5% /boot
tmpfs                  6084024         0   6084024   0% /dev/shm
/dev/mapper/vg00-log  20642428    176200  19417652   1% /log

[root@WebServer1 fd]# 






해결

루트(/) 영역의 중요하지 않은 파일 삭제
(Nginx 재기동 없이 서비스가 정상화 되었다.)



참조
http://lfender6445.github.io/application-net-err-content-length-mismatch/
http://stackoverflow.com/questions/22183859/javascript-err-content-length-mismatch


2015년 6월 16일 화요일

[Android] Lollipop 에서의 FRP (Factory Reset Protection) 기능에 대하여..



Android 유저인 내가 정말 좋아할만한 소식을 접했다.

Android Lollipop (Android 5.1 or higher) 이상에서는 구글의 Kill Switch 솔루션 FRP (Factory Reset Protection)가 들어가서 분실시에 디바이스를 초기화하여 사용하는 것을 방지해 준다고 한다.
https://support.google.com/nexus/answer/6172890?hl=en

Kill Switch 기능은 iOS 에서도 기능이 탑재되어 있었으며 Android 에서는 최근에 기본탑제가 된듯 하다.


음주 후 두세번 정도의 핸드폰 분실로 인해 속이 상했던터라 이번 기능에 대해서는 나를위한 기술이 나왔구나 싶었다.



사용방법
구글계정의 입력과 동시에 동작을 하기에 그냥 구글계정에 로그인하면 된다고 한다.


단말기를 공장초기화 하더라도 기존 입력했던 구글계정의 정보를 다시 입력하지 않으면 사용을 할 수 없다고 한다. (삼성/엘지와 같은 제조사에서도 복구 불가능)




PS.
핸드폰을 중고로 거래시 기존에는 통신사에 특정번호와 등록되어있는지의 여부를 확인했어야 하는데 앞으로는 기존에 구글계정이 로그인되어있는지까지 확인이 필요하겠다.


2015년 6월 5일 금요일

[Android][Settings] 설정에서 홈버튼의 비활성화 이슈




최근 이슈가 된 문제중에 어떠한 액션을 하게되면 "설정=>접근성"에서 하단 소프트키 버튼을 비활성화 시켜버리는 이슈가 있었다.



증상

설정 => 접근성 진입 시 아래와 같이 보여지는 문제가 있었다.

 




원인

해당 Andorid 디바이스의 Accessibility Activity 에서의 로직에서 해당 버튼을 비활성화 시켜버렸다.

만일 해당 SoftKey를 활성화 시키려면 아래와 같이 로직을 분석하여 비활성화 되지 않는 조건으로 만들어야 한다.





분석


1. 해당 Accessibility Activity를 알아보기 위해 apk를 가져온다.



Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.

C:\Users\1004lucifer>cd Downloads

C:\Users\1004lucifer\Downloads>cd LG

C:\Users\1004lucifer\Downloads\LG>
C:\Users\1004lucifer\Downloads\LG>adb shell
shell@g2:/ $

shell@g2:/ $ pm list packages -f | grep sett
pm list packages -f | grep sett
package:/system/priv-app/LGSettings.apk=com.android.settings
package:/system/priv-app/LGSettingsProvider.apk=com.android.providers.settings
package:/system/priv-app/LGSettingsAccessibility.apk=com.android.settingsaccessibility
package:/system/app/LGLockScreenSettings.apk=com.lge.lockscreensettings
package:/system/app/LGEasySettings.apk=com.lge.settings.easy
package:/system/priv-app/LGRoamingSettingsKr.apk=com.lge.roamingsettings
shell@g2:/ $

shell@g2:/ $ exit
exit

C:\Users\1004lucifer\Downloads\LG>adb pull /system/priv-app/LGSettingsAccessibility.apk
3529 KB/s (4351877 bytes in 1.204s)

C:\Users\1004lucifer\Downloads\LG>





2. 가져온 apk를 Decompile 하여 Source를 추출한다.

- Decompile 하는 방법은 인터넷 찾아보면 쉽게 방법을 알아낼 수 있다.




3. Log와 Source를 확인하여 Activity를 찾아 분석한다.

내가 테스트한 LG단말에서는 SettingsAccessibilityActivity.java 가 해당 Activity 였다.


  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    Intent localIntent1 = new Intent("com.lge.appbox.commonservice.update");
    localIntent1.putExtra("packagename", getPackageName());
    localIntent1.putExtra("type", "update");
    startService(localIntent1);
    ActionBar localActionBar = getActionBar();
    localActionBar.setDisplayOptions(15);
    Intent localIntent2 = getIntent();
    if (localIntent2 == null)
    {
      this.mFragmentInActivityStatus = 1;
      return;
    }
    Bundle localBundle = localIntent2.getExtras();
    if (localBundle != null)
    {
      String str = localBundle.getString("where_are_you_from");
      if ((str != null) && ("from_startup_wizard".equals(str)))
      {
        this.mFragmentInActivityStatus = 2;
        addFragment();
        localActionBar.setHomeButtonEnabled(false);
        localActionBar.setDisplayHomeAsUpEnabled(false);
        return;
      }
      this.mFragmentInActivityStatus = 1;
      return;
    }
    this.mFragmentInActivityStatus = 0;
    addFragment();
  }





PS.
주의사항
1. 단말기 제조사에 따라 apk나 수행하는 Activity Class 이름이 다를 수 있다.
2. 같은 Android version 이라도 Build version 이 다르면 Source가 다를 수 있다.