요약

  • 원래 IMAP 리터럴은 명령 안에 삽입된, 옥텟 수로 프레이밍된 문자열이었다. 클라이언트는{n}을 선언하고+컨티뉴에이션을 받은 뒤에야 다음n옥텟과 나머지 명령 구문을 전송했다.
  • RFC 2088의 LITERAL+는 표식을{n+}로 바꾸고 그 컨티뉴에이션 왕복을 없앴다. 지연 시간 절감은 또한 서버가 원치 않는 경우에도 이미 허가된 바이트를 비우거나 연결을 닫아야 할 수 있다는 뜻이었다.
  • RFC 7888의 LITERAL-는 같은 와이어 표식을 유지했지만, 요청하지 않은 리터럴을 4096옥텟으로 제한했다. APPENDLIMIT는 전역 또는 사서함별 업로드 정책을 별도로 노출했고, RFC 9051은 나중에 이 제한된 형식을 IMAP4rev2에 포함시켰다.

줄은 명령이 끝나기 전에 끝났다

오래된 IMAP 교환의 시작 부분을 보자.

C: A001 LOGIN {11}

중괄호 안 카운트 뒤의 CRLF는 한 줄의 끝처럼 보였지만, 명령의 끝은 아니었다. 클라이언트는 문자열 11옥텟이 뒤따르겠다고 약속한 상태였다. 따라서 기다려야 했다.+로 시작하는 서버 응답이 온 뒤에야 그 옥텟들이 연결을 건너갈 수 있었고, 그 뒤에 LOGIN의 나머지가 이어졌다.

이것은 구분자 관례가 아니었다. 리터럴 자체가 CR과 LF를 포함할 수 있었다. 리터럴의 끝은 선언된 옥텟 수로 정해졌고, 마지막으로 카운트된 옥텟 직후에 명령 문법이 다시 시작되었다. 공백이 또 다른 인자를 시작할 수 있었고, 이후의 CRLF가 비로소 명령을 완성할 수도 있었다. 따라서 프로토콜 수신자는 두 가지 사실을 동시에 기억해야 했다. 원시 옥텟이 몇 개 남았는지, 그리고 일반 명령 파싱이 어디서 재개되는지.

1996년 12월에 발표된RFC 2060은 리터럴을 IMAP4rev1의 일반 문자열 형식으로 설명했다.RFC 3501은 2003년에 그 규칙을 이어받았다. 서버에서 클라이언트로 가는 리터럴은 카운트 직후에 바로 이어질 수 있었다. 클라이언트에서 서버로 가는 리터럴은 그럴 수 없었다. 방향이 권한을 바꾼 이유는 연결 뒤의 명령 처리 및 저장 자원에 대한 승인 권한이 클라이언트가 아니라 서버에 있었기 때문이다.

{0}조차 기다려야 했다. 0옥텟을 승인한다고 해서 절약되는 페이로드 시간은 없었다. 이 멈춤은 와이어에 여유가 필요해서가 아니라, 그것이 프로토콜의 결정 지점이었기 때문에 존재했다.

컨티뉴에이션은 좁은 범위의 거부권이었다

+응답은 서버가 이 명령의 나머지를 받을 준비가 되었다는 뜻이었다. 명령의 최종 의미를 수락한 것은 아니었다. 뒤에 오는 인자가 여전히 유효하지 않을 수 있었고, APPEND는 여전히 실패할 수 있었으며, 인증은 여전히 거부될 수 있었다. 컨티뉴에이션은 수신자가 바이트를 초대할 만큼의 문맥을 파싱하기 전에 클라이언트가 바이트를 확정하지 못하게 막는 역할만 했다.

그 초대는 조기 오류 처리를 깔끔하게 만들기도 했다. 서버가 명령 접두부에서 문제를 발견하면+대신 태그가 붙은 BAD 완료 응답을 보낼 수 있었다. 그러면 클라이언트는 그 명령의 전송을 중단해야 했다. 원치 않는 리터럴이 바이트 스트림에 들어오지 않았으므로 다음 명령 경계는 모호하지 않게 유지되었다.

이것은 전송 계층 흐름 제어와 다르다. TCP 윈도는 수신 스택이 얼마나 많은 스트림 데이터를 버퍼링할 수 있는지를 말해 준다. 사서함 이름, IMAP 동사, 할당량, 문자열 위치에 대해서는 아무것도 알지 못한다. IMAP 컨티뉴에이션은 하나의 구문적 연속에 대한 애플리케이션 수준의 선택이었다. TCP 용량이 충분해도 IMAP은 여전히 '아직 안 된다'고 말할 수 있었다.

이 설계는 네트워크 왕복 한 번을 대가로 확실성을 얻었다. 로컬 링크에서는 그 멈춤이 거의 보이지 않을 수 있다. 지연이 큰 경로에서는 리터럴마다 명령 안에 요청과 허가의 교환이 삽입되었다. 여러 리터럴을 담은 명령은 그런 정지 지점을 여러 번 쌓을 수 있었다.

더하기 기호 하나가 왕복을 소비했다

1997년 1월에RFC 2088이 나왔다. 이 문서는LITERAL+기능을 도입하고 문법을 한 가지 작게 바꾸었다.

{11+}

서버가 LITERAL+를 광고하면 클라이언트는 이 비동기화 형식을 사용할 수 있었다. 서버는 이에 대해 컨티뉴에이션을 보내지 않았고, 클라이언트는 11옥텟을 보내기 전에 기다릴 필요가 없었다. 기능이 없으면 클라이언트는 원래의 동기화 형식을 유지했다.

RFC 2088의 예시는 그 결과를 구체적으로 보여 준다. LOGIN은 사용자 이름을 위한 비동기화 리터럴 하나와 비밀번호를 위한 또 하나를 담고, 클라이언트는 중간에 서버 왕복 없이 둘 다 전송한다. 프로토콜이 프레이밍을 잃은 것은 아니다. 각 카운트는 여전히 해당 리터럴에 속하는 옥텟 수를 정확히 결정하고, 이후 구문은 같은 명령의 일부로 남는다. 사라진 것은 경계에서의 허가였다.

기능 협상이 중요했던 이유는{n+}가 수신자 동작에 대한 약속이었기 때문이다. 더하기 형식을 인식하기로 합의하지 않은 서버는 이를 오류로 파싱할 수 있었고, 뒤따르는 바이트를 올바르게 비울 것이라고 가정할 수 없었다. 자발적 채택은 모든 기존 엔드포인트를 한꺼번에 재정의하지 않고도 최적화가 퍼지게 했다.

RFC 2088의 보안 섹션은 알려진 보안 문제가 없다고 밝혔다. 그 문장은 1997년에 확장의 저자들이 마무리할 수 있었던 범위를 담고 있었다. 운영 경험이 나중에 자원 보관에 관해 무엇을 드러낼지는 정하지 못했다.

이미 전송 중인 바이트는 거부하기 더 어려웠다

원래 순서에서는 서버가 명령 접두부를 검사하고 리터럴이 도착하기 전에 거부할 수 있었다. LITERAL+는 그 기회를 의도적으로 포기했다. 클라이언트가 아주 큰{n+}를 선언하고 곧바로 데이터를 스트리밍하면, 클라이언트가 여전히n옥텟 전부를 보낼 권한을 가진 동안 서버는 그 명령이 실패해야 한다고 판단할 수 있었다.

2016년 5월에 발표된RFC 7888은 두 가지 매력적이지 않은 선택을 설명한다. 서버는 지정된 바이트를 읽고 폐기한 뒤 명령을 거부할 수 있다. 그러면 스트림 동기화는 유지되지만 대역폭과 수신 측 작업이 낭비된다. 또는 BYE를 보내고 연결을 닫아 재연결을 강제하고, 사용자나 순진한 클라이언트가 오류를 이해하기 더 어렵게 만들 수 있다. 서버는 리터럴을 비우기 전에 BAD나 NO를 보낼 수 있지만, 판정이 빨랐다는 이유만으로 옥텟이 사라지지는 않는다.

APPEND는 이 문제를 첨예하게 만들었다. 대부분의 IMAP 인자는 크지 않다. APPEND는 메시지 전체와 첨부 파일을 담을 수 있다. 서버 구현은 메모리, 저장 공간, 처리를 보호하기 위해 리터럴 크기 제한을 둘 수 있다. 제한 없는 LITERAL+에서는 지연 최적화가 서버가 그 비용을 거부할 실질적인 방법을 갖기 전에 큰 본문이 도착하게 만들 수 있었다.

이것은 책임 소재의 변화였다. 클라이언트는 새 응답 없이 행동함으로써 시간을 얻었다. 그 행동이 로컬 한도를 넘으면 서버가 정리 문제를 떠안았다. 구문은 결정론적으로 남았고, 운영 부담이 이동했다.

LITERAL-는 표식을 바꾸지 않고 고정된 선을 그었다

RFC 7888은 RFC 2088을 폐기하고 두 가지 기능을 명시했다. LITERAL+는 제한 없는 비동기화 리터럴을 유지했다. LITERAL-는 같은{n+}형식을 사용하되 상한을 두었다. 비동기화 리터럴은 4096바이트를 초과해서는 안 된다. 그보다 큰 것은 동기화 형식인{n}으로 돌아가야 한다.

빼기 기호는 각 리터럴이 아니라 광고되는 기능 이름에 나타난다. 서버는 LITERAL+와 LITERAL-를 함께 광고해서는 안 된다. 클라이언트가 그 기능을 확인한 뒤에는{n+}안의 더하기가 협상된 계약에 따라 '이 확장에 의해 제한되지 않음' 또는 '4096까지만 허용됨'을 뜻한다.

그 분리는 미묘하고 유용하다. 문법은 작게 유지되었다. 기존 동기화 리터럴은 계속 유효했다. 작은 문자열은 왕복을 피할 수 있었다. 큰 문자열은 상당한 자원을 소비하기 전에 명시적 승인 지점을 되찾았다. 표준은 모든 서버나 사서함에 맞는 완벽한 임계값을 계산하려 하지 않았다. 대신 이 하나의 무허가 메커니즘에 보편적 최댓값을 만들었다.

이 숫자는 업로드 할당량이 아니다. 3000옥텟짜리 APPEND도 여전히 실패할 수 있고, 20메가바이트짜리 APPEND도 동기화 후에는 수락될 수 있다. LITERAL-는 이 리터럴이 컨티뉴에이션 없이 도착할 수 있는지만 답한다.

LITERAL-가 악용 클라이언트를 무해하게 만드는 것도 아니다. RFC 7888은 비동기화 리터럴이 메모리를 소비하고 서비스 거부 공격을 지원할 수 있으며, 제한된 확장은 상황을 부분적으로만 개선한다고 밝힌다. 많은 작은 리터럴, 비용이 큰 파싱, 반복되는 연결은 여전히 비용을 부과할 수 있다. 임계값은 하나의 노출을 좁힐 뿐 자원 거버넌스를 대체하지 않는다.

APPENDLIMIT는 다른 크기 질문에 답했다

같은 달에RFC 7889, 즉 APPENDLIMIT 확장이 나왔다. 관심사는 문자열이 어떻게 프레이밍되는지가 아니라, 메시지 업로드가 서버의 알려진 정책을 초과하는지 여부였다.

APPENDLIMIT=257890같은 기능은 모든 사서함에 대한 하나의 최댓값을 광고할 수 있다. 값 없는 APPENDLIMIT는 STATUS나 LIST-STATUS를 사용해 사서함별 값을 알아낼 수 있다고 클라이언트에 알린다. 사서함은 숫자나 NIL을 보고할 수 있다. 그러면 협조적인 클라이언트는 이미 너무 크다고 알려진 APPEND를 시도하지 않을 수 있다.

이 구분은 중요하다. LITERAL-의 4096은 비동기화 리터럴 구문에 대한 프로토콜 전반의 상한이다. APPENDLIMIT는 훨씬 클 수 있고, 사서함마다 다를 수 있으며, 메시지 업로드 결정에 적용된다. 동기화든 비동기화든 전송이 시작되기 전에 낭비를 막을 수 있다.

APPENDLIMIT는 근거이지 예약이 아니다. RFC 7889는 계산이 빨라야 하며 ACL, 할당량 또는 다른 조건을 포함할 필요가 없다고 밝힌다. 광고된 값보다 작은 메시지도 여전히 실패할 수 있다. 한도를 알 수 없으면 RFC는 클라이언트가 APPEND에 비동기화 리터럴을 사용하지 말라고 권고한다. 불확실성은 허가 왕복을 되살린다.

이 분할은 오래가는 인터넷 설계 패턴을 따른다. 공통 문법 규칙은 좁고 결정론적으로 유지하고, 운영 정책은 로컬이며 발견 가능하게 남겨 둔다. 하나의 마법 같은 숫자로는 두 가지 역할을 정직하게 표현할 수 없었을 것이다.

IMAP4rev2는 제한된 형식을 일상적인 것으로 만들었다

2021년 8월에 발표된RFC 9051은 LITERAL- 의미론을 IMAP4rev2에 포함시켰다. 이 문서는 동기화 리터럴, 비동기화 리터럴, 인용 문자열을 문자열의 세 가지 형식으로 제시한다. 다른 확장이 달리 말하지 않는 한 비동기화 리터럴은 4096옥텟으로 제한되며, 더 큰 것은 동기화한다.

이 통합은 역사적 태도를 바꾼다. 1997년의 선택적 최적화로 시작해 2016년에 자원 경계를 얻은 것이 현재 기본 문법의 일부가 되었다. 그러나 두 형식 모두 살아남았다. 더 새로운 프로토콜은 기다림을 구식이라고 선언하지 않았다. 사전 허가가 여전히 가치 있는 곳에서는 기다림을 그대로 유지했다.

IANA IMAP Capabilities 레지스트리는 여전히 LITERAL+, LITERAL- 및 APPENDLIMIT를 각각의 표준 참조와 함께 나열한다. 등록은 토큰이 무엇을 뜻하는지 말해 준다. 오늘날 어떤 서버가 이를 광고하는지, 클라이언트가 각 형식을 얼마나 자주 선택하는지, 어떤 성능 및 악용 결과가 나오는지는 보여 주지 않는다.

이 역사는 누가 다음 바이트를 확정할 수 있는지에 관한 것이었다

LITERAL+를 사소한 지연 수정으로, LITERAL-를 나중에 붙은 안전 제한으로 설명하고 싶어지지만, 더 깊은 흐름은 결정 권한에 관한 것이다.

원래 리터럴은 경계마다 수신자가 결정하게 했다. LITERAL+는 기능을 갖춘 서버가 그 결정을 클라이언트에 사전 위임하게 했다. 클라이언트는 더하기 기호 하나를 붙여 즉시 전송함으로써 그 위임을 사용했다. RFC 7888은 큰 비요청 데이터가 거부 비용을 높일 때 그 위임의 범위를 제한했다. APPENDLIMIT는 다른 종류의 사전 근거를 제공해 클라이언트가 알려진 정책 실패를 아예 피할 수 있게 했다.

어떤 형식도 최종 수락을 증명하지 않았다. 어떤 카운트도 저장 공간을 예약하지 않았다. 어떤 기능도 신뢰를 측정하지 않았다. 대신 표준은 각 단언을 작게 유지했다. 정확한 프레이밍, 컨티뉴에이션 준비, 컨티뉴에이션을 건너뛸 허가, 그 허가에 대한 고정 상한, 또는 보고된 업로드 한도.

기다린 바이트들은 우연히 비효율적이었던 것이 아니다. 그 멈춤은 한 시스템이 다른 시스템이 자원을 쓰기 전에 여전히 '아니오'라고 말할 수 있는 자리를 보존했다. 네트워크 왕복을 없애는 것은 표현하기 쉬웠다. 그 제거의 비용을 배분하는 일은 명확히 서술하는 데 19년이 더 걸렸다.

출처 및 증거의 한계

이 출처들은 프로토콜 문법, 기능 이력, 명시된 자원 고려 사항을 확립한다. 현재의 채택률, 제품 동작, 지연 절감, 공격 빈도, 또는 배포된 서비스의 사서함 정책을 측정하지는 않는다. 컨티뉴에이션은 최종 명령 수락이 아니고, 4096은 업로드 할당량이 아니며, APPENDLIMIT는 더 작은 APPEND가 성공하리라는 약속이 아니다.