커피닉스, 시스템 엔지니어의 쉼터
  HTTP 1.1의 Content Length와 Transfer Encoding 작성일 : 2005/12/04 04:29
 
  • 글쓴이 : 좋은진호 ( http://coffeenix.net/ )
  • 조회수 : 42763
     
    제  목 : HTTP 1.1의 Content Length와 Transfer Encoding
    작성자 : 좋은진호(truefeel, http://coffeenix.net/ )
    작성일 : 2005.12.2~3(금~토)

    HTTP 1.1 에서는 커넥션이 끊어지지 않고 유지되는 Keep Alive connection(persistent connection)
    을 지원한다. apache 에서 httpd.conf 에 Keepalive on으로 설정하면 Keep alive connection을 지원
    하게 된다.

    웹페이지를 가져오는 프로그램을 만들 때(php에서 fsockopen을 사용하는 경우도)를 생각해보자.
    하나의 커넥션이 계속 유지된 상태라면 정확한 본문의 내용이 어디까지인지, 그리고 길이는 얼마인지
    어떻게 확인해야할까? 또한 connection close형태로 요청을 하더라도 HTTP 1.1에서는 본문내용을
    chunked enconig(이게 뭔지는 글 중간에 설명한다.) 형식으로 표시하는 경우도 있다.
    이런 것처럼 HTTP 1.1로 요청을 할 때는 Content Length헤더로 표시되는 경우와 chunked encoding
    을 사용하는 경우 등 2가지 response 형식을 잘 구분해서 처리해주어야 한다.

    1. Content Length 헤더를 보여주는 경우

     
    $ telnet coffeenix.net 80
    Trying 211.xxx.xxx.xx...
    Connected to coffeenix.net (211.xxx.xxx.xx).
    Escape character is '^]'.
    GET /truefeel/files/mail_log.pl.txt HTTP/1.1      <-- HTTP/1.1 형태로 요청
    Host: coffeenix.net
    Connection: close
                                     <-- 요청 후 접속을 바로 끊음

    HTTP/1.1 200 OK                                   <-- 여기 부터는 서버의 응답
    Date: Sat, 03 Dec 2005 15:10:17 GMT               <-- RFC 1123을 따르는 날짜 표시
    Server: Secured                                   <-- 웹서버명 ^^
    Last-Modified: Sun, 18 Jan 2004 20:26:49 GMT
    ETag: "64151-7dc-400aec09"
    Accept-Ranges: bytes
    Content-Length: 2012                              <-- contents의 길이. 2012bytes
    Connection: close
    Content-Type: text/plain

    #!/usr/bin/perl                                   <-- 여기 부터는 contents
    #
    # procmail을 통해 넘겨온 메일 수신 정보를 DB로.
    #
    ... 생략 ...
    Connection closed by foreign host.
     


    한가지 더 보자. 아래와 같이 요청하면 connection은 계속 유지된 상태로 있게 된다.

     
    $ telnet www.cyworld.nate.com 80
    Trying 211.115.11.30...
    Connected to www.cyworld.nate.com (211.115.11.30).
    Escape character is '^]'.
    GET /main2/index.htm HTTP/1.1                     <-- HTTP/1.1 형태로 요청
    Host: www.cyworld.nate.com


    HTTP/1.1 200 OK                                   <-- 여기 부터는 서버의 응답
    Cache-Control: max-age=31536000
    Content-Length: 162368                            <-- contents의 길이. 162368bytes
    Content-Type: text/html
    Last-Modified: Sat, 03 Dec 2005 14:58:50 GMT
    Accept-Ranges: bytes
    ETag: "fee1c0111af8c51:d63"
    Server: Microsoft-IIS/6.0
    Date: Sat, 03 Dec 2005 15:24:06 GMT
     


    위처럼 'Content-Length' 헤더가 있다면 body부분에서 해당 길이만큼만 받아오고, close하면 된다.


    2. Transfer Encoding 헤더 (Chunked enconding 방식)

    Chunked enconding은 php나 CGI등에서 주로 사용하며, 헤더에는 Transfer-Encoding: chunked가
    표시되고, 본문은 chunk size+내용+chunk size+내용... 형식으로 표시된다.

     
    $ telnet coffeenix.net 80
    Trying 211.xxx.xxx.xx...
    Connected to coffeenix.net (211.xxx.xxx.xx).
    Escape character is '^]'.
    GET / HTTP/1.1                                    <-- HTTP/1.1 형태로 요청
    Host: coffeenix.net


    HTTP/1.1 200 OK                                   <-- 여기 부터는 서버의 응답
    Date: Sat, 03 Dec 2005 16:29:25 GMT
    Server: Secured
    Transfer-Encoding: chunked                        <-- chunked encoding
    Content-Type: text/html

    e22                         <-- chunked 된 size. 16진수로 표시되며, bytes 단위
    ... 내용 ...                <-- 해당 e22(3618 bytes) 길이의 contents
    192                         <-- 16진수 192
    ... 내용 ...                <-- 192(402 bytes) 길이의 contents
    f76
    ... 내용 ...
    101
    ... 이하 생략 ..
    0                           <-- 끝을 표시한다
     


    보다 정확하게 보자면 chunk size+CRLF문자(CRLF는 ASCII코드 16진수로 0d0a)가 표시되고,
    내용+CRLF문자가 오는 형태가 반복되며 0+CRLF문자로 마무리를 한다.
    따라서 웹페이지 읽어올 때는 처음 chunk size를 알아낸 후 해당 길이만큼 내용을 분리하고
    다시 chunk size을 확인, 내용 분리하는 과정을 반복한 후 내용만 합치는 과정이 필요하다.

    3. php에서 Content-Length 헤더 사용

    윈도의 응용프로그램과 php로 된 웹페이지간의 통신이 필요한 경우 chunked encoding된 페이지
    보다는 Content-Length 헤더를 따르도록 하면 응용 프로그램쪽 개발이 보다 쉬워질 것이다.

    이처럼 필요한 페이지만 Content-Length 헤더가 표시하고 싶다면 php에서 출력 버퍼링을 사용하도록
    하면 된다. php 시작에 ob_start()를 넣고 마지막에 ob_end_flush()를 사용하면 그 사이에 출력되는
    모든 내용을 버퍼에 쌓아 둔 후 마지막에 출력을 하게 된다.
    물론 버퍼에 저장된 내용 길이를 파악한 후 Content-Length: 헤더를 뿌려주는 것도 잊지 말자.

     
    < ?
    ob_start();

    ... 프로그램 코드 ...
    ... HTML ...

    $length=ob_get_length();
    header("Content-Length: $length");
    ob_end_flush();
    ?>
     



    4. 참고 자료

    * Content Length & Transfer Encoding
      http://www.simtec.ltd.uk/httpgallery/chunked/

    * HTTP/1.1표준(안) (김용운 역)
      http://coffeenix.net/doc/network/http11.txt

    * php ob_start() function
      http://kr.php.net/manual/kr/function.ob-start.php


    커피닉스, 시스템 엔지니어의 쉼터 / URL : http://coffeenix.net/board_view.php?bd_code=1280