Notice
Recent Posts
Recent Comments
Link
«   2024/03   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

pungjoo

Transfer-Encoding: chunked VS Content-Length 본문

HTTP

Transfer-Encoding: chunked VS Content-Length

pungjoo.kim 2009. 3. 9. 09:42

0. 들어가면서

Network에서는 전송하고자 하는 콘텐츠(content, 또는 data) 길이를 헤더에 기술하던가 콘텐츠의 끝이라고 서로간에 약속한 데이터를 마지막에 기술하던가 이도 저도 아니면 open된 stream(socket 포함)을 close를 할때 전송의 끝이라고 인식하게 됩니다.

영화를 보면 무전기를 들어 자신이 할 말이 끝났을 때 항상 끝에 '오바(over)'라고 해 자신이 할 말이 끝났음을 상대방에게 알려 줍니다. 이는 통신 규칙입니다.


1.  Content-Length

Content-Length는 응답(response)의 header에 정의 되는 것으로 요청한 내용에 대한 실제적인 결과인 body의 길이가 몇 bytes인가를 의미합니다. 클라이언트(통상 브라우져)는 헤더(header)에 정의된 Content-Length 만큼을 InputStream에서 읽게 됩니다.

[pungjoo@yeonwoo] $ cat Normal.java

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
           IOException {

     PrintWriter out = response.getWriter();

     try {
          out.print( "data1");
          out.print( "data2");
          out.println();
     } catch (Exception e) {
     } finally {
          if (out != null) {
               try {
                    out.close();
               } catch (Exception e) {
               }
          }
     }
}

[pungjoo@yeonwoo] $

위와 같은 /normal servlet을 호출해 보겠습니다.
참고로 keep-alive의 설정이 서버측면에 되어 있어서 keep-alive를 사용되지 않게 'Connection: close'를 추가했습니다. 이 부분은 HTTP/1.1 부분을 HTTP/1.0으로 하면 동일한 효과를 얻게 됩니다.

[pungjoo@yeonwoo /work/home/pungjoo]$ telnet dev.pungjoo.com 80
Trying 121.124.124.74...
Connected to dev.pungjoo.com.
Escape character is '^]'.
GET /normal HTTP/1.1
Host: dev.pungjoo.com
Connection: close

HTTP/1.1 200 OK
Date: Mon, 09 Mar 2009 03:19:40 GMT
Content-Type: text/plain
Content-Length: 12
Connection: close

data1data2
Connection closed by foreign host.
[pungjoo@yeonwoo /work/home/pungjoo]$

녹색으로 표기된 부분은 직접 입력을 한 부분이고 붉은 색으로 표기된 부분이 응답을 받은 내용중에 Content-Length에 의미를 두는 부분입니다.

클라이언트는 응답 헤더 중에 Content-Length를 통해 Body부분이 12byte로 이루어졌구나 판단 후 Body를 읽을때 InputStream에 12bytes만 읽으라고 정의를 하게 됩니다. 또한 서비스 제공하는 곳에서 첨부파일이 존재할 때 다운로드를 클릭하면 정확히 몇 바이트인 파일을 다운로드 한다고 기술되는 반면 어떤 서비스를 제공하는 곳에서는 '알 수 없는 ..' 이런 류를 보시게 됩니다. 이는 'Content-Length'가 응답 헤더에 있는냐 없느냐에 따라서 구별됩니다.
 
이상한 것은 길이가 12bytes이고 실제 'data1data2'는 총10bytes이므로 2byte의 오차가 생기는데 이는 눈에 보이지 않는 개행문자로 '\r\n'을 의미하게 됩니다.


2. Transfer-Encoding: chunked

초창기와 비교해 보면 요즘은 정적인 콘텐츠는 거의 없는 실정입니다. 비단 이미지 같은 바이너리도 동적으로 구성되어 제공되는 것이 현실입니다. 서버측에서 서비스 단위로 끊어서 로직을 진행하다 보니 모든 작업이 종료될 때까지 클라이언트는 멍하니 '흰 색'으로 채워진 내용을 보다 순간 '짠..'하고 화면에 나타나는 경우가 있게 됩니다. 이런 부분은 여러가지 요소의 영향으로 발생하는 현상이지만 순수히 Text인 Body(html)의 경우도 심심치 않게 발생하게 됩니다. 따라서 처리된 부분까지라도 그때 그때 클라이언트에 전송해 주면 클라이언트 입장에서 답답함은 조금 사라질 수 있을 것 입니다. (단, 처리되는 순서가 중요할 수 있습니다. )

[pungjoo@yeonwoo] $ cat Chunked.java

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
           IOException {

     PrintWriter out = response.getWriter();

     try {
          out.print("data1");
          out.flush(); // /normal servlet과 다르게 추가된 부분.
          out.print("data2");
          out.println();
          out.flush(); // /normal servlet과 다르게 추가된 부분.
     } catch (Exception e) {
     } finally {
          if (out != null) {
               try {
                    out.close();
               } catch (Exception e) {
               }
          }
     }
}

[pungjoo@yeonwoo] $

위와 같은 /chunked servlet을 호출해 보겠습니다. /normal servlet과의 차이는 out.flush(); 입니다.

[pungjoo@yeonwoo /work/home/pungjoo]$ telnet dev.pungjoo.com 80
Trying 121.124.124.74...
Connected to dev.pungjoo.com.
Escape character is '^]'.
GET /chunked HTTP/1.1
Host: dev.pungjoo.com
Connection: close

HTTP/1.1 200 OK
Date: Mon, 09 Mar 2009 05:47:21 GMT
Connection: close
Transfer-Encoding: chunked
Content-Type: text/plain

5
data1
7
data2

0

Connection closed by foreign host.
[pungjoo@yeonwoo /work/home/pungjoo]$

붉은 색으로 표기된 'Transfer-Encoding: chunked' 부분이 헤더에 추가되어 전송이 되면 클라이언트는 처음에 '5'를 읽고 '아..5 bytes. 전송하겠구나'라고 판단 5바이트를 읽고 대기(?)를 하다 '7'을 읽고 '5'와 동일하게 처리하는 것을 반복하다 '0'이 오면 더 이상 전송 받을 것이 없다고 판단하고 스트림 읽기를 중단합니다.

그런데 중요한 것이 web application server는 대체적으로 output-stream에 buffer를 둡니다. 따라서 flush를 한다고 해서 바로 클라이언트로 데이터가 전송되는 것이 아니라 buffer가 정의된 만큼 가득 차거나 더이상 전송할 것이 없을 경우 클라이언트에 데이터가 전송되므로 실질적인 앞서 설명된 효과를 얻기는 어렵습니다.

단, jsp/servlet에서 통상적으로 output을 response.getWriter()를 사용하기 때문에 설명된 것 처럼 작동(?)하지 않는 것이지 response.getOutputStream()를 사용하면 설명된 효과를 얻게 됩니다.


@
Comments