kakasoo

[HTTP] 21. 엔터티와 인코딩 본문

프로그래밍/HTTP

[HTTP] 21. 엔터티와 인코딩

카카수(kakasoo) 2021. 1. 31. 17:04
반응형

HTTP는 다음을 보장한다.

  • 객체는 올바르게 식별된다. ( Content-Type, Content-Language 등 헤더를 이용한다. )
  • 객체는 올바르게 압축이 풀린다. ( Content-Length, Content-Encoding 등 헤더를 이용한다. )
  • 객체는 항상 최신이다. ( 엔터티 검사, 캐시 만료 제어 )
  • 사용자의 요구를 만족한다. ( 내용 협상을 위한 Accept 관련 헤더를 이용한다. )
  • 네트워크 사이를 빠르고 효율적으로 이동한다.
  • 조작되지 않고 온전하게 ( 안전하게 ) 도착할 것이다.

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

HTTP를 컨테이너라고 한다면 HTTP 엔터티는 메시지의 실질적인 화물이다. 엔터티는 엔터티 헤더와 엔터티 본문으로 나뉜다.

HTTP/1.1의 10가지 주요 엔터티 헤더 필드

  1. Content-Type
  2. Content-Length
  3. Content-Language
  4. Content-Encoding : 객체 데이터에 행해진 변형 ( ex. 압축 )
  5. Content-Location : 요청 시점을 기준으로 객체의 또 다른 위치
  6. Content-Range
  7. Content-MD5 : 엔터티 본문의 콘텐츠에 대한 체크섬
  8. Last-Modified
  9. Expires : 데이터에 대한 유통기한 ( 비유하자면 그렇다. )
  10. Allow : 어떤 요청 메서드가 허용되는지
  • ETag
  • Cache-Control

의문점

체크섬은 말 그대로 제대로 메시지가 돌아왔는지 확인하기 위함인데, 이걸 의심하는 단계라면 헤더는 어떻게 믿을 수 있을까?

⇒ 50바이트 짜리 체크섬이 틀릴 가능성은, 1,550 바이트짜리 본문이 틀릴 가능성보다 작다.

15.1.1 엔터티 본문

인코딩 되지 않았다면, 엔터티 본문은 보통, 가공되지 않은 상태로 들어있다. 다른 정보들은 모두 헤더에 있다. 엔터티 본문 자체는 raw data이기 때문에, 엔터티 헤더를 봐야 한다.

엔터티 본문은 16진수 값 형태로 옮겨진다.

15.2 Content-Length: 엔터티의 길이

Content-Length 헤더는 메시지의 엔터티 본문의 크기를 바이트 단위로 나타낸다. 어떻게 인코딩되었든 상관없이 크기를 표현할 수 있다. ( 단, 압축될 경우 압축된 후의 크기이다. )

Content-Length 헤더는, 메시지의 청크 인코딩을 전송하지 않는 이상, 엔터티 본문을 포함한 메시지에서는 반드시 있어야 한다.

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

15.2.1 잘림 검출

옛날에는 커넥션이 닫히면 HTTP 메시지가 끝난 걸로 쳤지만, 사실 Content-Length가 없다면 정상적으로 닫힌 건지 아닌지를 구별할 수 없다.

메시지 잘림은 캐싱 프락시 서버에서 특히 취약한데, 캐시가 잘린 메시지를 수신했으나 잘렸음을 인식하지 못했다면 캐시는 결함있는 정보를 저장하고 계속해서 제공하게 될 것이다.

캐시는 이를 해결하고자 Content-Length가 없는 메시지를 저장하지 않는다.

15.2.2 잘못된 Content-Length

Content-Length가 잘못된 값을 가진 경우 아예 없는 것보다 피해가 커질 수 있다. 초창기 클라이언트들은 Content-Length 계산과 관련된 버그를 가지고 있다.

따라서 몇몇 클라언트, 서버, 프락시들은 서버의 오작동을 대비해 탐지 및 교정을 시도한다. 공식적으로 HTTP/1.1 사용자 에이전트는 잘못된 길이를 받고 인지한 경우 사용자에게 통지한다.

15.2.3 Content-Length와 지속 커넥션 ( Persistent Connection )

커넥션이 닫히게 되면 그걸 통해 메시지가 끝났다는 걸 알 수 있었지만 ( 예전의 프로토콜에서 그렇다는 의미다. ),

응답이 지속 커넥션으로 온 것이라면 커넥션이 닫히지 않고 유지될 것이므로 클라이언트는 메시지의 끝을 인식할 수가 없다. 따라서 Content-Length 헤더는 필수가 되었다.

15.2.4 콘텐츠 인코딩

HTTP는 보안을 강화하거나 압축을 통해 공간을 절약할 수 있도록 엔터티 본문을 인코딩할 수 있게 해준다. 이 때 Content-Length는 인코딩된 본문의 길이를 바이트 단위로 정의한다.

15.2.5 엔터티 본문 길이 판별을 위한 규칙

  1. 본문이 없어야 할 HTTP 메시지에서는 Content-Length 헤더는 무시된다. ( 잘못 만든 애플리케이션은 이 경우에도 Content-Length가 유의미하다고 착각한다. )
    • 예컨대 HEAD 응답에서는 본문이 없다.
  2. 메시지가 Transfer-Encoding 헤더를 포함하고 있다면 메시지가 커넥션이 닫혀서 먼저 끝나지 않는 이상 엔터티는 '0바이크 청크'라 불리는 특별한 패턴으로 끝나야 한다.
    • 추후 다룰 내용.
  3. 메시지가 Content-Length를 가진다면 본문의 길이를 담는다. 만약 Transfer-Encoding 헤더 필드를 받았다면 Content-Length 헤더는 무시한다. ( 전송 방식을 바꿀 것이기 때문에. )
  4. 엔터티는 커넥션이 닫힐 때 끝난다. 실질적으로, 오직 서버만이 메시지가 끝났음을 알리기 위해 커넥션을 닫을 수 있다.

15.3 엔터티 요약

HTTP가 일반적으로 TCP/IP와 같은 신뢰할만한 전송 프로토콜 위에서 구현됨에도 불구하고 메시지의 일부분은 변형이 될 수 있다. 이를 위해 체크섬이 필요하다.

⇒ 이 역시도 헤더가 망가지면 아무런 의미가 없다. 하지만 수신자는 엔터티의 변경을 잡아내기 위해 그 체크섬으로 기본적인 검사를 할 수 있다.

Content-MD5 헤더

서버는 Content-MD5 헤더를, 엔터티 본문에 MD5 알고리즘을 적용한 결과를 보내기 위해 사용한다. 오직 응답을 처음 만든 서버만이 이 헤더를 계산해서 보낼 것이며, 중간에 수정되지 않을 것이다.

그랬다가는 종단 간 무결성을 검증하는 목적이 훼손되고 만다.

클라이언트는 전송 인코딩을 디코딩한 뒤 디코딩된 엔터티 본문에 대해 MD5를 계산해야 한다.

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

Content-Type에는 MIME ( MultiPurspose Internet Mail Extentsions ) 타입을 기술한다. 클라이언트는 콘텐츠를 적절히 해독하기 위해 MIME 타입을 이용한다.

MIME 타입은 주 타입과 보조 타입이 슬래시를 구분으로 하여 작성된다.

Content-Type은 인코딩 이후에도, 인코딩 전의 type을 명시할 것이다.

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

Content-Type 헤더는 내용 유형을 더 자세히 지정하기 위해 선택적인 매개변수도 지원한다.

Content-Type : text/html; charset=iso-8858-4

15.4.2 멀티파트 미디어 타입

MIME type이 멀티파트 이메일 메시지인 경우, 여러 개의 메시지를 합쳐 하나의 복합 메시지로 보내진다. 각 구성 요소는 자족적으로 자신에 대해 서술하는 헤더를 포함한다. ( HTTP/1.1에서는 아닐 수도...? )

일반적으로는 폼을 채워서 제출할 때와 문서의 일부분을 실어 나르는 범위 응답을 할 때 두 가지 경우에만 사용된다.

15.4.3 멀티파트 폼 제출

HTTP 폼을 채워서 제출하면 가변 길이 텍스트 필드와 업로드 될 객체는 각각이 멀티파트 본문을 구성하는 하나의 파트로 보내진다.

멀티파트 본문은 여러 다른 종류의 길의 값으로 채워진 폼을 허용한다.

15.4.4 멀티파트 범위 응답

생략한다.

⇒ 멀티파트를 다뤄본 적이 없기 때문에 이 내용이 중요한지 여부를 잘 판단할 수가 없다.

15.5 콘텐츠 인코딩

HTTP 애플리케이션은 때때로 콘텐츠를 보내기 전에 인코딩을 하기도 한다. 예를 들어 느린 속도로 연결된 클라이언트에게 큰 HTML 문서를 전송하기 전에 서버는 압축을 할 수 있다.

이러한 종류의 인코딩은 발송하는 쪽에서만 적용한다.

15.5.1 콘텐츠 인코딩 과정

  1. 웹 서버가 원본 Content-Type 과 Content-Length 헤더를 수반한 원본 응답 메시지를 생성한다.
  2. 콘텐츠 인코딩 서버가 인코딩 된 메시지를 생성한다. Content-Type은 같지만, Content-Length는 이 때 달라진다. Content-Encoding 헤더를 인코딩된 메시지에 추가하여 추후 디코딩을 할 것을 알린다.
  3. 수신 측은 디코딩하여 원본을 얻는다.

15.5.2 콘텐츠 인코딩 유형

HTTP 는 몇 가지 표준 콘텐츠 인코딩 유형을 정의하고 확장 인코딩으로 인코딩을 추가하는 것도 허용한다. 인코딩은 콘텐츠 알고리즘에 대한 고유한 토큰을 할당하는 IANA를 통해 표준화된다.

몇 가지 자주 쓰이는 것은 아래와 같다.

  • gzip : 엔터티에 대해 GNU zip 인코딩이 적용되었음을 알린다.
  • compress : 엔터티에 대해 유닉스 압축 프로그램인 compress가 적용되었음을 알린다.
  • deflate : 엔터티가 zlib 포맷으로 압축되었음을 알린다.
  • identify : 엔터티에 대해 어떤 인코딩도 수행되지 않았음을 알린다. Content-Encoding이 없을 경우 identify로 처리한다.

gzip, compress, deflate 모두 무결성 압축 알고리즘이며, 이 중에서 gzip이 가장 효율적이며 널리 쓰인다.

15.5.3 Accept-Encoding 헤더

클라이언트가 어떤 방법으로 디코딩할 수 있는지, 서버에게 알려주기 위한 헤더

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

콘텐츠 인코딩은 콘텐츠 포맷과 긴밀한 연관이 있다. 예를 들어 텍스트 파일은 보통 gzip으로 압축하지만 JPEG는 그렇게 안한다. 왜냐하면 gzip으로는 압축 효과가 작기 때문이다.

15.6.1 안전한 전송

전송 인코딩은 다른 프로토콜에서도 네트워크를 통한 '안전한 전송'을 위해 존재한다. 안전한 전송을 방해하는 요소는 몇 가지 없다.

알 수 없는 크기

몇몇 게이트웨이 애플리케이션과 콘텐츠 인코더는 콘텐츠를 생성하지 않은 상태로는 메세지 본문의 최종 크기를 판단할 수 없다. 그래서 이 서버들은 그 사이즈를 알기 전에 데이터를 전송하고자 한다.

그러나 HTTP는 데이터에 앞서 Content-Length를 요구한다. 따라서 몇몇 서버는 데이터의 끝을 알리는 특별한 종결 꼬릿말을 포함시켜 전송 인코딩으로 데이터를 보내려고 시도한다.

⇒ Content-Length를 몰라도 데이터를 모두 보냈음을 알리기 위한 수단.

보안

알아보기 어렵게 뒤섞어 버리는 방식의 경우.

15.6.2 Transfer-Encoding 헤더

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

TE

어떤 확장된 전송 인코딩을 사용할 수 있는지 서버에게 알려주기 위해 요청 헤더에 사용한다.

15.6.3 청크 인코딩

청크 인코딩은 메시지를 일정 크기의 청크 여럿으로 쪼갠다. 서버는 각 청크를 순차적으로 보낸다. 청크 인코딩을 사용하면 메시지를 보내기 전 전체 크기를 알 필요가 없어진다.

본문이 동적으로 형성됨에 따라 서버는 그중 일부를 버퍼에 담은 뒤 한 청크를 그것의 크기와 함께 보낼 수 있다. 본문 전체를 모두 보낼 때까지 이것을 반복한다.

청크와 지속 커넥션

클라이언트와 서버 사이의 커넥션이 지속적이지 않다면, 클라이언트는 자신이 읽고있는 본문의 크기를 알 필요가 없다. 서버가 커넥션을 닫으면 된다.

지속 커넥션이라면 본문을 쓰기 전에 반드시 Content-Length를 보내야 한다. 다만 콘텐츠가 서버에서 동적으로 생성되는 경우에는 알아내는 것이 불가능할 것이다.

청크 인코딩은 서버가 본문을 여러 청크로 쪼개 보낼 수 있게 해줌으로써 이 딜레마에 대한 해법을 제공한다.

⇒ 원래대로면 동적으로 생성되는 경우는 없어야 하지만, form-data가 크기 때문에 분할이 필요함으로써 데이터 크기를 알 수 없게 되는 것이고, 이를 위해 청크 인코딩을 이용한다.

서버는 최종적으로 크기가 0인 청크를 보냄으로써 본문이 끝났음을 알리고, 다음 응답을 위해 커넥션을 유지해둘 수 있다.

다만, 클라이언트는 청크 요청이 411 Length Required 응답으로 거절당하는 것을 대비해야한다.

트레일러

마지막 청크 다음에 보내는 헤더로, Content-MD5.

청크 인코딩된 메시지의 트레일러

  • 클라이언트 TE 헤더가 트레일러를 받아들일 수 있음을 나타내는 경우
  • 트레일러가 서버 측에 의해 추가되었고, 그 콘텐츠가 선택적인 데이터라 클라이언트에게 반드시 필요한 게 아닌 경우

이 두 경우에만 트레일러를 추가할 수 있다.

15.6.4 콘텐츠와 전송 인코딩의 조합

콘텐츠 인코딩과 전송 인코딩은 동시에 사용될 수 있다. 예를 들어 콘텐츠를 인코딩한 결과를 다시, 청크 인코딩 ( 전송 인코딩 ) 할 수도 있다. 이 경우 인코딩과 디코딩 절차는 반대가 된다.

15.6.5 전송 인코딩 규칙

전송 인코딩이 메시지 본문에 적용될 때, 몇 가지 규칙이 반드시 적용되어야 한다.

  • 전송 인코딩 집합은 반드시 cuunked를 포함해야 한다. ( 메시지가 커넥션의 종료로 끝나는 경우는 제외한다. )
  • 청크 전송 인코딩이 사용되었다면 메시지 본문에 적용된 마지막 전송 인코딩이 존재해야 한다.
  • 청크 전송 인코딩은 반드시 본문에 1번 이상 적용되어야 한다.

이 규칙은 수신자가 메시지의 전송 길이를 알아낼 수 있게 해준다. 전송 인코딩은 HTTP 버전 1.1에 소개된 비교적 새로운 기능이다.

서버가 전송 인코딩을 이해할 수 없는 경우라면 501 Unimplemented 상태 코드로 응답한다.

15.7 시간에 따라 바뀌는 인스턴스

웹 사이트는 정적이지 않다. 예컨대 쇼핑몰 사이트는 시간에 따라 보이는 페이지가 달라진다. 이 때 페이지 자체를 객체로 생각해보고, 시간 대 별 페이지를 그 객체의 인스턴스라고 생각해보자.

HTTP 프로토콜은 어떤 특정한 종류의 요청이나 응답을 다루는 방법들을 정의하는데, 이것을 인스턴스 조작 이라고 부르며 객체의 인스턴스에 작용한다.

이 때 대표적인 두 가지가 범위 요청과 델타 인코딩이다.

이 둘 모두가, 클라이언트가 자신이 가지고 있는 리소스의 사본이 서버의 것과 정확히 일치하는지 판단하고 필요에 따라 새 요청을 하는 능력을 가질 것을 요구한다.

15.8 검사기와 신선도

클라이언트가 사본을 가지고 있지 않으면 서버에게 리소스를 줄 것을 요청해야 한다.

사본이 생긴 이후 클라이언트는 사본을 캐시해둔다. 그런데 이 사본은 얼마나 오랫동안 캐시해두어야 하는 걸까?

⇒ 문서가 만료된 경우에는 반드시 서버에게 최신 사본을 요구해야 한다. 서버에서도 문서가 변경되지 않은 경우에는 다시 받을 필요는 없다. 이를 조건부 요청이라고 한다. 더 설명해보겠다.

15.8.1 신선도

서버는 클라이언트에게 얼마나 오랫동안 그 콘텐츠를 캐시하고 유지할 수 있는지에 대한 정보를 줘야 한다. 이 때 Expires나 Cache-Control 헤더를 사용한다.

Expires 헤더는 문서가 만료되어 더 이상 신선하다고 간주할 수 없게 되는 정확한 날짜를 명시한다. 이는 절대시간을 이용하는데, 서로의 시계가 동기화 안되어 있을 가능성이 있어서 좋은 방법은 아니다.

Cache-Control은 서버가 전송한 후부터 동작하는 상대 시간을 의미한다. 수명은 시계의 동기화에 의존하지 않아서 더 정확하다.

15.8.2 조건부 요청과 검사기

서버의 문서는 대부분, 캐시된 문서와 같은 상태일 것이다. 그럼에도 매번 문서를 가져오게 한다면 이는 대역폭을 낭비하고 캐시와 서버에 불필요한 부하를 준다.

이를 고치기 위해 HTTP 크라이언트는 조건부 요청이라는 방법을 사용한다.

조건부 요청은 평범한 HTTP 요청 메시지지만 특정 조건이 참인 경우에만 수행된다. 조건부 요청은 'If-'로 시작된다. 조건부 요청은 검사기를 통해 참/거짓 여부가 판별된다.

검사기란 Last-Modified, ETag 헤더를 의미한다.

검사기는 다시 약한 검사기와 강한 검사기로 나뉘는데, 약한 검사기는 바이트 크기처럼 같다고 하더라도 문서가 같은 문서임을 보장할 수 없는 것, 강한 검사기는 무조건 일치하는 경우를 판별하는 것이다.

약한 검사기인 경우에는 W/를 붙임으로써, 약한 검사기로 검사될 것임을 미리 밝힌다.

15.9 범위 요청

범위 요청은 HTTP 클라이언트가 받다가 실패한 엔터티를 일부 혹은 범위로 요청함으로써 다운로드가 중단된 시점부터 재개할 수 있도록 하는 장치이다. ( 원 서버의 리소스에 변경점이 없단 가정 하에 )

GET /bigfile.html HTTP/1.1 HOST : www.joes-hardware.com Range : bytes=4000- User-Agent : Mozilla/4.61 [en] (WinNT; I)

15.10 델타 인코딩

서버가 새 페이지 전체를 보내는 대신에 정확히 변경된 지점만을 보내줄 수 있다면 ( 변경량이 작은 경우 ), 클라이언트는 더 빠르게 페이지를 얻을 수 있을 것이다.

델타 인코딩은 객체 전체가 아닌 변경된 부분에 대해서만 통신하여 전송량을 최적화하는 HTTP 프로토콜의 확장이다. 델타 인코딩은 일종의 인스턴스 조작인데,

왜냐하면 어떤 객체의 특정 인스턴스들에 대한 클라이언트와 서버 사이의 정보 교환에 의존하기 때문이다.

15.10.1 인스턴스 조작, 델타 생성기 그리고 델타 적용기

생략한다.

반응형

'프로그래밍 > HTTP' 카테고리의 다른 글

[HTTP] 23. 내용 협상과 트랜스 코딩  (0) 2021.01.31
[HTTP] 22. 국제화  (0) 2021.01.31
[HTTP] 20. 보안 HTTP  (0) 2021.01.31
[HTTP] 19. 기본 인증  (0) 2021.01.31
[HTTP] 18. 클라이언트 식별과 쿠키  (0) 2020.12.20