본문 바로가기
books/HTTP 완벽가이드

[HTTP 완벽가이드 15장] 엔터티와 인코딩

by Moonsc 2020. 5. 24.
728x90

4부.HTTP : 엔터티, 인코딩, 국제화


 

15장. 엔터티와 인코딩

HTTP는 매일 수십억 개의 미디어 객체를 실어 나른다. 이미지, 텍스트, 동영상, SW... 당신이 이름을 붙이면, HTTP는 실어 나른다.
HTTP는 또한 메시지가 올바르게 수송되고, 식별되고, 추출되고, 처리되는 것을 보장한다.

 

15.1 메시지는 컨테이너, 엔티티는 화물

  • HTTP 메시지를 인터넷 운송 시스템의 컨테이너라고 생각한다면, HTTP 엔터티는 메시지의 실질적인 화물이다.
                            --- 엔터티 시작
Content-type: text/plain
Content-length: 18          --- 엔터티의 헤더
(CRLF)
Hi! I`m a message!          --- 엔터티의 본문
  • 위의 빈 줄(CRLF)은 헤더 필드와 본문의 시작을 나눈다.

  • HTTP 엔터티 헤더는 HTTP 메시지의 내용물을 설명한다.

  • HTTP/1.1의 주요 헤더 필드

    • Content-Type : 엔터티에 의해 전달된 객체의 종류
    • Content-Length : 전달되는 메시지의 길이나 크기
    • Content-Language : 전달되는 객체와 가장 잘 대응되는 자연어
    • Content-Encoding : 객체 데이터에 대해 행해진 변형(압축 등)
    • Content-Location : 요청 시점을 기준으로, 객체의 또 다른 위치
    • Content-Range : 만약 이 엔터티가 부분 엔터티라면, 이 헤더는 이 엔터티가 전체에서 어느 부분에 해다아는지 정의한다.
    • Content-MD5 : 엔터티 본문의 콘텐츠에 대한 체크섬
    • Last-Modified : 서버에서 이 콘텐츠가 생성 혹은 수정된 날
    • Expires : 이 엔터티는 데이터가 더 이상 신선하지 않은 것으로 간주되기 시작하는 날짜와 시각
    • Allow : 이 리소스에 대한 어떤 요청 메서드가 허용되는지 예) GET과 HEAD
    • ETag : 이 인스턴스에 대한 교유한 검사기. 엄밀히 말해 ETag 헤더는 엔터티 헤더로 정의되어 있지 않지만 에터티와 관련된 많은 동작을 위해 중요
    • Cache-Control : 어떻게 이 문서가 캐시될 수 있는지에 대한 지시자. ETag와 같이 Cache-Control 헤더도 엔터티 헤더로 정의되지 않는다.

    1) 엔터티 본문

    • 엔터티 본문은 가공되지 않은 데이터만을 담고 있다. 다른 정보들은 모두 헤더에 담겨 있다.
    • 엔터티 본문은 날 것의 데이터에 불과하기에 엔터티 헤더는 그 데이터의 의미에 대해 설명할 필요가 있다. 예)Content-Type, Content-Encoding
    • 엔터티의 본문은 CRLF 줄 바로 다음부터 시작한다.

    2) Content-Length:엔터티의 길이

    • 이 헤더는 메시지의 엔터티 본문의 크기를 바이트 단위로 나타낸다.

    • 인코딩과 상관없이 크기를 표현할 수 있다.

    • 이 헤더는 메시지를 청크 인코딩으로 전송하지 않는 이상, 엔터티 본문을 포함한 메시지에서는 필수적으로 있어야 한다.

    • 서버 충돌로 인해 메시지가 잘렸는지 감지하고자 할 때와 지속 커넥션을 공유하는 메시지를 올바르게 분할하고 할 때 필요하다.

      2-1) 잘림 검출

      • 엣날 버전의 HTTP는 커넥션이 닫힌 것을 보고 메시지가 끝났음을 인지했지만, Content-Length가 없다면 클라이언트는 커넥션이 정상적으로 닫힌
        것인지 메시지 전송 중에 서버에 충돌이 발생한 것인지 구분하지 못한다.
      • 메시지 잘림은 캐싱 프락시 서버에서 특히 취약하다. 만약 캐시가 메시지를 수신했으나 잘렸다는 것을 인식하지 못한다면, 캐시는 결함이 있는 콘텐츠
        를 저장하고 계속해서 제공하게 될 것이다.
<Strong>2-2) 잘못된 Content-Length
- Content-Length가 잘못된 값을 담고 있을 경우 아예 빠진 것보다도 큰 피해를 유발할 수 있다.

<Strong>2-3) Content-Length와 지속 커넥션(Persistent Connection)
- Content-Length는 지속 커넥션을 위해 필수이다.
- 응답이 지속 커넥션을 통해서 온 것이라면, 또 다른 HTTP 응답이 그 뒤를 이을 것이다.
- Content-Length 헤더는 클라이언트에게 메시지 하나가 어디서 끝나고 다음 시작은 어디인지 알려준다.
- 커넥션이 지속적이기 때문에 클라이언트가 커넥션이 닫힌 위치를 근거로 메시지의 끝을 인식하는 것은 불가능하다.
- 청크 인코딩에선 Content-Length 헤더 없이 지속 커넥션을 만날수 있다. 청크 인코딩은 데이터를 각각이 특정한 크기를 갖는 일련의 청크들로 쪼개
  어 보낸다. 만약 서버가 헤더가 생성되는 시점에서 엔터티 전체의 크기를 알 수 없다하더라도, 서버는 청크 인코딩을 이용해 엔터티를 잘 정의된 크기
  의 조각들로 전송할 수 있다.

<Strong>2-4) 콘텐츠 인코딩
- HTTP는 보안을 강화거나 압축을 통해 공간을 절약할 수 있도록, 엔터티 본문을 인코딩할 수 있게 해준다.
- 본문의 콘텐츠가 인코딩되어 있다면, Content-Length 헤더는 인코딩 되지 않은 본문의 길이가 아닌 인코딩된 본문의 길이를 바이트로 정의한다.

<Strong>2-5) 엔터티 본문 길이 판별을 위한 규칙
1. 본문을 갖는 것이 허용되지 않는 특정 타입의 HTTP 메시지에서는, 본문 계산을 위한 Content-Length 헤더가 무시된다. 이 헤더는 부가정보에
   불과하며, 실제 본문 길이를 서술하지 않는다. 가장 중요한 예는 HEAD 응답이다. HEAD 메서드는 GET 요청을 보냈다면 받게 될 응답에서 본문은 
   제외하고 헤더들만 보내라고 서버에게 요청한다. GET 응답은 Content-Length 헤더를 돌려주기 떄문에, HEAD 응답 또한 그럴 것이다. 그러나 
   GET 응답과는 달리 HEAD 응답은 본문을 갖지 않는다.
2. 메시지가 Transfer-Encoding 헤더를 포함하고 있다면, 메시지가 커네션이 닫혀서 먼저 끝나지 않는 이상 엔터티는 '0바이트 청크'라 불리는
   특별한 패턴으로 끝나야 한다.
3. 메시지가 Content-Length 헤더를 갖는다면 Transfer-Encoding 헤더가 존재하지 않는 이상 Content-Length 값은 본문의 길이를 담는다.
   만약 Content-Length 헤더 필드와 Identity가 아닌 Transfer-Encoding 헤더 필드를 갖는 메시지를 받았다면 반드시 Content-Length
   헤더는 무시해야 한다. 왜냐하면 엔터티 본문을 표현하고 전송하는 방식을 바꿀 것이기 때문이다.
4. 메시지가 multipart/byteranges 미디어 타입을 사용하고 엔터티 길이가 별도로 정의되지 않았다면(Contetn-Length 헤더로), 멀티파트 
   메시지의 각 부분은 각자가 스스로 크기를 정의할 것이다. 이 멀티파트 유형은 자신의 크기를 스스로 결정할 수 있는 유일한 엔터티 본문 유형이다.
   따라서 이 미디어 타입은 수진자가 이것을 해석할 수 있다는 사실을 송신자가 알기 전까지는 절대로 보내지 말아야 한다.
5. 위의 어떤 규칙에도 해당되지 않는다면, 엔터티는 커넥션이 닫힐 때 끝난다. 실질적으로, 오직 서버만이 메시지가 끝났음을 알리기 위해서 커넥션을 
   닫을 수 있다. 클라이언트는 클라이언트 메시지가 끝났다는 신호를 위해 커넥션을 닫을 수 없다. 그렇게 커넥션이 닫힌다면 서버가 응답을 돌려줄 수 
   없다.
6. HTTP/1.0 애플리케이션과의 호환을 위해, 엔터티 본문을 갖고 있는 HTTP/1.1 요청은 반드시 유효한 Content-Length 헤더도 갖고 있어야 한 
   다. 1.1 명세는 요청에 본문은 잇지만 Length 헤더가 없는 경우, 메시지의 길이를 판별할 수 없다면 400 Bad Request 응답을 보내고 유효한 
   Length 헤더를 요구하고 싶다면 411 Length Required 응답을 보내라고 조언하고 있다.

15.3 엔터티 요약

  • Content-MD5 헤더는 서버가 엔터티 본문에 MD5 알고리즘을 적욜한 결과를 보내기 위해 사용되지만 잘 쓰이지 않으므로 생략

15.4 미디어 타입과 차셋(Charset)

  • Content-Type 헤더 필드는 엔터티 본문의 MIME 타입을 기술한다. MIME 타입은 전달되는 데이터 매체의 기저 형식으 표준화된 이름이다.

  • 클라이언트 애플리케이션은 콘텐츠를 적절히 해독하고 처리하기 위해 MIME 타입을 이용한다.

  • MIME 타입은 주 미디어 타입으로 시작해서 뒤이어 빗금(/), 그리고 미디어 타입을 더 구체적으로 서술하는 부 타입 Subtype으로 구성된다.

  • Content-Type 헤더가 원본 엔터티 본문의 미디어 타입을 명시한다는 것은 중요하다. 예를 들어 엔터티가 콘텐츠 인코딩을 거친 경우에도 Content-
    Type 헤더는 여전히 인코딩 전의 엔터티 본문 유형을 명시할 것이다.

    1) 텍스트 매체를 위한 문자 인코딩

    • Content-Type 헤더는 내용 유형을 더 자세히 지정하기 위한 선택적인 매개변수도 지원한다. 엔터티의 비트 집합을 텍스트 파일의 글자들로 변환하기 위
      한 charset 매개변수가 그 예이다.

    2) 멀티파트 미디어 타입

    • MIME 멀티파트 이메일 메시지는 서로 붙어있는 여러 개의 메시지를 포함하며, 하나의 복합 메시지로 보내진다. 각 구성요소는 자족적으로 자신에 대해
      서술하는 헤더를 포함한다. 여러 구성요소들이 이어져 있고, 문자열 하나로 서로의 경계가 식별된다.
    • HTTP는 멀티파트 본문도 지원한다. 그러나 일반적으로는 폼을 채워서 제출할 때와 문서의 일부분을 실어 나르는 범위 응답을 할 때의 두 가지 경우에만
      사용된다.

    3) 멀티파트 폼 제출

    • HTTP 폼을 채워서 제출하면, 가변 길이 텍스트 필드와 업로드 될 객체는 각각이 멀티파트 본문을 구성하는 하나의 파트가 되어 보내진다. 멀티파트 본문
      은 여러 다른 종류와 길이의 값으로 채워진 폼을 허용한다. 예를 들어 당신의 친구가 자신의 이름과 폭스바겐 버스 수리에 대한 자신의 열정을 서술한 긴
      수필을 적어 넣고 있을 때, 그와 다르게 당신은 이름 및 별명과 작은 사진을 포함한 소개를 요구하는 폼을 채우는 쪽을 선택할 수 있다.
    • HTTP는 다음과 같이 그러한 요청을 Content-Type: multipart/form-data나 Content-Type: multipart/mixed 헤더에 멀티파트 본문을
      함께 보낸다.

    4) 멀티파트 범위 응답

    • 범위 요청에 대한 HTTP 응답 또한 멀티파트가 될 수도 있다. 그러한 응답은 Content-Type: multipart/byterange 헤더 및 각각 다른 범위를
      담고 있는 멀티파트 본문이 함께 온다.

15.5 콘텐츠 인코딩

  • HTTP 애플리케이션은 때때로 콘텐츠를 보내기 전에 인코딩을 하려고 한다. 예를 들어 느린속도로 연결된 클라이언트에게 큰 HTML 문서를 전송하기 전에 서
    버는 전송 시간을 줄이기 위해 압출을 할 수 있다. 서버는 허가받지 않은 제 사잠가 볼 수 없도록 콘텐츠를 암호화하거나 뒤섞을 수도 있다.

    1) 콘텐츠 인코딩 과정

    1. 웹 서버가 원본 Content-Type과 Length 헤더를 수반한 원본 응답 메시지를 생성한다.
    2. 콘텐츠 이코딩 서버가 인코딩된 메시지를 생성한다. 인코딩된 메시지는 Content-Type은 같지만(본문 압축이 되었다면) Content-Length는 다르다.
      콘텐츠 인코딩 서버는 Content-Encoding 헤더를 인코딩된 메시지에 추가하여, 수신 측 애플리케이션이 그것을 디코딩할 수 있도록 한다.
    3. 수신 측 프로그램은 인코딩된 메시지를 받아서 디코딩하고 원본을 얻는다.
인코딩된 응답의 예(압축 이미지)
HTTP/1.1 200 OK
Date: 2020 10 10 ....
server: Apache/1.2.4
Content-Length: 6096
Content-Type: image/gif
Content-Encoding: gzip
[...]
  • 여전히 Content-Type 헤더가 메시지에 존재할 수 있고 또한 그래야 한다는 점을 말해두겠다. 엔터티의 원래 포맷을 기술하며, 이는 디코딩된 엔터티
    를 보여주기 위해 필요한 정보다. Content-Length 헤더는 이제 인코딩된 본문의 길이를 나타낸다는 것이다.

    2) 콘텐츠 인코딩 유형

  • HTTP는 몇 가지 표준 콘텐츠 인코딩 유형을 정의하고 확장 인코딩으로 인코딩을 추가하는 것도 허용한다.

    콘텐츠 인코딩 값 설명
    gzip 엔터티에 GNU zip 인코딩이 적용됨을 의미
    compress 엔터티에 대해 유닉스 파일 암축 프로그램인 compress가 실행되었음을 의미
    deflate 엔터티가 zlib 포맷으로 압축됨을 의미
    identity 엔터티에 어떤 인코딩도 수행되지 않았음을 의미 안코딩 헤더가 존재하지 않는다면 이값인 것으로 간주
  • gzip, compress, deflate 인코딩은 전송되는 메시지의 크기를 정보의 손실 없이 줄이기 위한 무손실 압축 알고리즘이며, gzip는 일반적으로 가장
    효율적이고 가장 널리 쓰이는 압축 알고리즘이다.

    3) Accept-Encoding 헤더

  • 서버에서는 클라이언트가 지원하지 않는 인코딩을 사용하는 것을 막기 위해, 클라이언트는 자신이 지원하는 인코딩의 목록을 Accept-Encoding 요청 헤
    더를 통해 전달한다.

  • Accept-Encoding 필드는 쉼표로 구분된다.

15.6 전송 인코딩과 청크 인코딩

  • 이전 절에서는 메시지 본문에 적용 된 가역적 변환인 콘텐츠 인코딩은 콘텐츠 포맷과 긴밀하게 연관되어 있다.
  • 예를 들어 텍스트 파일은 흔히 gzip으로 압축하지만 JPEG 파일은 gzip으로 잘 압축되지 않기에 사용하지 않는다
  • 이 절에서는 전송 인코딩에 대해 논의하는데, 전송 인코딩 또한 엔터티 본문에 적용되는 ㅏㄱ역적 변환이지만, 그들은 구조적인 이유 때문에 적용되는 것 이며
    콘텐츠의 포맷과는 독립적이다.
  • 메시지 데이터가 네트워크를 통해 전송되는 방법을 바꾸기 위해 전송 인코딩을 메시지에 적용할 수 있다.
  • 콘텐츠 인코딩된 메시지는 단지 엔터티 부분만 인코딩한다.
  • 전송 인코딩된 메시지는 전체 메시지를 인코딩하여 메시지 자체의 구조를 바꾼다.

1) 안전한 전송

  • 역사적으로, 전송 인코딩은 다른 프로토콜에서도 네트워크를 통한 '안전한 전송'을 위해 존재하였다.

  • 표준화된 전송 기반을 갖춘 HTTP는 '안전한 전송'의 초점을 다른 데에 맞추고 있다.

  • HTTP에서 전송된 메시지의 본문이 문제를 일으키는 두가지

    알 수 없는 크기

    • 몇몇 게이트웨이 애플이케이션과 콘텐츠 인코더는 콘텐츠를 먼저 생성하지 않고서는 메시지 본문의 최종 크기를 판단할 수 없다.
    • 이 서버들은 그 사이즈를 알기 전에 데이터 전송을 시작하려고 한다.
    • HTTP는 데이터에 앞서 Content-Length 헤더를 요구 하기 때문에, 몇몇 서버는 데이터의 끝을 알리는 특별한 종결 꼬리말을 포함하여 전송 인코딩으로 데이터를 보내려 시도한다.

    보안

    • 공용 전송 네트워크로 메시지 콘텐츠를 보내기 전에 전송 인코딩을 사용해 알아보기 어렵게 뒤섞는 방법도 있지만 SSL 같은 전송 계층 보안 방식을 주로 사용한다.

    2) Transfer-Encoding 헤더

  • 전송 인코딩을 제어하고 서술하기 위해 정의돈 헤더 두가지

    Transfer-Encoding

    • 안전한 전송을 위해 어떤 인코딩이 메시지에 적용 되었는지 수신자에게 알려준다.

    TE

    • 어떤 확장된 전송 인코딩을 사용할 수 있는지 서버에게 알려주기 위해 요청 헤더에 사용
    • Accept-Transfer-Encoding과 같은 의미이다.
      TE 헤더 사용 예
      

GET /new_products.html HTTP/1.1
Host: www.joes-hardware.com
User-Agent: Mozilla/4.61
TE: trailers, chunked [chunked-encoded 메시지와 메시지의 끝에 트레일러가 오는 것을 받아들일 수 있음을 서버에게 알려주기 위해 작성]

Transfer-Encoding 헤더 사용 예

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Server: Apache/3.0
[이 기초 헤더 뒤에 메시지 구조가 변함]

  <Strong>3) 청크 인코딩
  - 청크 인코딩은 메시지를 일정 크기의 청크 여럿으로 쪼갠다. 서버는 각 청크를 순차적으로 보낸다.
  - 청크 인코딩을 이용하면 메시지를 보내기 전에 전체 크기를 알 필요가 없어진다.
  - 청크 인코딩이 전송된 인코딩의 한 형태이며 본문이 아닌 메시지의 속성임에 주목해야 한다.

    <Strong>청크와 지속 커넥션
    - 지속 커넥션에서는 본문을 쓰기 전에 반드시 Content-Length 헤더에 본문의 길이를 담아서 보내줘야 한다.
    - 콘텐츠가 서버에서 동적으로 생성되는 경우에는, 보내기 전에 본문의 길이를 알아내는 것이 불가능할 것이다.
    - 청크 인코딩은 서버가 본문을 여러 청크로 쪼개 보낼 수 있게 해줌으로써 이것에 대한 해법을 제공한다.
    - 동적으로 본문이 생성되면서, 서버는 그중 일부를 버퍼에 담은 뒤 그 한 덩어리를 그의 크기와 함께 보낼 수 있다.
    - 마지막 청크는 본문의 끝을 의미하기 때문에 크기가 0이다.

![](https://github.com/kimmin-ko/HTTP-The-Definitive-Guide/raw/master/images/15-6.png?raw=true)

    <Strong>청크 인코딩된 메시지 트레일러
    - 다음 중 하나 이상의 조건을 만족하면 청크 메시지에 트레일러를 추가할 수 있다.
      - 클라이언트의 TE 헤더가 트레일러를 받아들일 수 있음을 나타내는 경우
      - 트레일러가 응답을 만든 서버에 의해 추가되었으며, 그 트레일러의 콘텐츠는 클라이언트가 이해하고 사용할 필요가 없는 선택적인 메타데이터이므로 클라이언트가 무시하고 버려도 되는 경우
    - 트레일러에는 본문의 콘텐츠가 먼저 생성되어야 한다거나 하는 등의 이유로 메시지 시작 시점에서는 그 값을 알 수 없는 추가적인 헤더 필드를 담을 수 있다.
    - 예로 Content-MD5 헤더가 있는데, Content-MD5 헤더는 문서가 생성되기 전에 그 문서의 MD5를 계산하기 어렵기 때문에 트레일러를 이용할 수 있다.
    - 마지막 청크 다음에 Trailer 헤더에 나열했던 헤더들이 온다.

Trailer 헤더에 담을 수 있는 Directives

message framing headers: Transfer-Encoding, Content-Length
routhing headers: Host
request modifiers: Control-Cache, Max-Forwards, TF
authentication headers: Authorization, Set-Cookie
etc: Content-Encoding, Content-Type, Content-Range, Trailer(itself)


  <Strong>4) 콘텐츠 전송 인코딩의 조합
  - 콘텐츠 인코딩과 전송 인코딩은 동시에 사용될 수 있다.
  - 콘텐츠 인코딩을 사용해서 HTML 파일을 압축하고 그 청크 데이터를 전송 인코딩을 사용해서 전송

  <Strong>5) 전송 인코딩 규칙
  - 전송 인코딩의 집합은 반드시 'chunked'를 포함해야 한다.
  - 청크 전송 인코딩이 사용되었다면, 메시지 본문에 적용된 마지막 인코딩이 존재해야 한다.
  - 청크 전송 인코딩은 반드시 메시지 본문에 한 번 이상 적용되어야 한다.

### 15.7 시간에 따라 바뀌는 인스턴스
- 같은 URL의 리소스에 대해 시간에 따라 다른 인스턴스(객체)를 응답 받을 수 있다.

![](https://github.com/kimmin-ko/HTTP-The-Definitive-Guide/raw/master/images/15-8.png?raw=true)

### 15.8 검사기와 신선도
- 클라이언트가 서버로부터 처음 받은 리소스를 캐시에 저장하는데, 만료되면 서버에게 최신 사본을 요구하고 서버는 둘을 비교 한 후 변경되었으면 새로 응답한다.

  <Strong>1) 신선도
  - 서버는 Expires와 Cache-Control 헤더를 통해 얼마나 콘텐츠를 캐시하고 있엇는지, 그것이 신선한지에 대한 정보를 제공한다.

  <Strong>2) 조건부 요청과 검사기
  - 클라이언트가 같은 리소스에 한 번 이상 접근했을 때, 우선 현재 사본이 여전히 신선한지 판별한다.
  - 만약 그렇지 않다면, 클아이언트는 반드시 서버로부터 최신 버전을 얻어와야 한다.
  - 리소스가 변경되지 않은 상황에서 똑같은 사본을 다시 받아오는 상황을 피하기 위해, 클라이언트는 서버에 현재 사본을 유일하게 식별할 검사기를 명시해서 조건부 요청을 보낼 수 있다.
  - 서버는 오직 클라이언트의 사본과 다를 때만 리소스의 사본을 보낼 것이다.

### 15.9 범위 요청
- HTTP 클라이언트는 받다가 실패한 엔터티를 일부 혹은 범위로 요청함으로써 다운로드를 중단된 시점에서 재개할 수 있다.

GET /bigfile.html HTTP/1.1
Host: www.joes-hardware.com
Range: bytes=4000-
User-Agent: Mozilla/4.61
[처음 4,000바이트 이후의 부분을 요청]

- 서버는 클라이언트에게 자신의 범위를 받아드릴 수 있는지 응답에 Accept-Ranges 헤더를 포함시켜 알려준다.
- 단위는 주로 바이트이다.

HTTP/1.1 200 OK
Date: Fri........
Server: Apache/1.2.4
Accept-Ranges: bytes

```

  • 클라이언트의 범위 요청은 오직 클라이언트와 서버가 같은 버전의 문서를 갖고 있을 때만 의미가 있다.

15.10 델타 인코딩

  • 만료된 웹 페이지에 대해 새 페이지 전체를 보내는 대신 변경된 부분만 서버가 보낸다면 클라이언트는 더 빨리 페이지를 얻을 수 있다.

댓글