요약

  • 바이트 오프셋은 조각이 어디에 속하는지는 말해 주지만, 어떤 버전의 표현에 속하는지는 말해 주지 않는다.
  • If-Range는 하나의 GET이 다음을 의미하도록 했다: 이 강한 검증자가 여전히 일치하면 내Range를 처리하고, 그렇지 않으면 범위를 무시하고 현재 표현 전체를 보내라.
  • 이 분기는 또 다른 요청을 피하고 오래된 바이트와 새 바이트가 합쳐지는 것을 막았으며, 범위 지원, 저장, 재조합, 콘텐츠 정확성은 별도의 결정에 맡겨 두었다.

꼬리 부분은 다른 객체에 속할 수도 있다

바이트 0부터 999,999까지 받다가 멈춘 전송을 상상해 보라. 클라이언트는 바이트 1,000,000부터 시작하는 나머지 전부를 요청할 수 있다. 서버의 표현이 바뀌지 않았다면 응답을 이어 붙이는 것이 경제적이다. 그러나 편집자가 문서 앞쪽에 단락을 삽입했거나, 빌드가 아카이브를 교체했거나, 콘텐츠 협상이 이제 다른 인코딩 바이트를 선택한다면 같은 오프셋은 다른 바이트열을 가리킨다. 완벽하게 전달된 꼬리 조각이 서버에 한 번도 존재하지 않았던 파일을 만들어 낼 수 있다.

이것은 단순한 전송 중단이 아니었다. TCP는 애플리케이션에 하나의 연결이 끝났음을 알려 줄 수 있었지만, 이후의 HTTP 요청은 선택된 표현이 바뀌었을 수 있는 리소스에 대해 새로운 교환을 열었다. 위치와 신원은 별개의 사실로 함께 전달되어야 했다.Range는 원하는 구간을 지정했다. 검증자는 클라이언트가 저장한 구간과 서버의 현재 구간이 하나의 객체를 공유할 자격이 있는지를 말해 주어야 했다.

첫 번째 표준 트랙 HTTP/1.1 명세인RFC 2068은 1997년 이 문제에 간결한 분기를 부여했다. 부분 엔티티를 가진 클라이언트는If-Match같은 일반 조건과 함께Range를 사용할 수 있었다. 그러나 엔티티가 바뀌었다면 서버는 그 조건을 거부하고 클라이언트는 현재 본문 전체를 얻기 위해 두 번째 요청이 필요했다.If-Range는 이 복구 절차를 한 번의 교환으로 압축했다. 엔티티가 바뀌지 않았으면 누락된 부분을 돌려주고, 바뀌었다면 새 엔티티 전체를 돌려준다.

RFC 2616은 이 메커니즘을 1999년 HTTP/1.1 통합본으로 이어 갔다. 그 두 가지 일반적인 결과는 특이한 의미를 드러냈다. 엔티티 태그가 일치하면 서버는 206 Partial Content로 부분 범위를 제공해야 했다. 일치하지 않으면 서버는 200 OK와 함께 엔티티 전체를 돌려주어야 했다. 거짓 조건이 전송을 실패로 만들지는 않았다. 그것은 오래된 사본을 완성하는 절약만을 철회했다.

하나의 요청이 두 가지 수용 가능한 형태를 담았다

전형적인 요청은 두 필드를 결합했다:

Range: bytes=1000000-
If-Range: "version-17"

클라이언트는 부분 응답만이 유일하게 수용 가능한 결과라고 말하는 것이 아니었다. 이름이 붙은 연속성 조건이 충족될 때만 부분 응답이 수용 가능하다고 말하는 것이었다. 일치하면Range는 유효하게 유지되었다. 불일치하면 수신자는Range를 무시했고, 그래서 보통의 성공적인 GET이 현재 선택된 표현 전체를 산출했다.

그렇기 때문에If-Range를 다른 HTTP 사전조건과 섞어서는 안 된다.If-Match는 메서드의 진행을 막고 412 Precondition Failed를 만들 수 있다.If-None-Match는 조건부 GET이 304 Not Modified를 산출하게 할 수 있다.If-Range는 둘 다 하지 않는다. 불일치 분기에서 클라이언트는 이미 현재 표현 전체가 유용하다고 선언한 상태다. 응답은 빈 거절이 되는 대신 형태가 바뀐다.

2014년 범위 명세인RFC 7233은 이를 두 번째 요청의 단축으로 묘사했다. 또한 문법의 운용적 사용을 좁혔다. 클라이언트는Range없이If-Range를 보내서는 안 되고, 서버는Range가 없거나 대상이 범위를 지원하지 않을 때 이를 무시한다. 검증자가 일치하면 서버는 범위를 처리해야 한다. 일치하지 않으면 서버는 범위를 무시해야 한다. 정확성은 모든 요청이 206을 낼 것이라는 기대가 아니라 분기 안에 있다.

RFC 9110의 현재 통합본은 평가 순서를 명시한다. GET에서Range와If-Range가 함께 있을 때, 조건이 참이고 적용 가능한 범위가 있으면 206이 된다. 그렇지 않으면 범위 필드는 무시되고 응답은 200이다. 먼저 감지될 수 있는 다른 실패는 여전히 우선한다. 조건은 서버가 범위를 지원하도록 강요하거나, 유효하지 않은 구간을 유효하게 만들거나, 정상적인 표현 선택을 우회할 수 없다.

정확한 바이트에는 강한 비교가 필요했다

HTTP에는 강한 검증자와 약한 검증자가 모두 있는데, 모든 비교가 같은 질문을 묻는 것은 아니기 때문이다. 두 표현은 캐시 신선도 목적으로는 의미상 서로 호환될 만큼 충분하지만 바이트 단위로는 다를 수 있다. 이 느슨함은 클라이언트가 캐시된 페이지를 계속 표시해도 되는지만 알고 싶을 때 유용하다. 그러나 클라이언트가 저장된 바이트에 새 바이트 구간을 용접하려 할 때는 안전하지 않다.

RFC 7232는 강한 검증자는 관찰 가능한 표현 데이터가 바뀔 때마다 바뀌고 부분 콘텐츠 범위에 사용할 수 있다고 말한다. 약한 검증자는 정확한 동일성이 필요 없는 상황에 예약되어 있다. 따라서 클라이언트는 약한 엔티티 태그를If-Range에 넣어서는 안 된다. Last-Modified 날짜는 클라이언트가 엔티티 태그를 갖고 있지 않고 그 날짜가 강한 검증자 자격을 갖출 때만 허용된다.

날짜는 그 조건이 왜 엄격한지를 드러낸다. 리소스는 시계의 분해능 안에서 두 번 바뀔 수 있고, 원점은 생성된 바이트와의 관계가 불확실한 타임스탬프를 내보낼 수 있다.If-Range는 HTTP 날짜를 선택된 표현의 Last-Modified 값과 정확히 비교하며,If-Unmodified-Since의 “이전이거나 같음” 규칙을 사용하지 않는다. 날짜가 충분히 강하지 않으면 조건은 거짓이다. 대략적인 시간이 버전 연속성을 증명한다고 가정하며 바이트를 아끼는 것은 가치가 없다.

엔티티 태그는 또한 불투명하다. 프로토콜은 그것이 암호학적 다이제스트일 것을 요구하지 않고, 클라이언트는 따옴표만으로 생성 알고리즘을 추론할 수 없다. 강한 비교가 말하는 것은 이 원점이 관련 기간 동안 이 리소스의 바이트가 다른 표현들에 걸쳐 검증자를 재사용하지 않을 것이라는 점이다. 그것이 원점을 인증하거나, 파일의 의미를 보증하거나, 다른 URL의 객체와의 동일성을 확립하지는 않는다. RFC 2616은 이미 서로 다른 URI에 걸친 태그 재사용이 동등성에 대해 아무것도 함축하지 않는다고 경고했다.

일치는 206을 약속하는 것이 아니라 범위 처리를 열었다

강한 태그가 일치한다고 가정하자. 서버는 여전히 여러 질문에 답해야 한다. 이 대상이 요청된 범위 단위를 지원하는가? 구문이 유효한가? 구간이 현재 선택된 표현과 겹치는가? 요청된 범위들을 합리적으로 제공할 수 있는가?If-Range는 어느 버전 분기가 안전한지를 결정하고, 보통의 범위 의미는 범위가 충족될 수 있는지를 결정한다.

지원되고, 유효하고, 충족 가능한 바이트 범위에 대해서는 보통의 결과가 206 Partial Content다. 단일 파트 응답은Content-Range를 실어 반환된 바이트가 선택된 표현 안에서 어디에 위치하는지 알려 주고, 알려진 경우 전체 길이를 제공한다. 여러 구간은multipart/byteranges를 사용하며 각 파트는 자체 범위 설명을 실는다. 요청된 어떤 구간도 충족 불가능하면 서버는 416 Range Not Satisfiable을 반환하고 현재 길이를 보고할 수 있다.

이 구분들은 메타데이터가 실제로 갖지 못한 권한을 얻는 것을 막는다.Content-Range: bytes 1000000-1999999/5000000은 반환된 100만 바이트가 500만 바이트 표현 안에서 어디에 놓이는지 말한다. 클라이언트의 첫 100만 바이트가 같은 버전에서 왔다고는 말하지 않는다. 그 권한은 공유된 강한 검증자와 그 비교에서 온다. 일치하는 검증자가 클라이언트가 응답 전체를 받았거나 올바르게 기록했다는 것을 증명하지도 않는다.

거짓 분기는 드러내는 방식으로 보수적이다. 검증자 불일치는 현재 표현 전체가 네트워크를 통과하기 때문에 대역폭을 소모할 수 있다. 그러나 그 대체는 일관된다. 거짓 일치는 훨씬 나쁠 수 있다. 클라이언트는 접두사와 접미사가 서로 다른 버전에서 온 하이브리드를 조용히 조립할 수 있다. HTTP는 불확실한 연속성을 보이지 않는 대신 비싸게 만들기를 선택했다.

캐시도 같은 경계를 지켜야 했다

캐시는 불완전한 200 응답, 206 응답 또는 서로 겹치지 않는 여러 범위를 저장할 수 있다. 그로 인해 캐시는 수동적인 상자가 아니라 조립의 참여자가 된다.RFC 7234는 불완전한 응답을Range와Content-Range를 이해하는 캐시만 저장할 수 있도록 허용했다. 여러 범위를 결합하는 것은 같은 강한 검증자를 공유할 때만 허용되었다.

이 규칙은 그럴듯하지만 무효인 최적화를 막는다. 캐시는 좌표들이 파일을 빈틈 없이 덮는다는 이유만으로 월요일의 접두사, 화요일의 중간 구간, 수요일의 접미사를 합칠 수 없다. 커버리지는 연속성이 아니다. 모든 부분은 같은 선택된 표현 버전을 참조해야 하고, 응답 메타데이터는 결합 규칙에 따라 갱신되어야 한다.

RFC 9111은 이 구분을 유지한다. 캐시는 이후의 범위 요청으로 저장된 불완전한 응답을 완성할 수 있지만, 실제로 완성되기 전까지는 결과를 완전한 것으로 사용해서는 안 된다. 부분 응답은 206으로 표시해야 한다. 강한 검증자 근거가 공유되면 범위를 결합할 수 있다. 저장, 재사용, 완성은 여전히 캐시의 로컬 책임이며,If-Range는 이를 원격으로 수행하지 않는다.

이 근거 모델은 소박하지만 내구성 있다. 요청 대상과 선택 입력이 리소스와 변형을 식별한다.Range는 요청된 구간을 기록한다.If-Range는 클라이언트가 이미 보유한 버전 근거를 기록한다. 서버의 현재 ETag 또는 Last-Modified 값이 분기를 뒷받침한다. 상태와 Content-Range는 응답을 묘사한다. 조각들이 실제로 수신되고 조립되고 보관되었다는 것은 오직 클라이언트나 캐시만이 나중에 확립할 수 있다.

작은 조건이 권한을 분산 상태로 유지했다

If-Range의 역사는 다운로드 소프트웨어의 편의 기능으로 오해하기 쉽다. 더 깊은 성과는 버전 결정을 근거를 가진 주체들 가까이에 두었다는 점이다. 원점은 선택된 표현에 대한 검증자를 생성했다. 클라이언트는 그 검증자를 부분 바이트와 함께 보존하고 또 다른 구간을 요청할 가치가 있는지 선택했다. 서버는 현재 상태와 비교하여 응답 형태를 선택했다. 캐시는 저장된 부분들에 대한 자체 보관 기록을 유지했다.

중앙 카탈로그가 파일 버전 신원을 할당할 필요가 없었다. 전송 서비스가 중단된 모든 클라이언트가 돌아올 때까지 리소스를 잠글 필요도 없었다. 공유 필드는 단 하나의 다음 결정에 필요한 만큼의 근거만 실었다. 이 요청된 바이트들을 이미 보유한 표현의 또 다른 부분으로 다룰 수 있는가? 부정적 답은 요청을 벌하지 않았다. 그것은 절약을 철회하고 완전한 현재 사본을 복원했다.

RFC들은 이 거래를 정의할 뿐, 현재의 제품이 어떻게 이를 구현하는지는 보여 주지 않는다. RFC들은 어떤 실제 ETag가 강한지, 중개자가 그것을 올바르게 보존했는지, 서버가 실제로 범위를 지원하는지, 최종 파일이 영구 저장소에 도달했는지를 증명할 수 없다. 그러한 주장에는 요청·응답 캡처, 표현 선택 입력, 검증자 이력, 바이트 해시, 클라이언트의 조립 기록이 필요하다.