# The WebSocket Protocol (웹소켓 프로토콜) **초록** WebSocket 프로토콜은 제어된 환경에서 신뢰할 수 없는 코드를 실행하는 클라이언트와 해당 코드와의 통신을 허용한 원격 호스트 간의 양방향 통신을 가능하게 합니다. 이를 위해 사용되는 보안 모델은 웹 브라우저에서 일반적으로 사용되는 출처 기반 보안 모델입니다. 이 프로토콜은 TCP 위에 계층화된 개시 핸드셰이크와 기본 메시지 프레이밍으로 구성됩니다. 이 기술의 목표는 여러 HTTP 연결(예: XMLHttpRequest 또는 `<iframe>`과 롱 폴링 사용)을 열지 않고도 서버와 양방향 통신이 필요한 브라우저 기반 애플리케이션을 위한 메커니즘을 제공하는 것입니다. **이 메모의 상태** 이것은 인터넷 표준 트랙 문서입니다. 이 문서는 IETF(Internet Engineering Task Force)의 산물입니다. 이것은 IETF 커뮤니티의 합의를 나타냅니다. 공개 검토를 거쳤으며 IESG(Internet Engineering Steering Group)의 출판 승인을 받았습니다. 인터넷 표준에 대한 자세한 정보는 RFC 5741의 섹션 2에서 확인할 수 있습니다. 이 문서의 현재 상태, 정오표, 피드백 제공 방법에 대한 정보는 http://www.rfc-editor.org/info/rfc6455 에서 얻을 수 있습니다. **저작권 고지** Copyright (c) 2011 IETF Trust 및 문서 저자로 식별된 사람들. 모든 권리 보유. 이 문서는 BCP 78 및 이 문서의 발행일에 유효한 IETF 문서와 관련된 IETF Trust의 법적 조항(http://trustee.ietf.org/license-info)의 적용을 받습니다. 이 문서는 귀하의 권리와 제한 사항을 설명하므로 주의 깊게 검토하십시오. 이 문서에서 추출한 코드 구성 요소는 Trust 법적 조항의 섹션 4.e에 설명된 대로 Simplified BSD License 텍스트를 포함해야 하며 Simplified BSD License에 설명된 대로 보증 없이 제공됩니다. **목차** 1. 소개 1.1. 배경 1.2. 프로토콜 개요 1.3. 개시 핸드셰이크 1.4. 종료 핸드셰이크 1.5. 설계 철학 1.6. 보안 모델 1.7. TCP 및 HTTP와의 관계 1.8. 연결 설정 1.9. WebSocket 프로토콜을 사용하는 서브프로토콜 2. 적합성 요구 사항 2.1. 용어 및 기타 규칙 3. WebSocket URI 4. 개시 핸드셰이크 4.1. 클라이언트 요구 사항 4.2. 서버 측 요구 사항 4.2.1. 클라이언트의 개시 핸드셰이크 읽기 4.2.2. 서버의 개시 핸드셰이크 전송 4.3. 핸드셰이크에 사용되는 새 헤더 필드에 대한 수집된 ABNF 4.4. 여러 버전의 WebSocket 프로토콜 지원 5. 데이터 프레이밍 5.1. 개요 5.2. 기본 프레이밍 프로토콜 5.3. 클라이언트-서버 마스킹 5.4. 단편화 5.5. 제어 프레임 5.5.1. Close 5.5.2. Ping 5.5.3. Pong 5.6. 데이터 프레임 5.7. 예제 5.8. 확장성 6. 데이터 송수신 6.1. 데이터 전송 6.2. 데이터 수신 7. 연결 종료 7.1. 정의 7.1.1. WebSocket 연결 종료 7.1.2. WebSocket 종료 핸드셰이크 시작 7.1.3. WebSocket 종료 핸드셰이크가 시작됨 7.1.4. WebSocket 연결이 종료됨 7.1.5. WebSocket 연결 종료 코드 7.1.6. WebSocket 연결 종료 이유 7.1.7. WebSocket 연결 실패 7.2. 비정상 종료 7.2.1. 클라이언트 시작 종료 7.2.2. 서버 시작 종료 7.2.3. 비정상 종료로부터 복구 7.3. 정상 연결 종료 7.4. 상태 코드 7.4.1. 정의된 상태 코드 7.4.2. 예약된 상태 코드 범위 8. 오류 처리 8.1. UTF-8 인코딩 데이터의 오류 처리 9. 확장 9.1. 확장 협상 9.2. 알려진 확장 10. 보안 고려 사항 10.1. 비브라우저 클라이언트 10.2. 출처 고려 사항 10.3. 인프라에 대한 공격 (마스킹) 10.4. 구현별 제한 10.5. WebSocket 클라이언트 인증 10.6. 연결 기밀성 및 무결성 10.7. 유효하지 않은 데이터 처리 10.8. WebSocket 핸드셰이크에서 SHA-1 사용 11. IANA 고려 사항 11.1. 새 URI 스킴 등록 11.1.1. "ws" 스킴 등록 11.1.2. "wss" 스킴 등록 11.2. "WebSocket" HTTP 업그레이드 키워드 등록 11.3. 새 HTTP 헤더 필드 등록 11.3.1. Sec-WebSocket-Key 11.3.2. Sec-WebSocket-Extensions 11.3.3. Sec-WebSocket-Accept 11.3.4. Sec-WebSocket-Protocol 11.3.5. Sec-WebSocket-Version 11.4. WebSocket 확장 이름 레지스트리 11.5. WebSocket 서브프로토콜 이름 레지스트리 11.6. WebSocket 버전 번호 레지스트리 11.7. WebSocket 종료 코드 번호 레지스트리 11.8. WebSocket Opcode 레지스트리 11.9. WebSocket 프레이밍 헤더 비트 레지스트리 12. 다른 사양에서 WebSocket 프로토콜 사용 13. 감사의 말 14. 참조 14.1. 규범적 참조 14.2. 정보 제공 참조 # 1. 소개 ## 1.1. 배경 `_이 섹션은 비규범적입니다._` 역사적으로 클라이언트와 서버 간의 양방향 통신이 필요한 웹 애플리케이션(예: 인스턴트 메시징 및 게임 애플리케이션)을 만들려면 HTTP를 남용하여 서버를 폴링하면서 업스트림 알림을 별도의 HTTP 호출로 전송해야 했습니다 [[RFC6202| [RFC6202] ]]. 이로 인해 다양한 문제가 발생합니다: o 서버는 각 클라이언트에 대해 여러 개의 기본 TCP 연결을 사용해야 합니다: 클라이언트에 정보를 보내는 연결 하나와 각 수신 메시지에 대한 새 연결. o 와이어 프로토콜은 오버헤드가 높으며, 각 클라이언트-서버 메시지에 HTTP 헤더가 있습니다. o 클라이언트 측 스크립트는 응답을 추적하기 위해 나가는 연결에서 들어오는 연결로의 매핑을 유지해야 합니다. 더 간단한 솔루션은 양방향 트래픽을 위해 단일 TCP 연결을 사용하는 것입니다. 이것이 WebSocket 프로토콜이 제공하는 것입니다. WebSocket API [[WSAPI| [WSAPI] ]]와 결합하면 웹 페이지에서 원격 서버로의 양방향 통신을 위한 HTTP 폴링의 대안을 제공합니다. 동일한 기술을 다양한 웹 애플리케이션에 사용할 수 있습니다: 게임, 주식 시세 표시기, 동시 편집이 가능한 다중 사용자 애플리케이션, 실시간으로 서버 측 서비스를 노출하는 사용자 인터페이스 등. WebSocket 프로토콜은 기존 인프라(프록시, 필터링, 인증)의 이점을 얻기 위해 HTTP를 전송 계층으로 사용하는 기존 양방향 통신 기술을 대체하도록 설계되었습니다. 이러한 기술은 HTTP가 처음에 양방향 통신을 위해 만들어지지 않았기 때문에 효율성과 신뢰성 간의 절충안으로 구현되었습니다(자세한 논의는 [[RFC6202| [RFC6202] ]] 참조). WebSocket 프로토콜은 기존 HTTP 인프라의 맥락에서 기존 양방향 HTTP 기술의 목표를 다루려고 시도합니다. 따라서 HTTP 포트 80 및 443에서 작동하고 HTTP 프록시 및 중개자를 지원하도록 설계되었으며, 이는 현재 환경에 특정한 일부 복잡성을 의미하더라도 마찬가지입니다. 그러나 설계는 WebSocket을 HTTP로 제한하지 않으며, 향후 구현은 전체 프로토콜을 재발명하지 않고 전용 포트를 통해 더 간단한 핸드셰이크를 사용할 수 있습니다. 대화형 메시징의 트래픽 패턴이 표준 HTTP 트래픽과 밀접하게 일치하지 않고 일부 구성 요소에 비정상적인 부하를 유발할 수 있기 때문에 이 마지막 점이 중요합니다. ## 1.2. 프로토콜 개요 `_이 섹션은 비규범적입니다._` 프로토콜은 두 부분으로 구성됩니다: 핸드셰이크와 데이터 전송. 클라이언트의 핸드셰이크는 다음과 같습니다: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: `dGhlIHNhbXBsZSBub25jZQ==` Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 서버의 핸드셰이크는 다음과 같습니다: HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: `s3pPLMBiTxaQ9kYGzzhZRbK+xOo=` Sec-WebSocket-Protocol: chat 클라이언트의 첫 줄은 Request-Line 형식을 따릅니다. 서버의 첫 줄은 Status-Line 형식을 따릅니다. Request-Line 및 Status-Line 생성은 [[RFC2616| [RFC2616] ]]에 정의되어 있습니다. 두 경우 모두 선행 줄 뒤에 순서가 지정되지 않은 헤더 필드 집합이 옵니다. 이러한 헤더 필드의 의미는 이 문서의 섹션 4에 지정되어 있습니다. 쿠키 [[RFC6265| [RFC6265] ]]와 같은 추가 헤더 필드도 있을 수 있습니다. 헤더의 형식 및 구문 분석은 [[RFC2616| [RFC2616] ]]에 정의된 대로입니다. 클라이언트와 서버가 모두 핸드셰이크를 보내고 핸드셰이크가 성공하면 데이터 전송 부분이 시작됩니다. 이것은 각 측이 다른 쪽과 독립적으로 원하는 대로 데이터를 보낼 수 있는 양방향 통신 채널입니다. 성공적인 핸드셰이크 후 클라이언트와 서버는 이 사양에서 "메시지"라고 하는 개념적 단위로 데이터를 주고받습니다. 와이어에서 메시지는 하나 이상의 프레임으로 구성됩니다. WebSocket 메시지는 단편화된 메시지가 중개자에 의해 통합되거나 분할될 수 있으므로 특정 네트워크 계층 프레이밍에 반드시 해당하지는 않습니다. 프레임에는 연결된 유형이 있습니다. 동일한 메시지에 속하는 각 프레임에는 동일한 유형의 데이터가 포함됩니다. 대략적으로 말하면 텍스트 데이터(UTF-8 [[RFC3629| [RFC3629] ]] 텍스트로 해석됨), 바이너리 데이터(해석은 애플리케이션에 맡김) 및 제어 프레임(애플리케이션을 위한 데이터를 전달하기 위한 것이 아니라 연결을 종료해야 한다는 신호와 같은 프로토콜 수준 신호를 위한 것)에 대한 유형이 있습니다. 이 버전의 프로토콜은 6가지 프레임 유형을 정의하고 10개를 향후 사용을 위해 예약해 둡니다. ## 1.3. 개시 핸드셰이크 `_이 섹션은 비규범적입니다._` 개시 핸드셰이크는 HTTP 기반 서버 측 소프트웨어 및 중개자와 호환되도록 의도되어 있으므로 단일 포트를 해당 서버와 통신하는 HTTP 클라이언트와 해당 서버와 통신하는 WebSocket 클라이언트 모두가 사용할 수 있습니다. 이를 위해 WebSocket 클라이언트의 핸드셰이크는 HTTP 업그레이드 요청입니다: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: `dGhlIHNhbXBsZSBub25jZQ==` Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 [[RFC2616| [RFC2616] ]]을 준수하여 핸드셰이크의 헤더 필드는 클라이언트가 임의의 순서로 보낼 수 있으므로 다른 헤더 필드가 수신되는 순서는 중요하지 않습니다. GET 메서드 [[RFC2616| [RFC2616] ]]의 "Request-URI"는 WebSocket 연결의 엔드포인트를 식별하는 데 사용되며, 하나의 IP 주소에서 여러 도메인을 제공하고 단일 서버에서 여러 WebSocket 엔드포인트를 제공할 수 있도록 합니다. 클라이언트는 [[RFC2616| [RFC2616] ]]에 따라 핸드셰이크의 |Host| 헤더 필드에 호스트 이름을 포함하므로 클라이언트와 서버 모두 사용 중인 호스트에 대해 동의하는지 확인할 수 있습니다. 추가 헤더 필드는 WebSocket 프로토콜에서 옵션을 선택하는 데 사용됩니다. 이 버전에서 사용할 수 있는 일반적인 옵션은 서브프로토콜 선택기(|Sec-WebSocket-Protocol|), 클라이언트가 지원하는 확장 목록(|Sec-WebSocket-Extensions|), |Origin| 헤더 필드 등입니다. |Sec-WebSocket-Protocol| 요청 헤더 필드를 사용하여 클라이언트가 허용할 수 있는 서브프로토콜(WebSocket 프로토콜 위에 계층화된 애플리케이션 수준 프로토콜)을 나타낼 수 있습니다. 서버는 허용 가능한 프로토콜 중 하나 또는 없음을 선택하고 해당 프로토콜을 선택했음을 나타내기 위해 핸드셰이크에서 해당 값을 에코합니다. Sec-WebSocket-Protocol: chat |Origin| 헤더 필드 [[RFC6454| [RFC6454] ]]는 웹 브라우저에서 WebSocket API를 사용하는 스크립트에 의한 WebSocket 서버의 무단 크로스 오리진 사용을 방지하는 데 사용됩니다. 서버는 WebSocket 연결 요청을 생성하는 스크립트의 출처를 알게 됩니다. 서버가 이 출처의 연결을 수락하고 싶지 않으면 적절한 HTTP 오류 코드를 전송하여 연결을 거부할 수 있습니다. 이 헤더 필드는 브라우저 클라이언트에서 전송됩니다. 비브라우저 클라이언트의 경우 해당 클라이언트의 컨텍스트에서 의미가 있는 경우 이 헤더 필드를 보낼 수 있습니다. 마지막으로 서버는 클라이언트의 WebSocket 핸드셰이크를 받았음을 클라이언트에게 증명해야 하므로 서버는 WebSocket 연결이 아닌 연결을 수락하지 않습니다. 이렇게 하면 공격자가 XMLHttpRequest [XMLHttpRequest] 또는 양식 제출을 사용하여 신중하게 작성된 패킷을 전송하여 WebSocket 서버를 속이는 것을 방지할 수 있습니다. 핸드셰이크가 수신되었음을 증명하기 위해 서버는 두 가지 정보를 가져와 결합하여 응답을 형성해야 합니다. 첫 번째 정보는 클라이언트 핸드셰이크의 |Sec-WebSocket-Key| 헤더 필드에서 가져옵니다: Sec-WebSocket-Key: `dGhlIHNhbXBsZSBub25jZQ==` 이 헤더 필드의 경우 서버는 값(헤더 필드에 있는 대로, 예: 선행 및 후행 공백을 뺀 base64로 인코딩된 [[RFC4648| [RFC4648] ]] 버전)을 가져와 WebSocket 프로토콜을 이해하지 못하는 네트워크 엔드포인트에서 사용할 가능성이 낮은 GUID(전역 고유 식별자, [[RFC4122| [RFC4122] ]]) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"을 문자열 형식으로 연결해야 합니다. 이 연결의 SHA-1 해시(160비트) [FIPS.180-3], base64로 인코딩된(섹션 4의 [[RFC4648| [RFC4648] ]] 참조)이 서버의 핸드셰이크에서 반환됩니다. 구체적으로 위의 예에서와 같이 |Sec-WebSocket-Key| 헤더 필드의 값이 `"dGhlIHNhbXBsZSBub25jZQ=="`인 경우 서버는 문자열 `"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"`을 연결하여 문자열 `"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"`을 형성합니다. 그런 다음 서버는 이것의 SHA-1 해시를 가져와 값 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea를 얻습니다. 이 값은 base64로 인코딩되어(섹션 4의 [[RFC4648| [RFC4648] ]] 참조) 값 `"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="`를 제공합니다. 이 값은 |Sec-WebSocket-Accept| 헤더 필드에서 에코됩니다. 서버의 핸드셰이크는 클라이언트 핸드셰이크보다 훨씬 간단합니다. 첫 번째 줄은 상태 코드 101을 포함하는 HTTP Status-Line입니다: HTTP/1.1 101 Switching Protocols 101 이외의 상태 코드는 WebSocket 핸드셰이크가 완료되지 않았으며 HTTP의 의미가 여전히 적용됨을 나타냅니다. 헤더는 상태 코드를 따릅니다. |Connection| 및 |Upgrade| 헤더 필드는 HTTP 업그레이드를 완료합니다. |Sec-WebSocket-Accept| 헤더 필드는 서버가 연결을 수락할 의향이 있는지 여부를 나타냅니다. 있는 경우 이 헤더 필드는 미리 정의된 GUID와 함께 |Sec-WebSocket-Key|에서 전송된 클라이언트의 논스 해시를 포함해야 합니다. 다른 값은 서버의 연결 수락으로 해석되어서는 안 됩니다. HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: `s3pPLMBiTxaQ9kYGzzhZRbK+xOo=` 이러한 필드는 스크립팅된 페이지에 대해 WebSocket 클라이언트에서 확인됩니다. |Sec-WebSocket-Accept| 값이 예상 값과 일치하지 않거나 헤더 필드가 누락되거나 HTTP 상태 코드가 101이 아닌 경우 연결이 설정되지 않고 WebSocket 프레임이 전송되지 않습니다. 옵션 필드도 포함될 수 있습니다. 이 버전의 프로토콜에서 주요 옵션 필드는 서버가 선택한 서브프로토콜을 나타내는 |Sec-WebSocket-Protocol|입니다. WebSocket 클라이언트는 서버가 WebSocket 클라이언트의 핸드셰이크에 지정된 값 중 하나를 포함했는지 확인합니다. 여러 서브프로토콜을 사용하는 서버는 클라이언트의 핸드셰이크를 기반으로 하나를 선택하고 핸드셰이크에서 지정해야 합니다. Sec-WebSocket-Protocol: chat 서버는 [[RFC6265| [RFC6265] ]]에 설명된 대로 `_set_cookies`에 쿠키 관련 옵션 필드를 설정할 수도 있습니다. ## 1.4. 종료 핸드셰이크 `_이 섹션은 비규범적입니다._` 종료 핸드셰이크는 개시 핸드셰이크보다 훨씬 간단합니다. 어느 피어든 지정된 제어 시퀀스를 포함하는 데이터가 있는 제어 프레임을 전송하여 종료 핸드셰이크를 시작할 수 있습니다(섹션 5.5.1에 자세히 설명). 이러한 프레임을 수신하면 다른 피어는 아직 보내지 않은 경우 응답으로 Close 프레임을 보냅니다. `_해당_` 제어 프레임을 수신하면 첫 번째 피어는 더 이상 데이터가 들어오지 않는다는 것을 알고 안전하게 연결을 종료합니다. 연결을 종료해야 함을 나타내는 제어 프레임을 보낸 후 피어는 추가 데이터를 보내지 않습니다. 연결을 종료해야 함을 나타내는 제어 프레임을 수신한 후 피어는 수신된 추가 데이터를 버립니다. 양쪽 피어가 이 핸드셰이크를 동시에 시작하는 것은 안전합니다. 종료 핸드셰이크는 TCP 종료 핸드셰이크(FIN/ACK)를 보완하기 위한 것입니다. 특히 가로채는 프록시 및 기타 중개자가 있는 경우 TCP 종료 핸드셰이크가 항상 엔드투엔드로 신뢰할 수 있는 것은 아니기 때문입니다. Close 프레임을 보내고 응답으로 Close 프레임을 기다림으로써 데이터가 불필요하게 손실될 수 있는 특정 경우를 피할 수 있습니다. 예를 들어 일부 플랫폼에서는 수신 대기열에 데이터가 있는 소켓이 종료되면 RST 패킷이 전송되며, 이로 인해 읽기를 기다리는 데이터가 있더라도 RST를 수신한 쪽에서 recv()가 실패합니다. ## 1.5. 설계 철학 `_이 섹션은 비규범적입니다._` WebSocket 프로토콜은 최소한의 프레이밍이 있어야 한다는 원칙에 따라 설계되었습니다(존재하는 유일한 프레이밍은 프로토콜을 스트림 기반이 아닌 프레임 기반으로 만들고 유니코드 텍스트와 바이너리 프레임 간의 구별을 지원하기 위한 것입니다). 메타데이터는 애플리케이션 계층에 의해 TCP 위에 메타데이터가 계층화되는 것과 같은 방식으로 애플리케이션 계층에 의해 WebSocket 위에 계층화될 것으로 예상됩니다(예: HTTP). 개념적으로 WebSocket은 실제로 다음을 수행하는 TCP 위의 계층일 뿐입니다: o 브라우저를 위한 웹 출처 기반 보안 모델 추가 o 하나의 포트에서 여러 서비스를 지원하고 하나의 IP 주소에서 여러 호스트 이름을 지원하기 위한 주소 지정 및 프로토콜 이름 지정 메커니즘 추가 o TCP가 구축된 IP 패킷 메커니즘으로 돌아가기 위해 TCP 위에 프레이밍 메커니즘을 계층화하되 길이 제한 없음 o 프록시 및 기타 중개자가 있는 상태에서 작동하도록 설계된 대역 내 추가 종료 핸드셰이크 포함 그 외에 WebSocket은 아무것도 추가하지 않습니다. 기본적으로 웹의 제약을 고려할 때 가능한 한 스크립트에 원시 TCP를 노출하는 것에 가깝도록 의도되었습니다. 또한 핸드셰이크가 유효한 HTTP 업그레이드 요청이 되도록 하여 서버가 HTTP 서버와 포트를 공유할 수 있도록 설계되었습니다. 개념적으로 다른 프로토콜을 사용하여 클라이언트-서버 메시징을 설정할 수 있지만 WebSocket의 의도는 HTTP 및 배포된 HTTP 인프라(예: 프록시)와 공존할 수 있고 보안 고려 사항을 고려할 때 이러한 인프라와 함께 사용하기에 안전한 TCP에 가까운 상대적으로 간단한 프로토콜을 제공하는 것이며, 사용을 단순화하고 간단한 것을 간단하게 유지하기 위한 대상 추가(예: 메시지 의미 추가)를 포함합니다. 프로토콜은 확장 가능하도록 의도되었습니다. 향후 버전에서는 멀티플렉싱과 같은 추가 개념을 도입할 가능성이 높습니다. ## 1.6. 보안 모델 `_이 섹션은 비규범적입니다._` WebSocket 프로토콜은 웹 페이지에서 WebSocket 프로토콜을 사용할 때 어떤 웹 페이지가 WebSocket 서버에 연결할 수 있는지 제한하기 위해 웹 브라우저에서 사용하는 출처 모델을 사용합니다. 물론 WebSocket 프로토콜이 전용 클라이언트에서 직접 사용되는 경우(즉, 웹 브라우저를 통한 웹 페이지가 아닌) 클라이언트가 임의의 출처 문자열을 제공할 수 있으므로 출처 모델은 유용하지 않습니다. 이 프로토콜은 SMTP [[RFC5321| [RFC5321] ]] 및 HTTP와 같은 기존 프로토콜의 서버와 연결을 설정하지 못하도록 의도되었으며, 원하는 경우 HTTP 서버가 이 프로토콜을 지원하도록 선택할 수 있도록 합니다. 이것은 엄격하고 정교한 핸드셰이크를 갖고 핸드셰이크가 완료되기 전에 연결에 삽입할 수 있는 데이터를 제한함으로써 달성됩니다(따라서 서버가 영향을 받을 수 있는 정도를 제한함). 마찬가지로 다른 프로토콜, 특히 HTTP의 데이터가 WebSocket 서버로 전송될 때 연결을 설정하지 못하도록 의도되었습니다. 예를 들어 HTML "양식"이 WebSocket 서버에 제출되는 경우가 발생할 수 있습니다. 이것은 주로 서버가 핸드셰이크를 읽었음을 증명하도록 요구함으로써 달성되며, 핸드셰이크에 적절한 부분이 포함된 경우에만 그렇게 할 수 있으며, 이는 WebSocket 클라이언트만 보낼 수 있습니다. 특히 이 사양을 작성하는 시점에 |Sec-|로 시작하는 필드는 XMLHttpRequest [XMLHttpRequest]와 같은 HTML 및 JavaScript API만 사용하여 웹 브라우저의 공격자가 설정할 수 없습니다. ## 1.7. TCP 및 HTTP와의 관계 `_이 섹션은 비규범적입니다._` WebSocket 프로토콜은 독립적인 TCP 기반 프로토콜입니다. HTTP와의 유일한 관계는 핸드셰이크가 HTTP 서버에 의해 업그레이드 요청으로 해석된다는 것입니다. 기본적으로 WebSocket 프로토콜은 일반 WebSocket 연결에 포트 80을 사용하고 TLS(전송 계층 보안) [[RFC2818| [RFC2818] ]]를 통해 터널링된 WebSocket 연결에 포트 443을 사용합니다. ## 1.8. 연결 설정 `_이 섹션은 비규범적입니다._` HTTP 서버가 공유하는 포트에 연결이 이루어져야 하는 경우(포트 80 및 443으로의 트래픽에서 발생할 가능성이 매우 높은 상황) 연결은 HTTP 서버에 업그레이드 제안이 있는 일반 GET 요청으로 나타납니다. 단일 호스트 이름에 대한 모든 트래픽에 대해 하나의 IP 주소와 단일 서버만 있는 비교적 간단한 설정에서 이를 통해 WebSocket 프로토콜을 기반으로 하는 시스템을 배포하는 실용적인 방법이 가능할 수 있습니다. 더 정교한 설정(예: 로드 밸런서 및 여러 서버 포함)에서는 HTTP 서버와 별도의 WebSocket 연결을 위한 전용 호스트 세트가 관리하기 더 쉬울 것입니다. 이 사양을 작성하는 시점에서 포트 80 및 443의 연결은 성공률이 크게 다르며 포트 443의 연결이 성공할 가능성이 훨씬 높지만 시간이 지남에 따라 변경될 수 있습니다. ## 1.9. WebSocket 프로토콜을 사용하는 서브프로토콜 `_이 섹션은 비규범적입니다._` 클라이언트는 핸드셰이크에 |Sec-WebSocket-Protocol| 필드를 포함하여 서버가 특정 서브프로토콜을 사용하도록 요청할 수 있습니다. 지정된 경우 서버는 연결을 설정하기 위해 응답에 동일한 필드와 선택한 서브프로토콜 값 중 하나를 포함해야 합니다. 이러한 서브프로토콜 이름은 섹션 11.5에 따라 등록되어야 합니다. 잠재적 충돌을 피하기 위해 서브프로토콜 작성자의 도메인 이름의 ASCII 버전을 포함하는 이름을 사용하는 것이 좋습니다. 예를 들어 Example Corporation이 많은 서버에서 구현할 채팅 서브프로토콜을 만드는 경우 "chat.example.com"으로 이름을 지정할 수 있습니다. Example Organization이 경쟁 서브프로토콜을 "chat.example.org"라고 부르면 두 서브프로토콜을 서버에서 동시에 구현할 수 있으며 서버는 클라이언트가 보낸 값을 기반으로 사용할 서브프로토콜을 동적으로 선택합니다. 서브프로토콜은 서브프로토콜 이름을 변경하여 이전 버전과 호환되지 않는 방식으로 버전을 지정할 수 있습니다. 예를 들어 "bookings.example.net"에서 "v2.bookings.example.net"로 이동합니다. 이러한 서브프로토콜은 WebSocket 클라이언트에 의해 완전히 별개로 간주됩니다. 이전 버전과 호환되는 버전 관리는 동일한 서브프로토콜 문자열을 재사용하되 이러한 종류의 확장성을 지원하도록 실제 서브프로토콜을 신중하게 설계함으로써 구현할 수 있습니다. # 2. 적합성 요구 사항 이 사양의 모든 다이어그램, 예제 및 참고 사항은 비규범적이며, 명시적으로 비규범적으로 표시된 모든 섹션도 마찬가지입니다. 이 사양의 다른 모든 것은 규범적입니다. 이 문서에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL"이라는 핵심 단어는 [[RFC2119| [RFC2119] ]]에 설명된 대로 해석됩니다. 알고리즘의 일부로 명령형으로 표현된 요구 사항(예: "선행 공백 문자 제거" 또는 "false를 반환하고 이 단계 중단")은 알고리즘을 도입하는 데 사용된 핵심 단어("MUST", "SHOULD", "MAY" 등)의 의미로 해석됩니다. 알고리즘 또는 특정 단계로 표현된 적합성 요구 사항은 최종 결과가 동등한 한 어떤 방식으로든 구현될 수 있습니다. (특히 이 사양에 정의된 알고리즘은 따라 하기 쉽도록 의도되었으며 성능을 위한 것이 아닙니다.) ## 2.1. 용어 및 기타 규칙 `_ASCII_`는 [ANSI.X3-4.1986]에 정의된 문자 인코딩 체계를 의미합니다. 이 문서는 UTF-8 값을 참조하고 STD 63 [[RFC3629| [RFC3629] ]]에 정의된 UTF-8 표기 형식을 사용합니다. 명명된 알고리즘 또는 정의와 같은 핵심 용어는 `_이렇게_` 표시됩니다. 헤더 필드 또는 변수의 이름은 |이렇게| 표시됩니다. 변수 값은 /이렇게/ 표시됩니다. 이 문서는 `_WebSocket 연결 실패_` 절차를 참조합니다. 이 절차는 섹션 7.1.7에 정의되어 있습니다. `_문자열을 ASCII 소문자로 변환_`한다는 것은 U+0041에서 U+005A 범위(즉, LATIN CAPITAL LETTER A에서 LATIN CAPITAL LETTER Z까지)의 모든 문자를 U+0061에서 U+007A 범위(즉, LATIN SMALL LETTER A에서 LATIN SMALL LETTER Z까지)의 해당 문자로 바꾸는 것을 의미합니다. `_ASCII 대소문자 구분 없이_` 두 문자열을 비교한다는 것은 U+0041에서 U+005A 범위(즉, LATIN CAPITAL LETTER A에서 LATIN CAPITAL LETTER Z까지)의 문자와 U+0061에서 U+007A 범위(즉, LATIN SMALL LETTER A에서 LATIN SMALL LETTER Z까지)의 해당 문자도 일치하는 것으로 간주한다는 점을 제외하고 코드 포인트별로 정확하게 비교하는 것을 의미합니다. "URI"라는 용어는 [[RFC3986| [RFC3986] ]]에 정의된 대로 이 문서에서 사용됩니다. 구현이 WebSocket 프로토콜의 일부로 데이터를 `_전송_`하도록 요구되는 경우 구현은 실제 전송을 임의로 지연시킬 수 있습니다. 예를 들어 더 적은 IP 패킷을 보내기 위해 데이터를 버퍼링합니다. 이 문서는 다른 섹션에서 [[RFC5234| [RFC5234] ]] 및 [[RFC2616| [RFC2616] ]] 변형의 ABNF를 모두 사용합니다. # 3. WebSocket URI 이 사양은 RFC 5234 [[RFC5234| [RFC5234] ]]에 정의된 ABNF 구문과 URI 사양 RFC 3986 [[RFC3986| [RFC3986] ]]에 정의된 용어 및 ABNF 생성을 사용하여 두 개의 URI 스킴을 정의합니다. ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] host = <host, [[RFC3986| [RFC3986] ]], 섹션 3.2.2에 정의> port = <port, [[RFC3986| [RFC3986] ]], 섹션 3.2.3에 정의> path = <path-abempty, [[RFC3986| [RFC3986] ]], 섹션 3.3에 정의> query = <query, [[RFC3986| [RFC3986] ]], 섹션 3.4에 정의> 포트 구성 요소는 선택 사항입니다. "ws"의 기본값은 포트 80이고 "wss"의 기본값은 포트 443입니다. 스킴 구성 요소가 대소문자 구분 없이 "wss"와 일치하는 경우 URI를 "보안"이라고 합니다(그리고 "보안 플래그가 설정됨"이라고 합니다). "resource-name"(섹션 4.1에서 /resource name/이라고도 함)은 다음을 연결하여 구성할 수 있습니다: o 경로 구성 요소가 비어 있는 경우 "/" o 경로 구성 요소 o 쿼리 구성 요소가 비어 있지 않은 경우 "?" o 쿼리 구성 요소 조각 식별자는 WebSocket URI의 컨텍스트에서 의미가 없으므로 이러한 URI에 사용해서는 안 됩니다. 다른 URI 스킴과 마찬가지로 조각의 시작을 나타내지 않는 경우 문자 "#"은 %23으로 이스케이프되어야 합니다. # 4. 개시 핸드셰이크 ## 4.1. 클라이언트 요구 사항 `_WebSocket 연결 설정_`을 위해 클라이언트는 연결을 열고 이 섹션에 정의된 대로 핸드셰이크를 보냅니다. 연결은 처음에 CONNECTING 상태로 정의됩니다. 클라이언트는 섹션 3에서 설명한 WebSocket URI의 구성 요소인 /host/, /port/, /resource name/, /secure/ 플래그와 함께 사용할 /protocols/ 및 /extensions/ 목록을 제공해야 합니다. 또한 클라이언트가 웹 브라우저인 경우 /origin/을 제공합니다. 모바일 단말기가 특정 통신사에 연결된 브라우저와 같은 제어된 환경에서 실행되는 클라이언트는 연결 관리를 네트워크의 다른 에이전트로 오프로드할 수 있습니다. 이러한 상황에서 이 사양의 목적상 클라이언트는 단말기 소프트웨어와 그러한 에이전트를 모두 포함하는 것으로 간주됩니다. 클라이언트가 사용할 /protocols/ 및 /extensions/ 목록과 함께 (/host/, /port/, /resource name/, /secure/ 플래그)의 집합이 주어진 `_WebSocket 연결 설정_`을 해야 하는 경우, 그리고 웹 브라우저의 경우 /origin/이 주어진 경우 연결을 열고 개시 핸드셰이크를 보내고 응답으로 서버의 핸드셰이크를 읽어야 합니다. 연결을 열어야 하는 방법, 개시 핸드셰이크에서 보내야 하는 내용, 서버의 응답을 해석하는 방법에 대한 정확한 요구 사항은 이 섹션에서 다음과 같습니다. 다음 텍스트에서 우리는 해당 섹션에 정의된 대로 "/host/" 및 "/secure/ 플래그"와 같은 섹션 3의 용어를 사용합니다. 1. 이 알고리즘에 전달된 WebSocket URI의 구성 요소(/host/, /port/, /resource name/, /secure/ 플래그)는 섹션 3에 지정된 WebSocket URI 사양에 따라 유효해야 합니다. 구성 요소 중 하나라도 유효하지 않으면 클라이언트는 `_WebSocket 연결 실패_`를 하고 이 단계를 중단해야 합니다. 2. 클라이언트가 동일한 IP 주소 및 포트 쌍에 대한 WebSocket 연결을 이미 가지고 있는 경우, 원격 호스트가 다른 이름으로 알려져 있더라도 클라이언트는 해당 연결이 설정될 때까지 또는 해당 연결이 실패할 때까지 기다려야 합니다. IP 주소 및 포트 쌍에 대해 CONNECTING 상태의 연결이 두 개 이상 있어서는 안 됩니다. 동일한 IP 주소 및 포트 쌍에 대한 여러 연결이 동시에 시도되는 경우 클라이언트는 다음 단계를 실행하는 연결이 한 번에 하나 이상 없도록 직렬화해야 합니다. 클라이언트가 원격 호스트의 IP 주소를 확인할 수 없는 경우(예: 모든 통신이 자체적으로 DNS 쿼리를 수행하는 프록시 서버를 통해 이루어지기 때문에) 클라이언트는 이 단계의 목적상 각 호스트 이름이 고유한 원격 호스트를 참조한다고 가정해야 하며, 대신 클라이언트는 동시 보류 연결의 총 수를 합리적으로 낮은 수로 제한해야 합니다(예: 클라이언트는 a.example.com 및 b.example.com에 대한 동시 보류 연결을 허용할 수 있지만 단일 호스트에 대한 30개의 동시 연결이 요청되는 경우 허용되지 않을 수 있음). 예를 들어 웹 브라우저 컨텍스트에서 클라이언트는 동시 보류 연결 수에 대한 제한을 설정할 때 사용자가 연 탭 수를 고려해야 합니다. 참고: 이렇게 하면 스크립트가 많은 수의 WebSocket 연결을 원격 호스트에 열어 서비스 거부 공격을 수행하기가 더 어려워집니다. 서버는 공격을 받을 때 연결을 닫기 전에 일시 중지하여 자체 부하를 더 줄일 수 있습니다. 이렇게 하면 클라이언트가 재연결하는 속도가 감소합니다. 참고: 클라이언트가 단일 원격 호스트와 가질 수 있는 설정된 WebSocket 연결 수에는 제한이 없습니다. 서버는 기존 연결이 과도한 호스트/IP 주소의 연결을 수락하지 않거나 높은 부하가 발생할 때 리소스를 많이 사용하는 연결을 끊을 수 있습니다. 3. `_프록시 사용_`: 클라이언트가 WebSocket 프로토콜을 사용하여 호스트 /host/ 및 포트 /port/에 연결할 때 프록시를 사용하도록 구성된 경우 클라이언트는 해당 프록시에 연결하고 /host/로 지정된 호스트와 /port/로 지정된 포트에 TCP 연결을 열도록 요청해야 합니다. 예: 예를 들어 클라이언트가 모든 트래픽에 대해 HTTP 프록시를 사용하는 경우 서버 example.com의 포트 80에 연결하려고 하면 프록시 서버에 다음 줄을 보낼 수 있습니다: CONNECT example.com:80 HTTP/1.1 Host: example.com 비밀번호가 있는 경우 연결은 다음과 같을 수 있습니다: CONNECT example.com:80 HTTP/1.1 Host: example.com Proxy-authorization: Basic ZWRuYW1vZGU6bm9jYXBlcyE= 클라이언트가 프록시를 사용하도록 구성되지 않은 경우 /host/로 지정된 호스트와 /port/로 지정된 포트에 직접 TCP 연결을 열어야 합니다. 참고: WebSocket 연결에 대해 다른 프록시와 별도로 프록시를 선택하기 위한 명시적 UI를 노출하지 않는 구현은 가능한 경우 WebSocket 연결에 대해 SOCKS5 [[RFC1928| [RFC1928] ]] 프록시를 사용하거나 실패하면 HTTP 연결에 대해 구성된 프록시보다 HTTPS 연결에 대해 구성된 프록시를 선호하는 것이 좋습니다. 프록시 자동 구성 스크립트의 목적상 함수에 전달할 URI는 섹션 3에 제공된 WebSocket URI 정의를 사용하여 /host/, /port/, /resource name/, /secure/ 플래그에서 구성되어야 합니다. 참고: WebSocket 프로토콜은 스킴("ws"는 암호화되지 않은 연결, "wss"는 암호화된 연결)에서 프록시 자동 구성 스크립트에서 식별될 수 있습니다. 4. 직접 연결이 실패했거나 사용된 프록시가 오류를 반환했기 때문에 연결을 열 수 없는 경우 클라이언트는 `_WebSocket 연결 실패_`를 하고 연결 시도를 중단해야 합니다. 5. /secure/가 true인 경우 클라이언트는 연결을 연 후 핸드셰이크 데이터를 보내기 전에 연결에서 TLS 핸드셰이크를 수행해야 합니다 [[RFC2818| [RFC2818] ]]. 이것이 실패하면(예: 서버의 인증서를 확인할 수 없음) 클라이언트는 `_WebSocket 연결 실패_`를 하고 연결을 중단해야 합니다. 그렇지 않으면 이 채널의 모든 추가 통신은 암호화된 터널을 통해 실행되어야 합니다 [[RFC5246| [RFC5246] ]]. 클라이언트는 TLS 핸드셰이크에서 Server Name Indication 확장을 사용해야 합니다 [[RFC6066| [RFC6066] ]]. 서버에 대한 연결이 설정되면(프록시를 통한 연결 또는 TLS 암호화 터널을 통한 연결 포함) 클라이언트는 서버에 개시 핸드셰이크를 보내야 합니다. 핸드셰이크는 필수 및 선택적 헤더 필드 목록과 함께 HTTP 업그레이드 요청으로 구성됩니다. 이 핸드셰이크에 대한 요구 사항은 다음과 같습니다. 1. 핸드셰이크는 [[RFC2616| [RFC2616] ]]에 지정된 유효한 HTTP 요청이어야 합니다. 2. 요청의 메서드는 GET이어야 하며 HTTP 버전은 최소 1.1이어야 합니다. 예를 들어 WebSocket URI가 "ws://example.com/chat"인 경우 보내야 하는 첫 번째 줄은 "GET /chat HTTP/1.1"이어야 합니다. 3. 요청의 "Request-URI" 부분은 섹션 3에 정의된 /resource name/(상대 URI)과 일치하거나 구문 분석될 때 해당 ws/wss URI와 일치하는 /resource name/, /host/, /port/를 가진 절대 http/https URI여야 합니다. 4. 요청은 /host/와 선택적으로 뒤따르는 ":"과 /port/(기본 포트를 사용하지 않는 경우)를 포함하는 값을 가진 |Host| 헤더 필드를 포함해야 합니다. 5. 요청은 "websocket" 키워드를 포함해야 하는 값을 가진 |Upgrade| 헤더 필드를 포함해야 합니다. 6. 요청은 "Upgrade" 토큰을 포함해야 하는 값을 가진 |Connection| 헤더 필드를 포함해야 합니다. 7. 요청은 |Sec-WebSocket-Key|라는 이름의 헤더 필드를 포함해야 합니다. 이 헤더 필드의 값은 무작위로 선택된 16바이트 값으로 구성된 논스여야 하며 base64로 인코딩되어야 합니다([[RFC4648| [RFC4648] ]]의 섹션 4 참조). 논스는 각 연결에 대해 무작위로 선택되어야 합니다. 참고: 예를 들어 무작위로 선택된 값이 바이트 시퀀스 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10인 경우 헤더 필드의 값은 `"AQIDBAUGBwgJCgsMDQ4PEA=="`가 됩니다. 8. 요청이 브라우저 클라이언트에서 오는 경우 |Origin| [[RFC6454| [RFC6454] ]]이라는 이름의 헤더 필드를 포함해야 합니다. 연결이 비브라우저 클라이언트에서 온 경우 해당 클라이언트의 의미가 브라우저 클라이언트에 대해 여기에 설명된 사용 사례와 일치하는 경우 이 헤더 필드를 포함할 수 있습니다. 이 헤더 필드의 값은 연결을 설정하는 코드가 실행되는 컨텍스트의 출처에 대한 ASCII 직렬화입니다. 이 헤더 필드 값이 구성되는 방법에 대한 자세한 내용은 [[RFC6454| [RFC6454] ]]를 참조하십시오. 예를 들어 www.example.com에서 다운로드한 코드가 ww2.example.com에 대한 연결을 설정하려고 시도하는 경우 헤더 필드의 값은 "http://www.example.com"이 됩니다. 9. 요청은 |Sec-WebSocket-Version|이라는 이름의 헤더 필드를 포함해야 합니다. 이 헤더 필드의 값은 13이어야 합니다. 참고: 이 문서의 초안 버전(-09, -10, -11, -12)이 게시되었지만(주로 편집 변경 및 명확화로 구성되었으며 와이어 프로토콜에 대한 변경은 아님) 값 9, 10, 11, 12는 Sec-WebSocket-Version에 대한 유효한 값으로 사용되지 않았습니다. 이러한 값은 IANA 레지스트리에 예약되었지만 사용되지 않았으며 사용되지 않을 것입니다. 10. 요청은 |Sec-WebSocket-Protocol|이라는 이름의 헤더 필드를 포함할 수 있습니다. 있는 경우 이 값은 클라이언트가 사용하려는 쉼표로 구분된 하나 이상의 서브프로토콜을 우선 순위에 따라 나타냅니다. 이 값을 구성하는 요소는 [[RFC2616| [RFC2616] ]]에 정의된 구분 문자를 포함하지 않는 U+0021에서 U+007E 범위의 문자를 가진 비어 있지 않은 문자열이어야 하며 모두 고유한 문자열이어야 합니다. 이 헤더 필드 값의 ABNF는 1#token이며, 여기서 구성 및 규칙의 정의는 [[RFC2616| [RFC2616] ]]에 제공된 대로입니다. 11. 요청은 |Sec-WebSocket-Extensions|라는 이름의 헤더 필드를 포함할 수 있습니다. 있는 경우 이 값은 클라이언트가 사용하려는 프로토콜 수준 확장을 나타냅니다. 이 헤더 필드의 해석 및 형식은 섹션 9.1에 설명되어 있습니다. 12. 요청은 쿠키 [[RFC6265| [RFC6265] ]] 및/또는 |Authorization| 헤더 필드 [[RFC2616| [RFC2616] ]]와 같은 인증 관련 헤더 필드와 같은 다른 헤더 필드를 포함할 수 있으며, 이는 정의하는 문서에 따라 처리됩니다. 클라이언트의 개시 핸드셰이크가 전송되면 클라이언트는 추가 데이터를 보내기 전에 서버의 응답을 기다려야 합니다. 클라이언트는 다음과 같이 서버의 응답을 검증해야 합니다: 1. 서버로부터 받은 상태 코드가 101이 아닌 경우 클라이언트는 HTTP [[RFC2616| [RFC2616] ]] 절차에 따라 응답을 처리합니다. 특히 클라이언트는 401 상태 코드를 받으면 인증을 수행할 수 있습니다. 서버는 3xx 상태 코드를 사용하여 클라이언트를 리디렉션할 수 있습니다(그러나 클라이언트가 따를 필요는 없음) 등. 그렇지 않으면 다음과 같이 진행합니다. 2. 응답에 |Upgrade| 헤더 필드가 없거나 |Upgrade| 헤더 필드에 "websocket" 값과 ASCII 대소문자 구분 없이 일치하지 않는 값이 포함된 경우 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. 3. 응답에 |Connection| 헤더 필드가 없거나 |Connection| 헤더 필드에 "Upgrade" 값과 ASCII 대소문자 구분 없이 일치하는 토큰이 포함되어 있지 않으면 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. 4. 응답에 |Sec-WebSocket-Accept| 헤더 필드가 없거나 |Sec-WebSocket-Accept|에 |Sec-WebSocket-Key|(문자열로, base64 디코딩되지 않음)와 문자열 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"의 연결에 대한 base64로 인코딩된 SHA-1이 아닌 값이 포함되어 있지만 선행 및 후행 공백을 무시하는 경우 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. 5. 응답에 |Sec-WebSocket-Extensions| 헤더 필드가 포함되어 있고 이 헤더 필드가 클라이언트의 핸드셰이크에 없던 확장의 사용을 나타내는 경우(서버가 클라이언트가 요청하지 않은 확장을 나타냄) 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. (요청된 확장을 확인하기 위해 이 헤더 필드를 구문 분석하는 것은 섹션 9.1에서 논의됩니다.) 6. 응답에 |Sec-WebSocket-Protocol| 헤더 필드가 포함되어 있고 이 헤더 필드가 클라이언트의 핸드셰이크에 없던 서브프로토콜의 사용을 나타내는 경우(서버가 클라이언트가 요청하지 않은 서브프로토콜을 나타냄) 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. 서버의 응답이 이 섹션 및 섹션 4.2.2에 정의된 서버의 핸드셰이크에 대한 요구 사항을 준수하지 않는 경우 클라이언트는 `_WebSocket 연결 실패_`를 해야 합니다. [[RFC2616| [RFC2616] ]]에 따라 HTTP 요청 및 HTTP 응답의 모든 헤더 필드 이름은 대소문자를 구분하지 않습니다. 서버의 응답이 위에서 제공한 대로 검증되면 `_WebSocket 연결이 설정됨_`이라고 하며 WebSocket 연결이 OPEN 상태에 있다고 합니다. `_사용 중인 확장_`은 서버의 핸드셰이크에서 제공한 |Sec-WebSocket-Extensions| 헤더 필드의 값과 같은 값을 가진 (비어 있을 수 있는) 문자열로 정의되거나 해당 헤더 필드가 서버의 핸드셰이크에 없는 경우 null 값으로 정의됩니다. `_사용 중인 서브프로토콜_`은 서버의 핸드셰이크에서 |Sec-WebSocket-Protocol| 헤더 필드의 값으로 정의되거나 해당 헤더 필드가 서버의 핸드셰이크에 없는 경우 null 값으로 정의됩니다. 또한 서버의 핸드셰이크의 헤더 필드가 쿠키를 설정해야 함을 나타내는 경우([[RFC6265| [RFC6265] ]]에 정의된 대로) 이러한 쿠키는 `_서버의 개시 핸드셰이크 중에 설정된 쿠키_`라고 합니다. ## 4.2. 서버 측 요구 사항 서버는 연결 관리를 로드 밸런서 및 역방향 프록시와 같은 네트워크의 다른 에이전트로 오프로드할 수 있습니다. 이러한 상황에서 이 사양의 목적상 서버는 TCP 연결을 종료하는 첫 번째 장치에서 요청을 처리하고 응답을 보내는 서버까지 모든 서버 측 인프라 부분을 포함하는 것으로 간주됩니다. 예: 데이터 센터에는 적절한 핸드셰이크로 WebSocket 요청에 응답한 다음 연결을 다른 서버로 전달하여 실제로 데이터 프레임을 처리하는 서버가 있을 수 있습니다. 이 사양의 목적상 "서버"는 두 컴퓨터의 조합입니다. ### 4.2.1. 클라이언트의 개시 핸드셰이크 읽기 클라이언트가 WebSocket 연결을 시작하면 개시 핸드셰이크의 부분을 보냅니다. 서버는 서버 부분의 핸드셰이크를 생성하는 데 필요한 정보를 얻기 위해 이 핸드셰이크의 적어도 일부를 구문 분석해야 합니다. 클라이언트의 개시 핸드셰이크는 다음 부분으로 구성됩니다. 서버가 핸드셰이크를 읽는 동안 클라이언트가 아래 설명과 일치하지 않는 핸드셰이크를 보내지 않은 것으로 판단하는 경우([[RFC2616| [RFC2616] ]]에 따라 헤더 필드의 순서는 중요하지 않음) 핸드셰이크 구성 요소에 대해 지정된 ABNF 문법 위반을 포함하되 이에 국한되지 않고 서버는 클라이언트의 핸드셰이크 처리를 중지하고 적절한 오류 코드(예: 400 Bad Request)가 있는 HTTP 응답을 반환해야 합니다. 1. 섹션 3에 정의된 /resource name/으로 해석되어야 하는 "Request-URI" [[RFC2616| [RFC2616] ]](또는 /resource name/을 포함하는 절대 HTTP/HTTPS URI)를 포함하는 HTTP/1.1 이상 GET 요청. 2. 서버의 권한을 포함하는 |Host| 헤더 필드. 3. ASCII 대소문자 구분 없이 값으로 처리되는 "websocket" 값을 포함하는 |Upgrade| 헤더 필드. 4. ASCII 대소문자 구분 없이 값으로 처리되는 "Upgrade" 토큰을 포함하는 |Connection| 헤더 필드. 5. 디코딩될 때 16바이트 길이인 base64로 인코딩된([[RFC4648| [RFC4648] ]]의 섹션 4 참조) 값을 가진 |Sec-WebSocket-Key| 헤더 필드. 6. 값이 13인 |Sec-WebSocket-Version| 헤더 필드. 7. 선택적으로 |Origin| 헤더 필드. 이 헤더 필드는 모든 브라우저 클라이언트에서 전송됩니다. 이 헤더 필드가 없는 연결 시도는 브라우저 클라이언트에서 온 것으로 해석되어서는 안 됩니다. 8. 선택적으로 클라이언트가 사용하려는 프로토콜을 우선 순위별로 나타내는 값 목록이 있는 |Sec-WebSocket-Protocol| 헤더 필드. 9. 선택적으로 클라이언트가 사용하려는 확장을 나타내는 값 목록이 있는 |Sec-WebSocket-Extensions| 헤더 필드. 이 헤더 필드의 해석은 섹션 9.1에서 논의됩니다. 10. 선택적으로 쿠키를 보내거나 서버에 인증을 요청하는 데 사용되는 것과 같은 다른 헤더 필드. 알 수 없는 헤더 필드는 [[RFC2616| [RFC2616] ]]에 따라 무시됩니다. ### 4.2.2. 서버의 개시 핸드셰이크 전송 클라이언트가 서버에 WebSocket 연결을 설정하면 서버는 연결을 수락하고 서버의 개시 핸드셰이크를 보내기 위해 다음 단계를 완료해야 합니다. 1. 연결이 HTTPS(HTTP-over-TLS) 포트에서 발생하는 경우 연결을 연 후 핸드셰이크 데이터를 보내기 전에 연결에서 TLS 핸드셰이크를 수행합니다. 이것이 실패하면(예: 클라이언트가 확장 클라이언트 hello `"server_name"` 확장에서 서버가 호스팅하지 않는 호스트 이름을 나타냄) 연결을 닫습니다. 그렇지 않으면 연결에 대한 모든 추가 통신(서버의 핸드셰이크 포함)은 암호화된 터널을 통해 실행되어야 합니다 [[RFC5246| [RFC5246] ]]. 2. 서버는 추가 클라이언트 인증을 수행할 수 있습니다. 예를 들어 [[RFC2616| [RFC2616] ]]에 설명된 대로 해당 |WWW-Authenticate| 헤더 필드와 함께 401 상태 코드를 반환합니다. 3. 서버는 3xx 상태 코드 [[RFC2616| [RFC2616] ]]를 사용하여 클라이언트를 리디렉션할 수 있습니다. 이 단계는 위에서 설명한 선택적 인증 단계와 함께, 그 전에 또는 그 후에 발생할 수 있습니다. 4. 다음 정보를 설정합니다: /origin/ 클라이언트의 핸드셰이크에서 |Origin| 헤더 필드는 연결을 설정하는 스크립트의 출처를 나타냅니다. 출처는 ASCII로 직렬화되고 소문자로 변환됩니다. 서버는 이 정보를 들어오는 연결을 수락할지 여부를 결정하는 일부로 사용할 수 있습니다. 서버가 출처를 검증하지 않으면 어디에서나 연결을 수락합니다. 서버가 이 연결을 수락하고 싶지 않으면 적절한 HTTP 오류 코드(예: 403 Forbidden)를 반환하고 이 섹션에 설명된 WebSocket 핸드셰이크를 중단해야 합니다. 자세한 내용은 섹션 10을 참조하십시오. /key/ 클라이언트의 핸드셰이크에서 |Sec-WebSocket-Key| 헤더 필드는 디코딩되면 16바이트 길이인 base64로 인코딩된 값을 포함합니다. 이 (인코딩된) 값은 연결 수락을 나타내기 위해 서버의 핸드셰이크를 만드는 데 사용됩니다. 서버는 |Sec-WebSocket-Key| 값을 base64 디코딩할 필요가 없습니다. /version/ 클라이언트의 핸드셰이크에서 |Sec-WebSocket-Version| 헤더 필드는 클라이언트가 통신하려는 WebSocket 프로토콜 버전을 포함합니다. 이 버전이 서버가 이해하는 버전과 일치하지 않으면 서버는 이 섹션에 설명된 WebSocket 핸드셰이크를 중단하고 대신 적절한 HTTP 오류 코드(예: 426 Upgrade Required)와 서버가 이해할 수 있는 버전을 나타내는 |Sec-WebSocket-Version| 헤더 필드를 보내야 합니다. /resource name/ 서버에서 제공하는 서비스의 식별자. 서버가 여러 서비스를 제공하는 경우 값은 GET 메서드의 "Request-URI" [[RFC2616| [RFC2616] ]]에서 클라이언트의 핸드셰이크에 제공된 리소스 이름에서 파생되어야 합니다. 요청된 서비스를 사용할 수 없는 경우 서버는 적절한 HTTP 오류 코드(예: 404 Not Found)를 보내고 WebSocket 핸드셰이크를 중단해야 합니다. /subprotocol/ 서버가 사용할 준비가 된 서브프로토콜을 나타내는 단일 값 또는 null. 선택한 값은 클라이언트의 핸드셰이크에서 파생되어야 합니다. 특히 서버가 이 연결에 사용할 의향이 있는 |Sec-WebSocket-Protocol| 필드의 값 중 하나를 선택합니다(있는 경우). 클라이언트의 핸드셰이크에 그러한 헤더 필드가 포함되어 있지 않거나 서버가 클라이언트가 요청한 서브프로토콜에 동의하지 않는 경우 허용 가능한 유일한 값은 null입니다. 이러한 필드의 부재는 null 값과 동일합니다(즉, 서버가 제안된 서브프로토콜 중 하나에 동의하지 않으려면 응답에서 |Sec-WebSocket-Protocol| 헤더 필드를 다시 보내서는 안 됩니다). 빈 문자열은 이러한 목적을 위해 null 값과 동일하지 않으며 이 필드에 대한 합법적인 값이 아닙니다. 이 헤더 필드 값의 ABNF는 (token)이며, 여기서 구성 및 규칙의 정의는 [[RFC2616| [RFC2616] ]]에 제공된 대로입니다. /extensions/ 서버가 사용할 준비가 된 프로토콜 수준 확장을 나타내는 (비어 있을 수 있는) 목록. 서버가 여러 확장을 지원하는 경우 값은 클라이언트의 핸드셰이크에서 파생되어야 합니다. 특히 |Sec-WebSocket-Extensions| 필드의 값 중 하나 이상을 선택합니다. 이러한 필드의 부재는 null 값과 동일합니다. 빈 문자열은 이러한 목적을 위해 null 값과 동일하지 않습니다. 클라이언트가 나열하지 않은 확장은 나열되어서는 안 됩니다. 이러한 값을 선택하고 해석하는 방법은 섹션 9.1에서 논의됩니다. 5. 서버가 들어오는 연결을 수락하기로 선택한 경우 다음을 나타내는 유효한 HTTP 응답으로 응답해야 합니다. 1. RFC 2616 [[RFC2616| [RFC2616] ]]에 따른 101 응답 코드가 있는 Status-Line. 이러한 응답은 "HTTP/1.1 101 Switching Protocols"처럼 보일 수 있습니다. 2. RFC 2616 [[RFC2616| [RFC2616] ]]에 따른 값 "websocket"이 있는 |Upgrade| 헤더 필드. 3. 값 "Upgrade"가 있는 |Connection| 헤더 필드. 4. |Sec-WebSocket-Accept| 헤더 필드. 이 헤더 필드의 값은 섹션 4.2.2의 4단계에서 위에 정의된 /key/를 문자열 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"과 연결하고 이 연결된 값의 SHA-1 해시를 가져와 20바이트 값을 얻고 이 20바이트 해시를 base64로 인코딩([[RFC4648| [RFC4648] ]]의 섹션 4 참조)하여 구성됩니다. 이 헤더 필드의 ABNF [[RFC2616| [RFC2616] ]]는 다음과 같이 정의됩니다: Sec-WebSocket-Accept = base64-value-non-empty base64-value-non-empty = (1*base64-data [ base64-padding ]) | base64-padding base64-data = 4base64-character base64-padding = (2base64-character "==") | (3base64-character "=") base64-character = ALPHA | DIGIT | "+" | "/" 참고: 예를 들어 클라이언트의 핸드셰이크에서 |Sec-WebSocket-Key| 헤더 필드의 값이 `"dGhlIHNhbXBsZSBub25jZQ=="`인 경우 서버는 문자열 `"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"`을 추가하여 문자열 `"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11"`을 형성합니다. 그런 다음 서버는 이 문자열의 SHA-1 해시를 가져와 값 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea를 얻습니다. 이 값은 base64로 인코딩되어 값 `"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="`를 제공하며, 이는 |Sec-WebSocket-Accept| 헤더 필드에서 반환됩니다. 5. 선택적으로 섹션 4.2.2의 4단계에 정의된 값 /subprotocol/이 있는 |Sec-WebSocket-Protocol| 헤더 필드. 6. 선택적으로 섹션 4.2.2의 4단계에 정의된 값 /extensions/가 있는 |Sec-WebSocket-Extensions| 헤더 필드. 여러 확장을 사용하려면 단일 |Sec-WebSocket-Extensions| 헤더 필드에 모두 나열하거나 |Sec-WebSocket-Extensions| 헤더 필드의 여러 인스턴스 간에 분할할 수 있습니다. 이것으로 서버의 핸드셰이크가 완료됩니다. 서버가 WebSocket 핸드셰이크를 중단하지 않고 이러한 단계를 완료하면 서버는 WebSocket 연결이 설정된 것으로 간주하고 WebSocket 연결이 OPEN 상태에 있다고 간주합니다. 이 시점에서 서버는 데이터를 보내고 받기 시작할 수 있습니다. ## 4.3. 핸드셰이크에 사용되는 새 헤더 필드에 대한 수집된 ABNF 이 섹션은 "암시적 *LWS 규칙"을 포함하여 [[RFC2616| [RFC2616] ]]의 섹션 2.1의 ABNF 구문/규칙을 사용합니다. 다음 ABNF 규칙은 이 섹션에서 사용됩니다. 일부 규칙의 이름은 해당 헤더 필드의 이름에 해당합니다. 이러한 규칙은 해당 헤더 필드의 값을 표현합니다. 예를 들어 Sec-WebSocket-Key ABNF 규칙은 |Sec-WebSocket-Key| 헤더 필드 값의 구문을 설명합니다. 이름에 "-Client" 접미사가 있는 ABNF 규칙은 클라이언트가 서버로 보낸 요청에서만 사용됩니다. 이름에 "-Server" 접미사가 있는 ABNF 규칙은 서버가 클라이언트로 보낸 응답에서만 사용됩니다. 예를 들어 ABNF 규칙 Sec-WebSocket-Protocol-Client는 클라이언트가 서버로 보낸 |Sec-WebSocket-Protocol| 헤더 필드 값의 구문을 설명합니다. 다음 새 헤더 필드는 클라이언트에서 서버로 핸드셰이크 중에 보낼 수 있습니다: Sec-WebSocket-Key = base64-value-non-empty Sec-WebSocket-Extensions = extension-list Sec-WebSocket-Protocol-Client = 1#token Sec-WebSocket-Version-Client = version base64-value-non-empty = (1*base64-data [ base64-padding ]) | base64-padding base64-data = 4base64-character base64-padding = (2base64-character "==") | (3base64-character "=") base64-character = ALPHA | DIGIT | "+" | "/" extension-list = 1#extension extension = extension-token *( ";" extension-param ) extension-token = registered-token registered-token = token extension-param = token [ "=" (token | quoted-string) ] ; quoted-string 구문 변형을 사용할 때 ; quoted-string 이스케이프 해제 후의 값은 ; 'token' ABNF를 준수해야 합니다. NZDIGIT = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" version = DIGIT | (NZDIGIT DIGIT) | ("1" DIGIT DIGIT) | ("2" DIGIT DIGIT) ; 0-255 범위로 제한, 선행 0 없음 다음 새 헤더 필드는 서버에서 클라이언트로 핸드셰이크 중에 보낼 수 있습니다: Sec-WebSocket-Extensions = extension-list Sec-WebSocket-Accept = base64-value-non-empty Sec-WebSocket-Protocol-Server = token Sec-WebSocket-Version-Server = 1#version ## 4.4. 여러 버전의 WebSocket 프로토콜 지원 이 섹션은 클라이언트 및 서버에서 여러 버전의 WebSocket 프로토콜을 지원하는 데 대한 일부 지침을 제공합니다. WebSocket 버전 광고 기능(|Sec-WebSocket-Version| 헤더 필드)을 사용하여 클라이언트는 처음에 선호하는 WebSocket 프로토콜 버전을 요청할 수 있습니다(반드시 클라이언트가 지원하는 최신 버전일 필요는 없음). 서버가 요청된 버전을 지원하고 핸드셰이크 메시지가 유효한 경우 서버는 해당 버전을 수락합니다. 서버가 요청된 버전을 지원하지 않으면 사용할 의향이 있는 모든 버전을 포함하는 |Sec-WebSocket-Version| 헤더 필드(또는 여러 |Sec-WebSocket-Version| 헤더 필드)로 응답해야 합니다. 이 시점에서 클라이언트가 광고된 버전 중 하나를 지원하는 경우 새 버전 값을 사용하여 WebSocket 핸드셰이크를 반복할 수 있습니다. 다음 예는 위에서 설명한 버전 협상을 보여줍니다: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade ... Sec-WebSocket-Version: 25 서버의 응답은 다음과 같을 수 있습니다: HTTP/1.1 400 Bad Request ... Sec-WebSocket-Version: 13, 8, 7 서버의 마지막 응답은 다음과 같을 수도 있습니다: HTTP/1.1 400 Bad Request ... Sec-WebSocket-Version: 13 Sec-WebSocket-Version: 8, 7 이제 클라이언트는 버전 13에 맞는 핸드셰이크를 반복합니다: GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade ... Sec-WebSocket-Version: 13 # 5. 데이터 프레이밍 ## 5.1. 개요 WebSocket 프로토콜에서 데이터는 프레임 시퀀스를 사용하여 전송됩니다. 네트워크 중개자(예: 가로채는 프록시)를 혼란스럽게 하는 것을 피하고 섹션 10.3에서 자세히 논의되는 보안상의 이유로, 클라이언트는 서버로 보내는 모든 프레임을 마스킹(MUST mask)해야 합니다(자세한 내용은 섹션 5.3 참조). (마스킹은 WebSocket 프로토콜이 TLS를 통해 실행되는지 여부와 관계없이 수행됩니다.) 서버는 마스킹되지 않은 프레임을 수신하면 연결을 닫아야(MUST close) 합니다. 이 경우, 서버는 섹션 7.4.1에 정의된 대로 상태 코드 1002(프로토콜 오류)가 있는 Close 프레임을 보낼 수 있습니다(MAY). 서버는 클라이언트로 보내는 프레임을 마스킹해서는 안 됩니다(MUST NOT). 클라이언트는 마스킹된 프레임을 감지하면 연결을 닫아야(MUST close) 합니다. 이 경우, 섹션 7.4.1에 정의된 대로 상태 코드 1002(프로토콜 오류)를 사용할 수 있습니다(MAY). (이러한 규칙은 향후 사양에서 완화될 수 있습니다.) 기본 프레이밍 프로토콜은 opcode, 페이로드 길이 및 "확장 데이터(Extension data)"와 "애플리케이션 데이터(Application data)"에 대한 지정된 위치가 있는 프레임 유형을 정의하며, 이들이 함께 "페이로드 데이터(Payload data)"를 정의합니다. 특정 비트 및 opcode는 프로토콜의 향후 확장을 위해 예약되어 있습니다. 데이터 프레임은 개시 핸드셰이크 완료 후 해당 엔드포인트가 Close 프레임(섹션 5.5.1)을 보내기 전에 언제든지 클라이언트 또는 서버에 의해 전송될 수 있습니다(MAY). ## 5.2. 기본 프레이밍 프로토콜 데이터 전송 부분에 대한 이 와이어 형식은 이 섹션에서 자세히 제공된 ABNF [[RFC5234| [RFC5234] ]]로 설명됩니다. (이 문서의 다른 섹션과 달리, 이 섹션의 ABNF는 비트 그룹에서 작동한다는 점에 유의하십시오. 각 비트 그룹의 길이는 주석에 표시됩니다. 와이어에서 인코딩될 때 가장 중요한(most significant) 비트가 ABNF에서 가장 왼쪽에 있습니다.) 프레이밍에 대한 높은 수준의 개요는 다음 그림에 나와 있습니다. 아래 그림과 이 섹션의 뒷부분에 지정된 ABNF 사이에 충돌이 있는 경우, 그림이 권위(authoritative)를 갖습니다. ``` 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+ ``` FIN: 1비트 이것이 메시지의 최종 조각(final fragment)임을 나타냅니다. 첫 번째 조각도 최종 조각일 수 있습니다(MAY). RSV1, RSV2, RSV3: 각 1비트 0이 아닌 값에 대한 의미를 정의하는 확장이 협상되지 않는 한 반드시 0이어야(MUST be 0) 합니다. 0이 아닌 값이 수신되고 협상된 확장 중 어느 것도 그러한 0이 아닌 값의 의미를 정의하지 않으면, 수신 엔드포인트는 반드시 `_WebSocket 연결 실패(Fail the WebSocket Connection)_`를 해야 합니다. Opcode: 4비트 "페이로드 데이터(Payload data)"의 해석을 정의합니다. 알 수 없는 opcode가 수신되면, 수신 엔드포인트는 반드시 `_WebSocket 연결 실패_`를 해야 합니다. 다음 값이 정의됩니다: * %x0은 연속 프레임(continuation frame)을 나타냅니다 * %x1은 텍스트 프레임(text frame)을 나타냅니다 * %x2는 바이너리 프레임(binary frame)을 나타냅니다 * %x3-7은 추가 비제어 프레임(non-control frames)을 위해 예약되었습니다 * %x8은 연결 종료(connection close)를 나타냅니다 * %x9는 ping을 나타냅니다 * %xA는 pong을 나타냅니다 * %xB-F는 추가 제어 프레임(control frames)을 위해 예약되었습니다 Mask: 1비트 "페이로드 데이터"가 마스킹되는지 여부를 정의합니다. 1로 설정되면, masking-key에 마스킹 키가 존재하며, 이는 섹션 5.3에 따라 "페이로드 데이터"를 마스킹 해제하는 데 사용됩니다. 클라이언트에서 서버로 보낸 모든 프레임은 이 비트가 1로 설정됩니다. Payload length: 7비트, 7+16비트, 또는 7+64비트 "페이로드 데이터"의 길이(바이트 단위): 0-125이면 그것이 페이로드 길이입니다. 126이면, 다음 2바이트를 16비트 부호 없는 정수로 해석한 것이 페이로드 길이입니다. 127이면, 다음 8바이트를 64비트 부호 없는 정수로 해석한 것(가장 중요한 비트는 반드시 0이어야 함)이 페이로드 길이입니다. 멀티바이트 길이 수량은 네트워크 바이트 순서로 표현됩니다. 모든 경우에, 길이를 인코딩하는 데 최소 바이트 수를 반드시 사용해야(MUST be used) 합니다. 예를 들어, 124바이트 길이의 문자열의 길이는 126, 0, 124 시퀀스로 인코딩할 수 없습니다. 페이로드 길이는 "확장 데이터"의 길이 + "애플리케이션 데이터"의 길이입니다. "확장 데이터"의 길이는 0일 수 있으며, 이 경우 페이로드 길이는 "애플리케이션 데이터"의 길이입니다. Masking-key: 0 또는 4바이트 클라이언트에서 서버로 보낸 모든 프레임은 프레임 내에 포함된 32비트 값으로 마스킹됩니다. 이 필드는 마스크 비트가 1로 설정된 경우 존재하고 마스크 비트가 0으로 설정된 경우 없습니다. 클라이언트-서버 마스킹에 대한 자세한 내용은 섹션 5.3을 참조하십시오. Payload data: (x+y)바이트 "페이로드 데이터"는 "확장 데이터"와 "애플리케이션 데이터"가 연결된 것으로 정의됩니다. Extension data: x바이트 "확장 데이터"는 확장이 협상되지 않는 한 0바이트입니다. 모든 확장은 반드시 "확장 데이터"의 길이 또는 그 길이를 계산하는 방법을 지정해야 하며, 확장 사용이 개시 핸드셰이크 중에 어떻게 협상되어야 하는지를 지정해야 합니다. 존재하는 경우, "확장 데이터"는 전체 페이로드 길이에 포함됩니다. Application data: y바이트 임의의 "애플리케이션 데이터"로, "확장 데이터" 이후 프레임의 나머지 부분을 차지합니다. "애플리케이션 데이터"의 길이는 페이로드 길이에서 "확장 데이터"의 길이를 뺀 것과 같습니다. 기본 프레이밍 프로토콜은 다음 ABNF [[RFC5234| [RFC5234] ]]에 의해 공식적으로 정의됩니다. 이 데이터의 표현이 ASCII 문자가 아니라 이진수라는 점에 유의하는 것이 중요합니다. 따라서, %x0 / %x1 값을 취하는 길이가 1비트인 필드는 값이 0 또는 1인 단일 비트로 표현되며, ASCII 인코딩에서 문자 "0" 또는 "1"을 나타내는 전체 바이트(옥텟)가 아닙니다. %x0-F 사이의 값을 가진 길이가 4비트인 필드 역시 이러한 값을 가진 ASCII 문자나 전체 바이트(옥텟)가 아니라 4비트로 표현됩니다. [[RFC5234| [RFC5234] ]]는 문자 인코딩을 지정하지 않습니다: "규칙은 터미널 값의 문자열로 해결되며, 때때로 문자라고 불립니다. ABNF에서 문자는 단지 음이 아닌 정수입니다. 특정 컨텍스트에서, 값을 문자 집합(예: ASCII)으로 매핑하는 특정 매핑(인코딩)이 지정됩니다." 여기서, 지정된 인코딩은 각 터미널 값이 각 필드마다 다른 지정된 비트 수로 인코딩되는 이진 인코딩입니다. ws-frame = frame-fin ; 1 bit in length frame-rsv1 ; 1 bit in length frame-rsv2 ; 1 bit in length frame-rsv3 ; 1 bit in length frame-opcode ; 4 bits in length frame-masked ; 1 bit in length frame-payload-length ; either 7, 7+16, ; or 7+64 bits in ; length [ frame-masking-key ] ; 32 bits in length frame-payload-data ; n*8 bits in ; length, where ; n >= 0 frame-fin = %x0 ; more frames of this message follow / %x1 ; final frame of this message ; 1 bit in length frame-rsv1 = %x0 / %x1 ; 1 bit in length, MUST be 0 unless ; negotiated otherwise frame-rsv2 = %x0 / %x1 ; 1 bit in length, MUST be 0 unless ; negotiated otherwise frame-rsv3 = %x0 / %x1 ; 1 bit in length, MUST be 0 unless ; negotiated otherwise frame-opcode = frame-opcode-non-control / frame-opcode-control / frame-opcode-cont frame-opcode-cont = %x0 ; frame continuation frame-opcode-non-control= %x1 ; text frame / %x2 ; binary frame / %x3-7 ; 4 bits in length, ; reserved for further non-control frames frame-opcode-control = %x8 ; connection close / %x9 ; ping / %xA ; pong / %xB-F ; reserved for further control ; frames ; 4 bits in length frame-masked = %x0 ; frame is not masked, no frame-masking-key / %x1 ; frame is masked, frame-masking-key present ; 1 bit in length frame-payload-length = ( %x00-7D ) / ( %x7E frame-payload-length-16 ) / ( %x7F frame-payload-length-63 ) ; 7, 7+16, or 7+64 bits in length, ; respectively frame-payload-length-16 = %x0000-FFFF ; 16 bits in length frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF ; 64 bits in length frame-masking-key = 4( %x00-FF ) ; present only if frame-masked is 1 ; 32 bits in length frame-payload-data = (frame-masked-extension-data frame-masked-application-data) ; when frame-masked is 1 / (frame-unmasked-extension-data frame-unmasked-application-data) ; when frame-masked is 0 frame-masked-extension-data = *( %x00-FF ) ; reserved for future extensibility ; n*8 bits in length, where n >= 0 frame-masked-application-data = *( %x00-FF ) ; n*8 bits in length, where n >= 0 frame-unmasked-extension-data = *( %x00-FF ) ; reserved for future extensibility ; n*8 bits in length, where n >= 0 frame-unmasked-application-data = *( %x00-FF ) ; n*8 bits in length, where n >= 0 ## 5.3. 클라이언트-서버 마스킹 마스킹된 프레임은 섹션 5.2에 정의된 대로 frame-masked 필드가 1로 설정되어야(MUST) 합니다. 마스킹 키는 섹션 5.2에서 frame-masking-key로 정의된 대로 프레임 내에 완전히 포함됩니다. 이는 같은 섹션에서 frame-payload-data로 정의된 "페이로드 데이터"를 마스킹하는 데 사용되며, 여기에는 "확장 데이터"와 "애플리케이션 데이터"가 포함됩니다. 마스킹 키는 클라이언트가 무작위로 선택한 32비트 값입니다. 마스킹된 프레임을 준비할 때, 클라이언트는 반드시 허용된 32비트 값 집합에서 새로운 마스킹 키를 선택해야(MUST pick) 합니다. 마스킹 키는 예측할 수 없어야 합니다. 따라서, 마스킹 키는 반드시 강력한 엔트로피 소스에서 파생되어야 하며(MUST be derived), 주어진 프레임에 대한 마스킹 키는 서버/프록시가 후속 프레임에 대한 마스킹 키를 예측하는 것을 간단하게 만들어서는 안 됩니다(MUST NOT). 마스킹 키의 예측 불가능성은 악의적인 애플리케이션의 작성자가 와이어에 나타나는 바이트를 선택하는 것을 방지하는 데 필수적입니다. RFC 4086 [[RFC4086| [RFC4086] ]]은 보안에 민감한 애플리케이션을 위한 적절한 엔트로피 소스가 무엇인지 논의합니다. 마스킹은 "페이로드 데이터"의 길이에 영향을 주지 않습니다. 마스킹된 데이터를 마스킹되지 않은 데이터로 변환하거나 그 반대로 변환하려면, 다음 알고리즘이 적용됩니다. 변환 방향에 관계없이 동일한 알고리즘이 적용됩니다. 즉, 데이터를 마스킹하는 것과 데이터를 마스킹 해제하는 것에 동일한 단계가 적용됩니다. 변환된 데이터의 옥텟 i("transformed-octet-i")는 원본 데이터의 옥텟 i("original-octet-i")와 마스킹 키의 인덱스 i modulo 4의 옥텟("masking-key-octet-j")의 XOR입니다: j = i MOD 4 transformed-octet-i = original-octet-i XOR masking-key-octet-j 프레이밍에서 frame-payload-length로 표시되는 페이로드 길이는 마스킹 키의 길이를 포함하지 않습니다. 이는 "페이로드 데이터"의 길이, 즉 마스킹 키 다음의 바이트 수입니다. ## 5.4. 단편화(Fragmentation) 단편화의 주요 목적은 메시지가 시작될 때 크기를 알 수 없는 메시지를 그 메시지를 버퍼링하지 않고도 보낼 수 있도록 하는 것입니다. 메시지를 단편화할 수 없다면, 엔드포인트는 첫 번째 바이트를 보내기 전에 길이를 세기 위해 전체 메시지를 버퍼링해야 합니다. 단편화를 사용하면, 서버나 중개자는 적절한 크기의 버퍼를 선택할 수 있고, 버퍼가 가득 차면 네트워크에 조각을 쓸 수 있습니다. 단편화의 부차적인 사용 사례는 멀티플렉싱인데, 하나의 논리 채널에서 큰 메시지가 출력 채널을 독점하는 것이 바람직하지 않아서, 멀티플렉싱이 메시지를 더 작은 조각으로 자유롭게 분할하여 출력 채널을 더 잘 공유할 필요가 있는 경우입니다. (멀티플렉싱 확장은 이 문서에서 설명하지 않습니다.) 확장에 의해 달리 지정되지 않는 한, 프레임은 의미론적 의미가 없습니다. 클라이언트와 서버가 확장을 협상하지 않았거나 일부 확장이 협상되었지만 중개자가 협상된 모든 확장을 이해하고 이러한 확장이 있는 상태에서 프레임을 병합 및/또는 분할하는 방법을 아는 경우, 중개자는 프레임을 병합 및/또는 분할할 수 있습니다. 이것의 한 가지 의미는 확장이 없는 경우, 발신자와 수신자가 특정 프레임 경계의 존재에 의존해서는 안 된다는 것입니다. 단편화에는 다음 규칙이 적용됩니다: o 단편화되지 않은 메시지는 FIN 비트가 설정되고(섹션 5.2) opcode가 0이 아닌 단일 프레임으로 구성됩니다. o 단편화된 메시지는 FIN 비트가 지워지고 opcode가 0이 아닌 단일 프레임으로 시작하여, FIN 비트가 지워지고 opcode가 0으로 설정된 0개 이상의 프레임이 뒤따르고, FIN 비트가 설정되고 opcode가 0인 단일 프레임으로 종료됩니다. 단편화된 메시지는 개념적으로 페이로드가 순서대로 조각의 페이로드를 연결한 것과 같은 단일 더 큰 메시지와 동등합니다. 그러나, 확장이 있는 경우, 확장이 존재하는 "확장 데이터"의 해석을 정의하므로 이것이 사실이 아닐 수 있습니다. 예를 들어, "확장 데이터"는 첫 번째 조각의 시작 부분에만 존재하고 후속 조각에 적용될 수 있거나, 각 조각에 존재하는 "확장 데이터"가 해당 특정 조각에만 적용될 수 있습니다. "확장 데이터"가 없는 경우, 다음 예는 단편화가 어떻게 작동하는지 보여줍니다. 예: 세 개의 조각으로 전송된 텍스트 메시지의 경우, 첫 번째 조각은 opcode가 0x1이고 FIN 비트가 지워지고, 두 번째 조각은 opcode가 0x0이고 FIN 비트가 지워지고, 세 번째 조각은 opcode가 0x0이고 FIN 비트가 설정됩니다. o 제어 프레임(섹션 5.5)은 단편화된 메시지 중간에 삽입될 수 있습니다(MAY). 제어 프레임 자체는 반드시 단편화되어서는 안 됩니다(MUST NOT). o 메시지 조각은 반드시 발신자가 보낸 순서대로 수신자에게 전달되어야(MUST) 합니다. o 한 메시지의 조각은, 인터리빙을 해석할 수 있는 확장이 협상되지 않는 한, 다른 메시지의 조각 사이에 인터리빙되어서는 안 됩니다(MUST NOT). o 엔드포인트는 반드시 단편화된 메시지 중간에 제어 프레임을 처리할 수 있어야(MUST be capable) 합니다. o 발신자는 비제어 메시지에 대해 임의 크기의 조각을 생성할 수 있습니다(MAY). o 클라이언트와 서버는 반드시 단편화된 메시지와 단편화되지 않은 메시지를 모두 수신할 수 있어야(MUST support) 합니다. o 제어 프레임은 단편화될 수 없으므로, 중개자는 제어 프레임의 단편화를 변경하려고 시도해서는 안 됩니다(MUST NOT). o 예약된 비트 값이 사용되고 중개자가 이러한 값의 의미를 알지 못하는 경우, 중개자는 메시지의 단편화를 변경해서는 안 됩니다(MUST NOT). o 확장이 협상되었고 중개자가 협상된 확장의 의미를 인식하지 못하는 연결의 컨텍스트에서, 중개자는 어떤 메시지의 단편화도 변경해서는 안 됩니다(MUST NOT). 마찬가지로, WebSocket 핸드셰이크를 보지 못했고(그 내용에 대해 통지받지 못했고) WebSocket 연결로 귀결된 중개자는 그러한 연결의 어떤 메시지의 단편화도 변경해서는 안 됩니다(MUST NOT). o 이러한 규칙의 결과로, 메시지의 모든 조각은 첫 번째 조각의 opcode에 의해 설정된 동일한 유형입니다. 제어 프레임은 단편화될 수 없으므로, 메시지의 모든 조각에 대한 유형은 반드시 텍스트, 바이너리 또는 예약된 opcode 중 하나여야 합니다(MUST be). 참고: 제어 프레임을 삽입할 수 없다면, 예를 들어 큰 메시지 뒤에 있는 경우 ping의 지연 시간이 매우 길어질 것입니다. 따라서, 단편화된 메시지 중간에 제어 프레임을 처리하는 요구 사항이 있습니다. 구현 참고: 확장이 없는 경우, 수신자는 프레임을 처리하기 위해 전체 프레임을 버퍼링할 필요가 없습니다. 예를 들어, 스트리밍 API가 사용되는 경우, 프레임의 일부를 애플리케이션에 전달할 수 있습니다. 그러나, 이 가정은 미래의 모든 WebSocket 확장에 대해 사실이 아닐 수 있다는 점에 유의하십시오. ## 5.5. 제어 프레임 제어 프레임은 opcode의 가장 중요한 비트가 1인 opcode로 식별됩니다. 현재 정의된 제어 프레임용 opcode에는 0x8(Close), 0x9(Ping), 0xA(Pong)가 포함됩니다. Opcode 0xB-0xF는 아직 정의되지 않은 추가 제어 프레임을 위해 예약되어 있습니다. 제어 프레임은 WebSocket에 대한 상태를 전달하는 데 사용됩니다. 제어 프레임은 단편화된 메시지 중간에 삽입될 수 있습니다. 모든 제어 프레임은 반드시 페이로드 길이가 125바이트 이하여야 하며(MUST have) 단편화되어서는 안 됩니다(MUST NOT be fragmented). ### 5.5.1. Close Close 프레임에는 opcode 0x8이 포함됩니다. Close 프레임은 종료 이유를 나타내는 본문(프레임의 "애플리케이션 데이터" 부분)을 포함할 수 있습니다(MAY). 예를 들어 엔드포인트가 종료되거나, 엔드포인트가 너무 큰 프레임을 수신했거나, 엔드포인트가 기대한 형식과 일치하지 않는 프레임을 수신했기 때문입니다. 본문이 있는 경우, 본문의 첫 2바이트는 반드시 섹션 7.4에 정의된 /code/ 값을 가진 상태 코드를 나타내는 2바이트 부호 없는 정수(네트워크 바이트 순서)여야 합니다(MUST be). 2바이트 정수 다음에, 본문은 값 /reason/을 가진 UTF-8로 인코딩된 데이터를 포함할 수 있으며(MAY), 그 해석은 이 사양에 의해 정의되지 않습니다. 이 데이터는 반드시 사람이 읽을 수 있는 것은 아니지만 디버깅이나 연결을 열었던 스크립트와 관련된 정보를 전달하는 데 유용할 수 있습니다. 데이터가 사람이 읽을 수 있다고 보장되지 않으므로, 클라이언트는 반드시 이를 최종 사용자에게 보여주어서는 안 됩니다(MUST NOT). 클라이언트에서 서버로 보낸 Close 프레임은 섹션 5.3에 따라 마스킹되어야 합니다. 애플리케이션은 Close 프레임을 보낸 후 더 이상 데이터 프레임을 보내서는 안 됩니다(MUST NOT). 엔드포인트가 Close 프레임을 수신하고 이전에 Close 프레임을 보내지 않은 경우, 엔드포인트는 반드시 응답으로 Close 프레임을 보내야 합니다(MUST send). (응답으로 Close 프레임을 보낼 때, 엔드포인트는 일반적으로 수신한 상태 코드를 에코합니다.) 엔드포인트는 가능한 한 빨리 그렇게 해야 합니다(SHOULD do so). 엔드포인트는 현재 메시지가 전송될 때까지 Close 프레임 전송을 지연할 수 있습니다(MAY). (예를 들어, 단편화된 메시지의 대부분이 이미 전송된 경우, 엔드포인트는 Close 프레임을 보내기 전에 나머지 조각을 보낼 수 있습니다(MAY).) 그러나, 이미 Close 프레임을 보낸 엔드포인트가 데이터를 계속 처리할 것이라는 보장은 없습니다. Close 메시지를 보내고 받은 후, 엔드포인트는 WebSocket 연결이 닫힌 것으로 간주하고 반드시 기본 TCP 연결을 닫아야 합니다(MUST close). 서버는 반드시 기본 TCP 연결을 즉시 닫아야 합니다(MUST close). 클라이언트는 서버가 연결을 닫을 때까지 기다려야 하지만(SHOULD wait), Close 메시지를 보내고 받은 후 언제든지 연결을 닫을 수 있습니다(MAY close). 예를 들어, 적절한 시간 내에 서버로부터 TCP Close를 받지 못한 경우입니다. 클라이언트와 서버가 동시에 Close 메시지를 보내는 경우, 두 엔드포인트 모두 Close 메시지를 보내고 받았으므로 WebSocket 연결이 닫힌 것으로 간주하고 기본 TCP 연결을 닫아야 합니다. ### 5.5.2. Ping Ping 프레임에는 opcode 0x9가 포함됩니다. Ping 프레임은 "애플리케이션 데이터"를 포함할 수 있습니다(MAY). Ping 프레임을 수신하면, 엔드포인트는 이미 Close 프레임을 수신하지 않은 한 응답으로 Pong 프레임을 보내야 합니다(MUST send). 엔드포인트는 가능한 한 빨리 Pong 프레임으로 응답해야 합니다(SHOULD respond). Pong 프레임은 섹션 5.5.3에서 논의됩니다. 엔드포인트는 연결이 설정된 후 그리고 연결이 닫히기 전 언제든지 Ping 프레임을 보낼 수 있습니다(MAY send). 참고: Ping 프레임은 킵얼라이브 역할을 하거나 원격 엔드포인트가 여전히 응답하는지 확인하는 수단으로 사용될 수 있습니다. ### 5.5.3. Pong Pong 프레임에는 opcode 0xA가 포함됩니다. 섹션 5.5.2는 Ping과 Pong 프레임 모두에 적용되는 요구 사항을 자세히 설명합니다. Ping 프레임에 대한 응답으로 보낸 Pong 프레임은 응답하는 Ping 프레임의 메시지 본문에서 발견된 것과 동일한 "애플리케이션 데이터"를 가져야 합니다. 엔드포인트가 Ping 프레임을 수신하고 이전 Ping 프레임(들)에 대한 응답으로 Pong 프레임(들)을 아직 보내지 않은 경우, 엔드포인트는 가장 최근에 처리된 Ping 프레임에 대해서만 Pong 프레임을 보내도록 선택할 수 있습니다(MAY elect). Pong 프레임은 요청되지 않은 상태로 보낼 수 있습니다(MAY be sent). 이것은 단방향 하트비트로 사용됩니다. 요청되지 않은 Pong 프레임에 대한 응답은 예상되지 않습니다. ## 5.6. 데이터 프레임 데이터 프레임(즉, 비제어 프레임)은 opcode의 가장 중요한 비트가 0인 opcode로 식별됩니다. 현재 정의된 데이터 프레임용 opcode에는 0x1(텍스트), 0x2(바이너리)가 포함됩니다. Opcode 0x3-0x7은 아직 정의되지 않은 추가 비제어 프레임을 위해 예약되어 있습니다. 데이터 프레임은 애플리케이션 계층 및/또는 확장 계층 데이터를 전달합니다. opcode는 데이터의 해석을 결정합니다: Text "페이로드 데이터"는 UTF-8로 인코딩된 텍스트 데이터입니다. 특정 텍스트 프레임은 부분 UTF-8 시퀀스를 포함할 수 있다는 점에 유의하십시오. 그러나, 전체 메시지는 반드시 유효한 UTF-8을 포함해야 합니다(MUST contain). 재조립된 메시지의 잘못된 UTF-8은 섹션 8.1에 설명된 대로 처리됩니다. Binary "페이로드 데이터"는 해석이 전적으로 애플리케이션 계층에 달려 있는 임의의 바이너리 데이터입니다. ## 5.7. 예제 o 단일 프레임 마스킹되지 않은 텍스트 메시지 * 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f ("Hello"를 포함) o 단일 프레임 마스킹된 텍스트 메시지 * 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 ("Hello"를 포함) o 단편화된 마스킹되지 않은 텍스트 메시지 * 0x01 0x03 0x48 0x65 0x6c ("Hel"을 포함) * 0x80 0x02 0x6c 0x6f ("lo"를 포함) o 마스킹되지 않은 Ping 요청과 마스킹된 Ping 응답 * 0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f ("Hello"의 본문을 포함하지만, 본문의 내용은 임의적임) * 0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (ping의 본문과 일치하는 "Hello"의 본문을 포함) o 단일 마스킹되지 않은 프레임에 256바이트 바이너리 메시지 * 0x82 0x7E 0x0100 [256바이트의 바이너리 데이터] o 단일 마스킹되지 않은 프레임에 64KiB 바이너리 메시지 * 0x82 0x7F 0x0000000000010000 [65536바이트의 바이너리 데이터] ## 5.8. 확장성 프로토콜은 기본 프로토콜에 기능을 추가할 확장을 허용하도록 설계되었습니다. 연결의 엔드포인트는 반드시 개시 핸드셰이크 동안 확장 사용을 협상해야 합니다(MUST negotiate). 이 사양은 확장에서 사용하기 위해 opcode 0x3부터 0x7까지와 0xB부터 0xF까지, "확장 데이터" 필드, 그리고 프레임 헤더의 frame-rsv1, frame-rsv2, frame-rsv3 비트를 제공합니다. 확장의 협상은 섹션 9.1에서 더 자세히 논의됩니다. 다음은 확장의 예상 사용의 일부입니다. 이 목록은 완전하지도 규정적이지도 않습니다. o "확장 데이터"는 "애플리케이션 데이터" 전에 "페이로드 데이터"에 배치될 수 있습니다. o 예약된 비트는 프레임별 필요를 위해 할당될 수 있습니다. o 예약된 opcode 값이 정의될 수 있습니다. o 더 많은 opcode 값이 필요한 경우 예약된 비트를 opcode 필드에 할당할 수 있습니다. o 예약된 비트 또는 "확장" opcode를 정의하여 더 큰 opcode 또는 더 많은 프레임별 비트를 정의하기 위해 "페이로드 데이터"에서 추가 비트를 할당할 수 있습니다. # 6. 데이터 송수신 ## 6.1. 데이터 전송 WebSocket 연결을 통해 /data/로 구성된 `_WebSocket 메시지 전송(Send a WebSocket Message)_`을 하려면, 엔드포인트는 반드시 다음 단계를 수행해야 합니다(MUST perform). 1. 엔드포인트는 반드시 WebSocket 연결이 OPEN 상태인지 확인해야 합니다(MUST ensure) (섹션 4.1과 4.2.2 참조). 어느 시점에서든 WebSocket 연결의 상태가 변경되면, 엔드포인트는 반드시 다음 단계를 중단해야 합니다(MUST abort). 2. 엔드포인트는 반드시 섹션 5.2에 정의된 대로 /data/를 WebSocket 프레임으로 캡슐화해야 합니다(MUST encapsulate). 보낼 데이터가 크거나 엔드포인트가 데이터 전송을 시작하려는 시점에 데이터의 전체가 사용 가능하지 않은 경우, 엔드포인트는 섹션 5.4에 정의된 대로 데이터를 일련의 프레임으로 캡슐화할 수도 있습니다(MAY alternately encapsulate). 3. 데이터를 포함하는 첫 번째 프레임의 opcode(frame-opcode)는 반드시 수신자가 텍스트 또는 바이너리 데이터로 해석할 데이터에 대해 섹션 5.2의 적절한 값으로 설정되어야 합니다(MUST be set). 4. 데이터를 포함하는 마지막 프레임의 FIN 비트(frame-fin)는 반드시 섹션 5.2에 정의된 대로 1로 설정되어야 합니다(MUST be set). 5. 클라이언트가 데이터를 보내는 경우, 프레임(들)은 반드시 섹션 5.3에 정의된 대로 마스킹되어야 합니다(MUST be masked). 6. (섹션 9) 확장이 WebSocket 연결에 대해 협상된 경우, 해당 확장의 정의에 따라 추가 고려 사항이 적용될 수 있습니다. 7. 형성된 프레임(들)은 반드시 기본 네트워크 연결을 통해 전송되어야 합니다(MUST be transmitted). ## 6.2. 데이터 수신 WebSocket 데이터를 수신하려면, 엔드포인트는 기본 네트워크 연결을 수신 대기합니다. 들어오는 데이터는 반드시 섹션 5.2에 정의된 대로 WebSocket 프레임으로 구문 분석되어야 합니다(MUST be parsed). 제어 프레임(섹션 5.5)이 수신되면, 프레임은 반드시 섹션 5.5에 정의된 대로 처리되어야 합니다(MUST be handled). 데이터 프레임(섹션 5.6)을 수신하면, 엔드포인트는 반드시 섹션 5.2의 opcode(frame-opcode)에 의해 정의된 데이터의 /type/을 기록해야 합니다(MUST note). 이 프레임의 "애플리케이션 데이터"는 메시지의 /data/로 정의됩니다. 프레임이 단편화되지 않은 메시지(섹션 5.4)를 구성하는 경우, 유형 /type/과 데이터 /data/를 가진 `_WebSocket 메시지가 수신됨(A WebSocket Message Has Been Received)_`이라고 합니다. 프레임이 단편화된 메시지의 일부인 경우, 후속 데이터 프레임의 "애플리케이션 데이터"가 /data/를 형성하기 위해 연결됩니다. FIN 비트(frame-fin)에 의해 표시되는 마지막 조각이 수신되면, 데이터 /data/(조각의 "애플리케이션 데이터"의 연결로 구성됨)와 유형 /type/(단편화된 메시지의 첫 번째 프레임에서 기록됨)을 가진 `_WebSocket 메시지가 수신됨_`이라고 합니다. 후속 데이터 프레임은 반드시 새 WebSocket 메시지에 속하는 것으로 해석되어야 합니다(MUST be interpreted). 확장(섹션 9)은 데이터가 읽히는 방법의 의미론을 변경할 수 있으며(MAY change), 특히 메시지 경계를 구성하는 것을 포함합니다. 확장은 페이로드에서 "애플리케이션 데이터" 전에 "확장 데이터"를 추가하는 것 외에도, "애플리케이션 데이터"를 수정할 수 있습니다(MAY also modify) (예: 압축하여). 서버는 반드시 섹션 5.3에 설명된 대로 클라이언트로부터 받은 데이터 프레임에 대한 마스킹을 제거해야 합니다(MUST remove). # 7. 연결 종료 ## 7.1. 정의 ### 7.1.1. WebSocket 연결 종료 `_WebSocket 연결 종료(Close the WebSocket Connection)_`를 하려면, 엔드포인트는 기본 TCP 연결을 닫습니다. 엔드포인트는 TCP 연결과 해당되는 경우 TLS 세션을 깔끔하게 닫는 방법을 사용해야 하며(SHOULD), 수신되었을 수 있는 후행 바이트를 폐기합니다. 엔드포인트는 필요할 때(예: 공격을 받고 있을 때) 사용 가능한 모든 수단을 통해 연결을 닫을 수 있습니다(MAY). 기본 TCP 연결은, 대부분의 정상적인 경우에, 서버가 먼저 닫아야 하므로(SHOULD) TIME_WAIT 상태를 유지하는 것은 클라이언트가 아니라 서버입니다(이것은 클라이언트가 2 최대 세그먼트 수명(2MSL) 동안 연결을 다시 여는 것을 방지하지만, 더 높은 seq 번호를 가진 새로운 SYN에서 TIME_WAIT 연결이 즉시 다시 열리므로 이에 상응하는 서버 영향은 없습니다). 비정상적인 경우(예: 합리적인 시간이 지난 후 서버로부터 TCP Close를 받지 못한 경우), 클라이언트가 TCP Close를 시작할 수 있습니다(MAY). 따라서, 서버가 `_WebSocket 연결 종료_`를 하도록 지시받으면 즉시 TCP Close를 시작해야 하고(SHOULD), 클라이언트가 동일한 작업을 하도록 지시받으면 서버로부터 TCP Close를 기다려야 합니다(SHOULD). 예를 들어, Berkeley 소켓을 사용하는 C에서 깔끔한 종료를 얻는 방법의 예로, 소켓에서 SHUT_WR과 함께 shutdown()을 호출하고, 피어도 순서있는 종료를 수행했음을 나타내는 반환 값 0을 얻을 때까지 recv()를 호출하고, 마지막으로 소켓에서 close()를 호출합니다. ### 7.1.2. WebSocket 종료 핸드셰이크 시작 상태 코드(섹션 7.4) /code/와 선택적 종료 이유(섹션 7.1.6) /reason/으로 `_WebSocket 종료 핸드셰이크 시작(Start the WebSocket Closing Handshake)_`을 하려면, 엔드포인트는 반드시 섹션 5.5.1에 설명된 대로 상태 코드가 /code/로 설정되고 종료 이유가 /reason/으로 설정된 Close 제어 프레임을 보내야 합니다(MUST send). 엔드포인트가 Close 제어 프레임을 보내고 받으면, 해당 엔드포인트는 섹션 7.1.1에 정의된 대로 `_WebSocket 연결 종료_`를 해야 합니다(SHOULD). ### 7.1.3. WebSocket 종료 핸드셰이크가 시작됨 Close 제어 프레임을 보내거나 받으면, `_WebSocket 종료 핸드셰이크가 시작됨(The WebSocket Closing Handshake is Started)_`이라고 하며 WebSocket 연결이 CLOSING 상태에 있다고 합니다. ### 7.1.4. WebSocket 연결이 종료됨 기본 TCP 연결이 종료되면, `_WebSocket 연결이 종료됨(The WebSocket Connection is Closed)_`이라고 하며 WebSocket 연결이 CLOSED 상태에 있다고 합니다. WebSocket 종료 핸드셰이크가 완료된 후 TCP 연결이 종료된 경우, WebSocket 연결이 `_깔끔하게(cleanly)_` 종료되었다고 합니다. WebSocket 연결을 설정할 수 없었던 경우, `_WebSocket 연결이 종료됨_`이라고도 하지만 `_깔끔하게_` 종료되지는 않았다고 합니다. ### 7.1.5. WebSocket 연결 종료 코드 섹션 5.5.1과 7.4에 정의된 대로, Close 제어 프레임은 종료 이유를 나타내는 상태 코드를 포함할 수 있습니다. WebSocket 연결의 종료는 어느 엔드포인트에 의해서든 시작될 수 있으며, 잠재적으로 동시에 시작될 수 있습니다. `_WebSocket 연결 종료 코드(The WebSocket Connection Close Code)_`는 이 프로토콜을 구현하는 애플리케이션이 수신한 첫 번째 Close 제어 프레임에 포함된 상태 코드(섹션 7.4)로 정의됩니다. 이 Close 제어 프레임에 상태 코드가 없으면, `_WebSocket 연결 종료 코드_`는 1005로 간주됩니다. `_WebSocket 연결이 종료됨_`이고 엔드포인트가 Close 제어 프레임을 수신하지 않은 경우(예: 기본 전송 연결이 손실된 경우 발생할 수 있음), `_WebSocket 연결 종료 코드_`는 1006으로 간주됩니다. 참고: 두 엔드포인트가 `_WebSocket 연결 종료 코드_`의 값에 동의하지 않을 수 있습니다. 예를 들어, 원격 엔드포인트가 Close 프레임을 보냈지만 로컬 애플리케이션이 소켓의 수신 버퍼에서 Close 프레임을 포함하는 데이터를 아직 읽지 않았고, 로컬 애플리케이션이 독립적으로 연결을 닫고 Close 프레임을 보내기로 결정한 경우, 두 엔드포인트 모두 Close 프레임을 보내고 받았으며 더 이상 Close 프레임을 보내지 않습니다. 각 엔드포인트는 다른 쪽이 보낸 상태 코드를 `_WebSocket 연결 종료 코드_`로 볼 것입니다. 따라서, 두 엔드포인트가 모두 독립적으로 그리고 거의 동시에 `_WebSocket 종료 핸드셰이크 시작_`을 하는 경우 두 엔드포인트가 `_WebSocket 연결 종료 코드_`의 값에 동의하지 않을 수 있습니다. ### 7.1.6. WebSocket 연결 종료 이유 섹션 5.5.1과 7.4에 정의된 대로, Close 제어 프레임은 종료 이유를 나타내는 상태 코드를 포함할 수 있으며, 그 뒤에 UTF-8로 인코딩된 데이터가 이어질 수 있고, 해당 데이터의 해석은 엔드포인트에 맡겨지며 이 프로토콜에 의해 정의되지 않습니다. WebSocket 연결의 종료는 어느 엔드포인트에 의해서든 시작될 수 있으며, 잠재적으로 동시에 시작될 수 있습니다. `_WebSocket 연결 종료 이유(The WebSocket Connection Close Reason)_`는 이 프로토콜을 구현하는 애플리케이션이 수신한 첫 번째 Close 제어 프레임에 포함된 상태 코드(섹션 7.4) 다음에 오는 UTF-8로 인코딩된 데이터로 정의됩니다. Close 제어 프레임에 그러한 데이터가 없으면, `_WebSocket 연결 종료 이유_`는 빈 문자열입니다. 참고: 섹션 7.1.5에 언급된 것과 동일한 논리에 따라, 두 엔드포인트가 `_WebSocket 연결 종료 이유_`에 동의하지 않을 수 있습니다. ### 7.1.7. WebSocket 연결 실패 특정 알고리즘과 사양은 엔드포인트가 `_WebSocket 연결 실패(Fail the WebSocket Connection)_`를 하도록 요구합니다. 그렇게 하려면, 클라이언트는 반드시 `_WebSocket 연결 종료_`를 해야 하며(MUST), 적절한 방식으로 사용자에게(개발자에게 특히 유용할 것임) 문제를 보고할 수 있습니다(MAY). 마찬가지로, 그렇게 하려면 서버는 반드시 `_WebSocket 연결 종료_`를 해야 하며(MUST), 문제를 로그로 남겨야 합니다(SHOULD). 엔드포인트가 `_WebSocket 연결 실패_`를 하도록 요구되기 전에 `_WebSocket 연결이 설정됨(The WebSocket Connection is Established)_`이었다면, 엔드포인트는 `_WebSocket 연결 종료_`를 진행하기 전에 적절한 상태 코드(섹션 7.4)를 가진 Close 프레임을 보내야 합니다(SHOULD). 엔드포인트는, WebSocket 연결이 처음부터 실패하게 만든 오류의 특성으로 인해 상대방이 Close 프레임을 수신하고 처리할 수 없을 것으로 판단되는 경우, Close 프레임 전송을 생략할 수 있습니다(MAY). 엔드포인트는 `_WebSocket 연결 실패_`를 하도록 지시받은 후 원격 엔드포인트로부터 (응답하는 Close 프레임을 포함하여) 데이터를 처리하려고 계속 시도해서는 안 됩니다(MUST NOT). 위에 표시된 경우나 애플리케이션 계층에 의해 지정된 경우(예: WebSocket API를 사용하는 스크립트)를 제외하고, 클라이언트는 연결을 닫아서는 안 됩니다(SHOULD NOT). ## 7.2. 비정상 종료 ### 7.2.1. 클라이언트 시작 종료 특정 알고리즘은, 특히 개시 핸드셰이크 중에, 클라이언트가 `_WebSocket 연결 실패_`를 하도록 요구합니다. 그렇게 하려면, 클라이언트는 반드시 섹션 7.1.7에 정의된 대로 `_WebSocket 연결 실패_`를 해야 합니다(MUST). 어느 시점에서든 기본 전송 계층 연결이 예기치 않게 손실되면, 클라이언트는 반드시 `_WebSocket 연결 실패_`를 해야 합니다(MUST). 위에 표시된 경우나 애플리케이션 계층에 의해 지정된 경우(예: WebSocket API를 사용하는 스크립트)를 제외하고, 클라이언트는 연결을 닫아서는 안 됩니다(SHOULD NOT). ### 7.2.2. 서버 시작 종료 특정 알고리즘은 서버가 개시 핸드셰이크 중에 `_WebSocket 연결 중단(Abort the WebSocket Connection)_`을 하도록 요구하거나 권장합니다. 그렇게 하려면, 서버는 반드시 단순히 `_WebSocket 연결 종료_`(섹션 7.1.1)를 해야 합니다(MUST). ### 7.2.3. 비정상 종료로부터 복구 비정상 종료는 여러 가지 이유로 발생할 수 있습니다. 이러한 종료는 일시적인 오류의 결과일 수 있으며, 이 경우 재연결하면 좋은 연결로 이어지고 정상 작동이 재개될 수 있습니다. 이러한 종료는 또한 비일시적 문제의 결과일 수 있으며, 이 경우 배포된 각 클라이언트가 비정상 종료를 경험하고 즉시 그리고 지속적으로 재연결을 시도하면, 서버는 재연결을 시도하는 많은 수의 클라이언트에 의한 서비스 거부 공격에 해당하는 것을 경험할 수 있습니다. 이러한 시나리오의 최종 결과는 서비스가 적시에 복구할 수 없거나 복구가 훨씬 더 어려워질 수 있습니다. 이를 방지하기 위해, 클라이언트는 이 섹션에 설명된 대로 비정상 종료 후 재연결을 시도할 때 일종의 백오프를 사용해야 합니다(SHOULD). 첫 번째 재연결 시도는 무작위 시간만큼 지연되어야 합니다(SHOULD). 이 무작위 지연을 선택하는 매개변수는 클라이언트가 결정하도록 남겨져 있습니다. 0과 5초 사이에서 무작위로 선택된 값이 합리적인 초기 지연이지만, 클라이언트는 구현 경험과 특정 애플리케이션에 따라 지연 길이를 선택할 다른 간격을 선택할 수 있습니다(MAY). 첫 번째 재연결 시도가 실패하면, 후속 재연결 시도는 절단된 이진 지수 백오프와 같은 방법을 사용하여 점점 더 긴 시간만큼 지연되어야 합니다(SHOULD). ## 7.3. 정상 연결 종료 서버는 원하는 때 언제든지 WebSocket 연결을 닫을 수 있습니다(MAY). 클라이언트는 WebSocket 연결을 임의로 닫아서는 안 됩니다(SHOULD NOT). 어느 경우든, 엔드포인트는 `_WebSocket 종료 핸드셰이크 시작_`(섹션 7.1.2) 절차를 따라 종료를 시작합니다. ## 7.4. 상태 코드 설정된 연결을 종료할 때(예: 개시 핸드셰이크가 완료된 후 Close 프레임을 보낼 때), 엔드포인트는 종료 이유를 나타낼 수 있습니다(MAY). 엔드포인트에 의한 이 이유의 해석과 이 이유가 주어졌을 때 엔드포인트가 취해야 할 조치는 이 사양에 의해 정의되지 않은 채로 남겨져 있습니다. 이 사양은 미리 정의된 상태 코드 집합을 정의하고 확장, 프레임워크 및 최종 애플리케이션에서 사용할 수 있는 범위를 지정합니다. 상태 코드와 관련된 텍스트 메시지는 Close 프레임의 선택적 구성 요소입니다. ### 7.4.1. 정의된 상태 코드 엔드포인트는 Close 프레임을 보낼 때 다음 미리 정의된 상태 코드를 사용할 수 있습니다(MAY). 1000 1000은 정상 종료를 나타내며, 연결이 설정된 목적이 달성되었음을 의미합니다. 1001 1001은 엔드포인트가 "떠나고 있음"을 나타냅니다. 예를 들어 서버가 다운되거나 브라우저가 페이지에서 벗어난 경우입니다. 1002 1002는 엔드포인트가 프로토콜 오류로 인해 연결을 종료하고 있음을 나타냅니다. 1003 1003은 엔드포인트가 수용할 수 없는 유형의 데이터를 수신했기 때문에 연결을 종료하고 있음을 나타냅니다(예: 텍스트 데이터만 이해하는 엔드포인트는 바이너리 메시지를 수신하면 이것을 보낼 수 있습니다(MAY)). 1004 예약됨. 특정 의미는 미래에 정의될 수 있습니다. 1005 1005는 예약된 값이며 엔드포인트에 의해 Close 제어 프레임의 상태 코드로 설정되어서는 안 됩니다(MUST NOT). 실제로 상태 코드가 없었음을 나타내기 위해 상태 코드를 기대하는 애플리케이션에서 사용하도록 지정되었습니다. 1006 1006은 예약된 값이며 엔드포인트에 의해 Close 제어 프레임의 상태 코드로 설정되어서는 안 됩니다(MUST NOT). 연결이 비정상적으로 닫혔음을 나타내기 위해 상태 코드를 기대하는 애플리케이션에서 사용하도록 지정되었습니다. 예를 들어, Close 제어 프레임을 보내거나 받지 않고 닫힌 경우입니다. 1007 1007은 엔드포인트가 메시지 유형과 일치하지 않는 메시지 내의 데이터를 수신했기 때문에 연결을 종료하고 있음을 나타냅니다(예: 텍스트 메시지 내의 비UTF-8 [[RFC3629| [RFC3629] ]] 데이터). 1008 1008은 엔드포인트가 정책을 위반하는 메시지를 수신했기 때문에 연결을 종료하고 있음을 나타냅니다. 이것은 다른 더 적절한 상태 코드(예: 1003 또는 1009)가 없거나 정책에 대한 특정 세부 정보를 숨길 필요가 있을 때 반환될 수 있는 일반 상태 코드입니다. 1009 1009는 엔드포인트가 처리하기에 너무 큰 메시지를 수신했기 때문에 연결을 종료하고 있음을 나타냅니다. 1010 1010은 엔드포인트(클라이언트)가 서버가 하나 이상의 확장을 협상하기를 기대했지만 서버가 WebSocket 핸드셰이크의 응답 메시지에서 그것들을 반환하지 않았기 때문에 연결을 종료하고 있음을 나타냅니다. 필요한 확장 목록은 Close 프레임의 /reason/ 부분에 나타나야 합니다(SHOULD). 이 상태 코드는 서버에 의해 사용되지 않는데, 서버는 대신 WebSocket 핸드셰이크를 실패시킬 수 있기 때문입니다. 1011 1011은 서버가 요청을 이행하는 것을 방해한 예기치 않은 조건을 만났기 때문에 연결을 종료하고 있음을 나타냅니다. 1015 1015는 예약된 값이며 엔드포인트에 의해 Close 제어 프레임의 상태 코드로 설정되어서는 안 됩니다(MUST NOT). TLS 핸드셰이크를 수행하는 데 실패하여 연결이 종료되었음을 나타내기 위해 상태 코드를 기대하는 애플리케이션에서 사용하도록 지정되었습니다(예: 서버 인증서를 확인할 수 없음). ### 7.4.2. 예약된 상태 코드 범위 0-999 0-999 범위의 상태 코드는 사용되지 않습니다. 1000-2999 1000-2999 범위의 상태 코드는 이 프로토콜, 그 미래 개정판 및 영구적이고 쉽게 이용 가능한 공개 사양에 지정된 확장에 의한 정의를 위해 예약되어 있습니다. 3000-3999 3000-3999 범위의 상태 코드는 라이브러리, 프레임워크 및 애플리케이션에서 사용하도록 예약되어 있습니다. 이러한 상태 코드는 IANA에 직접 등록됩니다. 이러한 코드의 해석은 이 프로토콜에 의해 정의되지 않습니다. 4000-4999 4000-4999 범위의 상태 코드는 개인 사용을 위해 예약되어 있으므로 등록할 수 없습니다. 이러한 코드는 WebSocket 애플리케이션 간의 사전 합의에 의해 사용될 수 있습니다. 이러한 코드의 해석은 이 프로토콜에 의해 정의되지 않습니다. # 8. 오류 처리 ## 8.1. UTF-8 인코딩 데이터의 오류 처리 엔드포인트가 바이트 스트림을 UTF-8로 해석해야 하지만 바이트 스트림이 실제로는 유효한 UTF-8 스트림이 아님을 발견하면, 해당 엔드포인트는 반드시 `_WebSocket 연결 실패_`를 해야 합니다(MUST). 이 규칙은 개시 핸드셰이크 중과 후속 데이터 교환 중 모두에 적용됩니다. # 9. 확장 WebSocket 클라이언트는 이 사양에 대한 확장을 요청할 수 있으며(MAY), WebSocket 서버는 클라이언트가 요청한 일부 또는 모든 확장을 수락할 수 있습니다(MAY). 서버는 클라이언트가 요청하지 않은 확장으로 응답해서는 안 됩니다(MUST NOT). 클라이언트와 서버 간의 협상에 확장 매개변수가 포함된 경우, 해당 매개변수는 반드시 매개변수가 적용되는 확장의 사양에 따라 선택되어야 합니다(MUST be chosen). ## 9.1. 확장 협상 클라이언트는 정상적인 HTTP 헤더 필드 규칙([[RFC2616| [RFC2616] ]], 섹션 4.2 참조)을 따르는 |Sec-WebSocket-Extensions| 헤더 필드를 포함하여 확장을 요청하며, 헤더 필드의 값은 다음 ABNF [[RFC2616| [RFC2616] ]]에 의해 정의됩니다. 이 섹션은 "implied *LWS rule"을 포함하여 [[RFC2616| [RFC2616] ]]의 ABNF 구문/규칙을 사용하고 있다는 점에 유의하십시오. 협상 중에 클라이언트나 서버가 아래 ABNF에 부합하지 않는 값을 수신하면, 그러한 잘못된 형식의 데이터의 수신자는 반드시 즉시 `_WebSocket 연결 실패_`를 해야 합니다(MUST). Sec-WebSocket-Extensions = extension-list extension-list = 1#extension extension = extension-token *( ";" extension-param ) extension-token = registered-token registered-token = token extension-param = token [ "=" (token | quoted-string) ] ;quoted-string 구문 변형을 사용할 때, 값은 ;quoted-string unescaping 후에 ;'token' ABNF에 부합해야 합니다(MUST). 다른 HTTP 헤더 필드와 마찬가지로, 이 헤더 필드는 여러 줄에 걸쳐 분할되거나 결합될 수 있습니다(MAY). 따라서, 다음은 동등합니다: Sec-WebSocket-Extensions: foo Sec-WebSocket-Extensions: bar; baz=2 는 정확히 다음과 동등합니다: Sec-WebSocket-Extensions: foo, bar; baz=2 사용되는 모든 extension-token은 반드시 등록된 토큰이어야 합니다(MUST be) (섹션 11.4 참조). 주어진 확장과 함께 제공되는 매개변수는 반드시 해당 확장에 대해 정의되어야 합니다(MUST be defined). 클라이언트는 광고된 확장을 사용하겠다고 제안하는 것일 뿐이며 서버가 확장을 사용하기를 원한다고 표시하지 않는 한 반드시 그것들을 사용해서는 안 됩니다(MUST NOT). 확장의 순서가 중요하다는 점에 유의하십시오. 여러 확장 간의 모든 상호 작용은 확장을 정의하는 문서에서 정의될 수 있습니다(MAY). 그러한 정의가 없는 경우, 해석은 클라이언트가 요청에 나열한 헤더 필드가 사용하기를 원하는 헤더 필드의 선호도를 나타내며, 처음 나열된 옵션이 가장 선호된다는 것입니다. 서버가 응답으로 나열한 확장은 연결에 실제로 사용되는 확장을 나타냅니다. 확장이 데이터 및/또는 프레이밍을 수정하는 경우, 데이터에 대한 작업 순서는 개시 핸드셰이크에서 서버의 응답에 확장이 나열된 순서와 동일하다고 가정해야 합니다. 예를 들어, "foo"와 "bar" 두 개의 확장이 있고 서버가 보낸 |Sec-WebSocket-Extensions| 헤더 필드의 값이 "foo, bar"이면, 데이터에 대한 작업은 bar(foo(data))로 이루어지며, 이는 데이터 자체에 대한 변경(예: 압축) 또는 "스택"될 수 있는 프레이밍에 대한 변경입니다. 허용 가능한 확장 헤더 필드의 비규범적 예(긴 줄은 가독성을 위해 접혀 있음): Sec-WebSocket-Extensions: deflate-stream Sec-WebSocket-Extensions: mux; max-channels=4; flow-control, deflate-stream Sec-WebSocket-Extensions: private-extension 서버는 클라이언트가 요청한 하나 이상의 확장을 포함하는 |Sec-WebSocket-Extensions| 헤더 필드를 포함하여 하나 이상의 확장을 수락합니다. 확장 매개변수의 해석과 클라이언트가 요청한 매개변수 집합에 대한 서버의 유효한 응답을 구성하는 것은 각 확장에 의해 정의됩니다. ## 9.2. 알려진 확장 확장은 구현이 추가 프로토콜 기능을 선택할 수 있는 메커니즘을 제공합니다. 이 문서는 어떤 확장도 정의하지 않지만, 구현은 별도로 정의된 확장을 사용할 수 있습니다(MAY). # 10. 보안 고려 사항 이 섹션은 WebSocket 프로토콜에 적용 가능한 일부 보안 고려 사항을 설명합니다. 특정 보안 고려 사항은 이 섹션의 하위 섹션에 설명되어 있습니다. ## 10.1. 비브라우저 클라이언트 WebSocket 프로토콜은 예를 들어 |Origin| 헤더 필드 확인(아래 참조)을 통해 웹 브라우저와 같은 신뢰할 수 있는 애플리케이션 내에서 실행되는 악의적인 JavaScript로부터 보호합니다. 추가 세부 정보는 섹션 1.6을 참조하십시오. 이러한 가정은 더 강력한 클라이언트의 경우에는 사실이 아닙니다. 이 프로토콜은 웹 페이지의 스크립트에서 사용되도록 의도되었지만, 호스트에 의해 직접 사용될 수도 있습니다. 이러한 호스트는 자신을 대신하여 행동하므로 가짜 |Origin| 헤더 필드를 보내 서버를 오도할 수 있습니다. 따라서 서버는 알려진 출처의 스크립트와 직접 통신하고 있다고 가정하는 것에 대해 주의해야 하며 예상치 못한 방식으로 액세스될 수 있다는 것을 고려해야 합니다. 특히, 서버는 어떤 입력도 유효하다고 신뢰해서는 안 됩니다. 예: 서버가 SQL 쿼리의 일부로 입력을 사용하는 경우, 모든 입력 텍스트는 SQL 서버로 전달되기 전에 이스케이프되어야 하며, 그렇지 않으면 서버가 SQL 인젝션에 취약할 수 있습니다. ## 10.2. 출처 고려 사항 모든 웹 페이지가 아니라 특정 사이트의 입력만 처리하도록 의도된 서버는 |Origin| 필드가 예상하는 출처인지 확인해야 합니다(SHOULD). 표시된 출처가 서버에 허용되지 않으면, 서버는 HTTP 403 Forbidden 상태 코드를 포함하는 응답으로 WebSocket 핸드셰이크에 응답해야 합니다(SHOULD). |Origin| 헤더 필드는 신뢰할 수 없는 당사자가 일반적으로 신뢰할 수 있는 클라이언트의 컨텍스트에서 실행되는 JavaScript 애플리케이션의 작성자인 경우의 공격 사례로부터 보호합니다. 클라이언트 자체는 서버에 연결할 수 있으며, |Origin| 헤더 필드의 메커니즘을 통해 JavaScript 애플리케이션에 해당 통신 권한을 확장할지 여부를 결정할 수 있습니다. 의도는 비브라우저가 연결을 설정하는 것을 방지하는 것이 아니라, 잠재적으로 악의적인 JavaScript의 제어 하에 있는 신뢰할 수 있는 브라우저가 WebSocket 핸드셰이크를 위조할 수 없도록 보장하는 것입니다. ## 10.3. 인프라에 대한 공격 (마스킹) 엔드포인트가 WebSocket을 통한 공격의 대상이 되는 것 외에도, 프록시와 같은 웹 인프라의 다른 부분이 공격의 대상이 될 수 있습니다. 이 프로토콜이 개발되는 동안, 실제 배포된 캐싱 프록시의 중독으로 이어지는 프록시에 대한 일련의 공격을 입증하기 위한 실험이 수행되었습니다 [TALKING]. 공격의 일반적인 형태는 "공격자"의 제어 하에 있는 서버에 대한 연결을 설정하고, WebSocket 프로토콜이 연결을 설정하기 위해 수행하는 것과 유사하게 HTTP 연결에서 UPGRADE를 수행하고, 이후 그 UPGRADEd 연결을 통해 특정 알려진 리소스(공격에서는 히트 추적을 위한 광범위하게 배포된 스크립트나 광고 제공 네트워크의 리소스와 같은 것일 가능성이 높음)에 대한 GET 요청처럼 보이는 데이터를 보내는 것이었습니다. 원격 서버는 가짜 GET 요청에 대한 응답처럼 보이는 것으로 응답하고, 이 응답은 배포된 중개자의 0이 아닌 비율에 의해 캐시되어 캐시를 중독시킵니다. 이 공격의 순 효과는 사용자가 공격자가 제어하는 웹사이트를 방문하도록 설득될 수 있다면, 공격자가 잠재적으로 해당 사용자와 동일한 캐시 뒤에 있는 다른 사용자의 캐시를 중독시키고 다른 출처에서 악의적인 스크립트를 실행하여 웹 보안 모델을 손상시킬 수 있다는 것입니다. 배포된 중개자에 대한 이러한 공격을 피하기 위해서는, HTTP와 호환되지 않는 프레이밍으로 애플리케이션 제공 데이터를 접두사로 붙이는 것만으로는 충분하지 않습니다. 왜냐하면 각 비준수 중개자가 그러한 비HTTP 프레이밍을 건너뛰지 않고 프레임 페이로드에 대해 잘못 행동하지 않는다는 것을 완전히 발견하고 테스트하는 것이 불가능하기 때문입니다. 따라서, 채택된 방어는 클라이언트에서 서버로의 모든 데이터를 마스킹하는 것이므로, 원격 스크립트(공격자)가 전송되는 데이터가 와이어에 나타나는 방식을 제어할 수 없으며 따라서 중개자에 의해 HTTP 요청으로 잘못 해석될 수 있는 메시지를 구성할 수 없습니다. 클라이언트는 반드시 각 프레임에 대해 새로운 마스킹 키를 선택해야 하며(MUST), 데이터를 제공하는 최종 애플리케이션에 의해 예측될 수 없는 알고리즘을 사용해야 합니다. 예를 들어, 각 마스킹은 암호학적으로 강력한 난수 생성기에서 가져올 수 있습니다. 동일한 키가 사용되거나 다음 키가 선택되는 방법에 대한 해독 가능한 패턴이 존재하는 경우, 공격자는 마스킹될 때 HTTP 요청처럼 보일 수 있는 메시지를 보낼 수 있습니다(공격자가 와이어에서 보고 싶어하는 메시지를 가져와 사용될 다음 마스킹 키로 마스킹함으로써, 클라이언트가 적용할 때 마스킹 키가 효과적으로 데이터를 마스킹 해제하게 됩니다). 또한 클라이언트로부터 프레임의 전송이 시작되면, 해당 프레임의 페이로드(애플리케이션 제공 데이터)가 애플리케이션에 의해 수정될 수 없어야 합니다. 그렇지 않으면, 공격자는 초기 데이터가 알려진 값(예: 모두 0)인 긴 프레임을 보내고, 데이터의 첫 부분을 수신할 때 사용 중인 마스킹 키를 계산한 다음, 마스킹될 때 HTTP 요청처럼 보이도록 프레임에서 아직 전송되지 않은 데이터를 수정할 수 있습니다. (이것은 본질적으로 알려진 또는 예측 가능한 마스킹 키를 사용하는 것과 관련하여 이전 단락에서 설명한 것과 동일한 문제입니다.) 추가 데이터를 보내거나 보낼 데이터가 어떻게든 변경되는 경우, 그 새롭거나 변경된 데이터는 새 프레임에서 그리고 따라서 새 마스킹 키로 보내야 합니다. 요약하자면, 프레임의 전송이 시작되면, 내용은 원격 스크립트(애플리케이션)에 의해 수정될 수 없어야 합니다. 보호되는 위협 모델은 클라이언트가 HTTP 요청처럼 보이는 데이터를 보내는 것입니다. 따라서, 마스킹되어야 하는 채널은 클라이언트에서 서버로의 데이터입니다. 서버에서 클라이언트로의 데이터는 응답처럼 보이게 만들 수 있지만, 이 요청을 수행하려면 클라이언트도 요청을 위조할 수 있어야 합니다. 따라서, 양방향으로 데이터를 마스킹할 필요가 없다고 간주되었습니다(서버에서 클라이언트로의 데이터는 마스킹되지 않습니다). 마스킹에 의해 제공되는 보호에도 불구하고, 비준수 HTTP 프록시는 여전히 마스킹을 적용하지 않는 클라이언트와 서버에 의한 이러한 유형의 중독 공격에 취약합니다. ## 10.4. 구현별 제한 여러 프레임에서 재조립 후 프레임 크기 또는 전체 메시지 크기에 관한 구현 및/또는 플랫폼별 제한이 있는 구현은 반드시 그러한 제한을 초과하지 않도록 자신을 보호해야 합니다(MUST). (예를 들어, 악의적인 엔드포인트는 단일 큰 프레임(예: 크기 `2**60`)을 보내거나 단편화된 메시지의 일부인 작은 프레임의 긴 스트림을 보내어 피어의 메모리를 소진시키거나 서비스 거부 공격을 시도할 수 있습니다.) 이러한 구현은 프레임 크기와 여러 프레임에서 재조립 후 전체 메시지 크기에 대한 제한을 부과해야 합니다(SHOULD). ## 10.5. WebSocket 클라이언트 인증 이 프로토콜은 WebSocket 핸드셰이크 중에 서버가 클라이언트를 인증할 수 있는 특정 방법을 규정하지 않습니다. WebSocket 서버는 쿠키, HTTP 인증 또는 TLS 인증과 같은 일반 HTTP 서버에서 사용할 수 있는 모든 클라이언트 인증 메커니즘을 사용할 수 있습니다. ## 10.6. 연결 기밀성 및 무결성 연결 기밀성 및 무결성은 TLS를 통해 WebSocket 프로토콜을 실행함으로써(wss URI) 제공됩니다. WebSocket 구현은 반드시 TLS를 지원해야 하며(MUST), 피어와 통신할 때 이를 사용해야 합니다(SHOULD). TLS를 사용하는 연결의 경우, TLS에 의해 제공되는 이점의 양은 TLS 핸드셰이크 중에 협상된 알고리즘의 강도에 크게 의존합니다. 예를 들어, 일부 TLS 암호 메커니즘은 연결 기밀성을 제공하지 않습니다. 합리적인 수준의 보호를 달성하려면, 클라이언트는 강력한 TLS 알고리즘만 사용해야 합니다. "Web Security Context: User Interface Guidelines" [W3C.REC-wsc-ui-20100812]는 강력한 TLS 알고리즘을 구성하는 것이 무엇인지 논의합니다. [[RFC5246| [RFC5246] ]]는 부록 A.5와 부록 D.3에서 추가 지침을 제공합니다. ## 10.7. 유효하지 않은 데이터 처리 들어오는 데이터는 반드시 항상 클라이언트와 서버 모두에 의해 검증되어야 합니다(MUST). 언제든지, 엔드포인트가 이해하지 못하는 데이터나 엔드포인트가 입력의 안전성을 결정하는 기준을 위반하는 데이터에 직면하거나, 엔드포인트가 예상하는 값과 일치하지 않는 개시 핸드셰이크를 볼 때(예: 클라이언트 요청의 잘못된 경로나 출처), 엔드포인트는 TCP 연결을 끊을 수 있습니다(MAY). 성공적인 WebSocket 핸드셰이크 이후에 유효하지 않은 데이터가 수신된 경우, 엔드포인트는 `_WebSocket 연결 종료_`를 진행하기 전에 적절한 상태 코드(섹션 7.4)를 가진 Close 프레임을 보내야 합니다(SHOULD). 적절한 상태 코드를 가진 Close 프레임의 사용은 문제를 진단하는 데 도움이 될 수 있습니다. WebSocket 핸드셰이크 중에 유효하지 않은 데이터가 전송되는 경우, 서버는 적절한 HTTP [[RFC2616| [RFC2616] ]] 상태 코드를 반환해야 합니다(SHOULD). 보안 문제의 일반적인 클래스는 잘못된 인코딩을 사용하여 텍스트 데이터를 보낼 때 발생합니다. 이 프로토콜은 Text 데이터 유형을 가진 메시지(Binary 또는 다른 유형과 대조적으로)가 UTF-8로 인코딩된 데이터를 포함하도록 지정합니다. 길이가 여전히 표시되고 이 프로토콜을 구현하는 애플리케이션은 프레임이 실제로 끝나는 위치를 결정하기 위해 길이를 사용해야 하지만, 부적절한 인코딩으로 데이터를 보내는 것은 여전히 이 프로토콜 위에 구축된 애플리케이션이 만들 수 있는 가정을 깨뜨릴 수 있으며, 데이터 오해석에서 데이터 손실 또는 잠재적인 보안 버그에 이르기까지 모든 것으로 이어질 수 있습니다. ## 10.8. WebSocket 핸드셰이크에서 SHA-1 사용 이 문서에 설명된 WebSocket 핸드셰이크는 충돌 저항성이나 두 번째 사전 이미지 공격에 대한 저항성([[RFC4270| [RFC4270] ]]에 설명된 대로)과 같은 SHA-1의 보안 속성에 의존하지 않습니다. # 11. IANA 고려 사항 ## 11.1. 새 URI 스킴 등록 ### 11.1.1. "ws" 스킴 등록 |ws| URI는 WebSocket 서버와 리소스 이름을 식별합니다. URI 스킴 이름 ws 상태 영구 URI 스킴 구문 ABNF [[RFC5234| [RFC5234] ]] 구문과 URI 사양 [[RFC3986| [RFC3986] ]]의 ABNF 터미널을 사용: ``` "ws:" "//" authority path-abempty [ "?" query ] ``` `<path-abempty>`와 `<query>` [RFC3986] 구성 요소는 원하는 서비스 종류를 식별하기 위해 서버로 전송되는 리소스 이름을 형성합니다. 다른 구성 요소는 [[RFC3986| [RFC3986] ]]에 설명된 의미를 갖습니다. URI 스킴 의미론 이 스킴에 대한 유일한 작업은 WebSocket 프로토콜을 사용하여 연결을 여는 것입니다. 인코딩 고려 사항 위에서 정의된 구문으로 제외되는 호스트 구성 요소의 문자는 [[RFC3987| [RFC3987] ]] 또는 그 대체에서 지정된 대로 유니코드에서 ASCII로 변환되어야 합니다(MUST). 스킴 기반 정규화를 위해, 호스트 구성 요소의 국제화된 도메인 이름(IDN) 형식과 punycode로의 변환은 동등한 것으로 간주됩니다([[RFC3987| [RFC3987] ]]의 섹션 5.3.3 참조). 위에서 정의된 구문으로 제외되는 다른 구성 요소의 문자는 먼저 문자를 UTF-8로 인코딩한 다음 URI [[RFC3986| [RFC3986] ]]와 국제화된 리소스 식별자(IRI) [[RFC3987| [RFC3987] ]] 사양에 정의된 대로 백분율 인코딩된 형식을 사용하여 해당 바이트를 대체함으로써 유니코드에서 ASCII로 변환되어야 합니다(MUST). 이 URI 스킴 이름을 사용하는 애플리케이션/프로토콜 WebSocket 프로토콜 상호 운용성 고려 사항 WebSocket 사용은 HTTP 버전 1.1 이상의 사용을 요구합니다. 보안 고려 사항 "보안 고려 사항" 섹션 참조. 연락처 HYBI WG <hybi@ietf.org> 작성자/변경 제어자 IETF <iesg@ietf.org> 참조 자료 RFC 6455 ### 11.1.2. "wss" 스킴 등록 |wss| URI는 WebSocket 서버와 리소스 이름을 식별하며 해당 연결을 통한 트래픽이 TLS를 통해 보호됨을 나타냅니다(데이터 기밀성 및 무결성과 엔드포인트 인증과 같은 TLS의 표준 이점 포함). URI 스킴 이름 wss 상태 영구 URI 스킴 구문 ABNF [[RFC5234| [RFC5234] ]] 구문과 URI 사양 [[RFC3986| [RFC3986] ]]의 ABNF 터미널을 사용: ``` "wss:" "//" authority path-abempty [ "?" query ] ``` `<path-abempty>`와 `<query>` 구성 요소는 원하는 서비스 종류를 식별하기 위해 서버로 전송되는 리소스 이름을 형성합니다. 다른 구성 요소는 [[RFC3986| [RFC3986] ]]에 설명된 의미를 갖습니다. URI 스킴 의미론 이 스킴에 대한 유일한 작업은 TLS를 사용하여 암호화된 WebSocket 프로토콜을 사용하여 연결을 여는 것입니다. 인코딩 고려 사항 위에서 정의된 구문으로 제외되는 호스트 구성 요소의 문자는 [[RFC3987| [RFC3987] ]] 또는 그 대체에서 지정된 대로 유니코드에서 ASCII로 변환되어야 합니다(MUST). 스킴 기반 정규화를 위해, 호스트 구성 요소의 IDN 형식과 punycode로의 변환은 동등한 것으로 간주됩니다([[RFC3987| [RFC3987] ]]의 섹션 5.3.3 참조). 위에서 정의된 구문으로 제외되는 다른 구성 요소의 문자는 먼저 문자를 UTF-8로 인코딩한 다음 URI [[RFC3986| [RFC3986] ]]와 IRI [[RFC3987| [RFC3987] ]] 사양에 정의된 대로 백분율 인코딩된 형식을 사용하여 해당 바이트를 대체함으로써 유니코드에서 ASCII로 변환되어야 합니다(MUST). 이 URI 스킴 이름을 사용하는 애플리케이션/프로토콜 TLS를 통한 WebSocket 프로토콜 상호 운용성 고려 사항 WebSocket 사용은 HTTP 버전 1.1 이상의 사용을 요구합니다. 보안 고려 사항 "보안 고려 사항" 섹션 참조. 연락처 HYBI WG <hybi@ietf.org> 작성자/변경 제어자 IETF <iesg@ietf.org> 참조 자료 RFC 6455 ## 11.2. "WebSocket" HTTP Upgrade 키워드 등록 이 섹션은 RFC 2817 [[RFC2817| [RFC2817] ]]에 따라 HTTP Upgrade 토큰 레지스트리에 등록된 키워드를 정의합니다. 토큰 이름 WebSocket 작성자/변경 제어자 IETF <iesg@ietf.org> 연락처 HYBI <hybi@ietf.org> 참조 자료 RFC 6455 ## 11.3. 새 HTTP 헤더 필드 등록 ### 11.3.1. Sec-WebSocket-Key 이 섹션은 Permanent Message Header Field Names 레지스트리 [[RFC3864| [RFC3864] ]]에 등록된 헤더 필드를 설명합니다. 헤더 필드 이름 Sec-WebSocket-Key 적용 가능한 프로토콜 http 상태 standard 작성자/변경 제어자 IETF 사양 문서 RFC 6455 관련 정보 이 헤더 필드는 WebSocket 개시 핸드셰이크에만 사용됩니다. |Sec-WebSocket-Key| 헤더 필드는 WebSocket 개시 핸드셰이크에서 사용됩니다. 클라이언트에서 서버로 전송되어 서버가 유효한 WebSocket 개시 핸드셰이크를 수신했음을 증명하는 데 사용되는 정보의 일부를 제공합니다. 이것은 서버가 의심하지 않는 WebSocket 서버로 데이터를 보내기 위해 악용되는 비WebSocket 클라이언트(예: HTTP 클라이언트)로부터의 연결을 수락하지 않도록 보장하는 데 도움이 됩니다. |Sec-WebSocket-Key| 헤더 필드는 HTTP 요청에서 두 번 이상 나타나서는 안 됩니다(MUST NOT). ### 11.3.2. Sec-WebSocket-Extensions 이 섹션은 Permanent Message Header Field Names 레지스트리 [[RFC3864| [RFC3864] ]]에 등록하기 위한 헤더 필드를 설명합니다. 헤더 필드 이름 Sec-WebSocket-Extensions 적용 가능한 프로토콜 http 상태 standard 작성자/변경 제어자 IETF 사양 문서 RFC 6455 관련 정보 이 헤더 필드는 WebSocket 개시 핸드셰이크에만 사용됩니다. |Sec-WebSocket-Extensions| 헤더 필드는 WebSocket 개시 핸드셰이크에서 사용됩니다. 처음에는 클라이언트에서 서버로 전송되고, 이후 서버에서 클라이언트로 전송되어 연결 기간 동안 사용할 프로토콜 수준 확장 집합에 동의합니다. |Sec-WebSocket-Extensions| 헤더 필드는 HTTP 요청에서 여러 번 나타날 수 있습니다(MAY)(이는 논리적으로 모든 값을 포함하는 단일 |Sec-WebSocket-Extensions| 헤더 필드와 동일합니다). |Sec-WebSocket-Extensions| 헤더 필드는 HTTP 응답에서 여러 번 나타날 수 있습니다(MAY)(이는 논리적으로 모든 값을 포함하는 단일 |Sec-WebSocket-Extensions| 헤더 필드와 동일합니다). ### 11.3.3. Sec-WebSocket-Accept 이 섹션은 Permanent Message Header Field Names 레지스트리 [[RFC3864| [RFC3864] ]]에 등록된 헤더 필드를 설명합니다. 헤더 필드 이름 Sec-WebSocket-Accept 적용 가능한 프로토콜 http 상태 standard 작성자/변경 제어자 IETF 사양 문서 RFC 6455 관련 정보 이 헤더 필드는 WebSocket 개시 핸드셰이크에만 사용됩니다. |Sec-WebSocket-Accept| 헤더 필드는 WebSocket 개시 핸드셰이크에서 사용됩니다. 서버에서 클라이언트로 전송되어 서버가 WebSocket 연결을 시작할 의사가 있음을 확인합니다. |Sec-WebSocket-Accept| 헤더는 HTTP 응답에서 두 번 이상 나타나서는 안 됩니다(MUST NOT). ### 11.3.4. Sec-WebSocket-Protocol 이 섹션은 Permanent Message Header Field Names 레지스트리 [[RFC3864| [RFC3864] ]]에 등록된 헤더 필드를 설명합니다. 헤더 필드 이름 Sec-WebSocket-Protocol 적용 가능한 프로토콜 http 상태 standard 작성자/변경 제어자 IETF 사양 문서 RFC 6455 관련 정보 이 헤더 필드는 WebSocket 개시 핸드셰이크에만 사용됩니다. |Sec-WebSocket-Protocol| 헤더 필드는 WebSocket 개시 핸드셰이크에서 사용됩니다. 클라이언트에서 서버로, 그리고 서버에서 다시 클라이언트로 전송되어 연결의 서브프로토콜을 확인합니다. 이를 통해 스크립트는 서브프로토콜을 선택하고 서버가 해당 서브프로토콜을 제공하기로 동의했는지 확인할 수 있습니다. |Sec-WebSocket-Protocol| 헤더 필드는 HTTP 요청에서 여러 번 나타날 수 있습니다(MAY)(이는 논리적으로 모든 값을 포함하는 단일 |Sec-WebSocket-Protocol| 헤더 필드와 동일합니다). 그러나 |Sec-WebSocket-Protocol| 헤더 필드는 HTTP 응답에서 두 번 이상 나타나서는 안 됩니다(MUST NOT). ### 11.3.5. Sec-WebSocket-Version 이 섹션은 Permanent Message Header Field Names 레지스트리 [[RFC3864| [RFC3864] ]]에 등록된 헤더 필드를 설명합니다. 헤더 필드 이름 Sec-WebSocket-Version 적용 가능한 프로토콜 http 상태 standard 작성자/변경 제어자 IETF 사양 문서 RFC 6455 관련 정보 이 헤더 필드는 WebSocket 개시 핸드셰이크에만 사용됩니다. |Sec-WebSocket-Version| 헤더 필드는 WebSocket 개시 핸드셰이크에서 사용됩니다. 클라이언트에서 서버로 전송되어 연결의 프로토콜 버전을 나타냅니다. 이를 통해 서버는 개시 핸드셰이크와 데이터에서 전송되는 후속 데이터를 올바르게 해석할 수 있으며, 서버가 해당 데이터를 안전한 방식으로 해석할 수 없는 경우 연결을 닫을 수 있습니다. |Sec-WebSocket-Version| 헤더 필드는 WebSocket 핸드셰이크 오류 시 서버에서 클라이언트로도 전송되며, 클라이언트로부터 수신된 버전이 서버가 이해하는 버전과 일치하지 않을 때 발생합니다. 이러한 경우 헤더 필드에는 서버가 지원하는 프로토콜 버전이 포함됩니다. 더 높은 버전 번호가 반드시 더 낮은 버전 번호와 하위 호환되는 것은 아닙니다. |Sec-WebSocket-Version| 헤더 필드는 HTTP 응답에서 여러 번 나타날 수 있습니다(MAY)(이는 논리적으로 모든 값을 포함하는 단일 |Sec-WebSocket-Version| 헤더 필드와 동일합니다). 그러나 |Sec-WebSocket-Version| 헤더 필드는 HTTP 요청에서 두 번 이상 나타나서는 안 됩니다(MUST NOT). ## 11.4. WebSocket 확장 이름 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket 프로토콜과 함께 사용할 WebSocket 확장 이름에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리의 일부로, IANA는 다음 정보를 유지합니다: 확장 식별자 이 사양의 섹션 11.3.2에 등록된 |Sec-WebSocket-Extensions| 헤더 필드에서 사용될 확장의 식별자입니다. 값은 이 사양의 섹션 9.1에 정의된 extension-token에 대한 요구 사항을 준수해야 합니다. 확장 일반 이름 일반적으로 참조되는 확장의 이름입니다. 확장 정의 WebSocket 프로토콜과 함께 사용되는 확장이 정의된 문서에 대한 참조입니다. 알려진 비호환 확장 이 확장과 호환되지 않는 것으로 알려진 확장 식별자 목록입니다. WebSocket 확장 이름은 [[RFC5226| [RFC5226] ]]의 "First Come First Served" IANA 등록 정책의 적용을 받습니다. 이 레지스트리에는 초기 값이 없습니다. ## 11.5. WebSocket 서브프로토콜 이름 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket 프로토콜과 함께 사용할 WebSocket 서브프로토콜 이름에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리의 일부로, IANA는 다음 정보를 유지합니다: 서브프로토콜 식별자 이 사양의 섹션 11.3.4에 등록된 |Sec-WebSocket-Protocol| 헤더 필드에서 사용될 서브프로토콜의 식별자입니다. 값은 이 사양의 섹션 4.1의 항목 10에 제공된 요구 사항을 준수해야 합니다. 즉, 값은 RFC 2616 [[RFC2616| [RFC2616] ]]에서 정의한 토큰이어야 합니다. 서브프로토콜 일반 이름 일반적으로 참조되는 서브프로토콜의 이름입니다. 서브프로토콜 정의 WebSocket 프로토콜과 함께 사용되는 서브프로토콜이 정의된 문서에 대한 참조입니다. WebSocket 서브프로토콜 이름은 [[RFC5226| [RFC5226] ]]의 "First Come First Served" IANA 등록 정책의 적용을 받습니다. ## 11.6. WebSocket 버전 번호 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket 프로토콜과 함께 사용할 WebSocket 버전 번호에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리의 일부로, IANA는 다음 정보를 유지합니다: 버전 번호 |Sec-WebSocket-Version|에서 사용될 버전 번호는 이 사양의 섹션 4.1에 지정되어 있습니다. 값은 0과 255(포함) 사이의 범위에 있는 음이 아닌 정수여야 합니다. 참조 새 버전 번호를 요청하는 RFC 또는 버전 번호가 포함된 드래프트 이름(아래 참조). 상태 "Interim" 또는 "Standard". 아래 설명 참조. 버전 번호는 "Interim" 또는 "Standard"로 지정됩니다. "Standard" 버전 번호는 RFC에 문서화되어 있으며 이 RFC에서 정의한 버전과 같은 WebSocket 프로토콜의 주요하고 안정적인 버전을 식별하는 데 사용됩니다. "Standard" 버전 번호는 [[RFC5226| [RFC5226] ]]의 "IETF Review" IANA 등록 정책의 적용을 받습니다. "Interim" 버전 번호는 Internet-Draft에 문서화되어 있으며 이 RFC 발행 전에 개발된 버전과 같이 배포된 WebSocket 프로토콜 버전을 구현자가 식별하고 상호 운용할 수 있도록 돕는 데 사용됩니다. "Interim" 버전 번호는 [[RFC5226| [RFC5226] ]]의 "Expert Review" IANA 등록 정책의 적용을 받으며, HYBI 워킹 그룹의 의장(또는 워킹 그룹이 종료되는 경우 IETF Applications Area의 Area Directors)이 초기 지정 전문가입니다. IANA는 다음과 같이 레지스트리에 초기 값을 추가했습니다. ``` +--------+-----------------------------------------+----------+ | 버전 | 참조 | 상태 | | 번호 | | | +--------+-----------------------------------------+----------+ | 0 + draft-ietf-hybi-thewebsocketprotocol-00 | Interim | +--------+-----------------------------------------+----------+ | 1 + draft-ietf-hybi-thewebsocketprotocol-01 | Interim | +--------+-----------------------------------------+----------+ | 2 + draft-ietf-hybi-thewebsocketprotocol-02 | Interim | +--------+-----------------------------------------+----------+ | 3 + draft-ietf-hybi-thewebsocketprotocol-03 | Interim | +--------+-----------------------------------------+----------+ | 4 + draft-ietf-hybi-thewebsocketprotocol-04 | Interim | +--------+-----------------------------------------+----------+ | 5 + draft-ietf-hybi-thewebsocketprotocol-05 | Interim | +--------+-----------------------------------------+----------+ | 6 + draft-ietf-hybi-thewebsocketprotocol-06 | Interim | +--------+-----------------------------------------+----------+ | 7 + draft-ietf-hybi-thewebsocketprotocol-07 | Interim | +--------+-----------------------------------------+----------+ | 8 + draft-ietf-hybi-thewebsocketprotocol-08 | Interim | +--------+-----------------------------------------+----------+ | 9 + Reserved | | +--------+-----------------------------------------+----------+ | 10 + Reserved | | +--------+-----------------------------------------+----------+ | 11 + Reserved | | +--------+-----------------------------------------+----------+ | 12 + Reserved | | +--------+-----------------------------------------+----------+ | 13 + RFC 6455 | Standard | +--------+-----------------------------------------+----------+ ``` ## 11.7. WebSocket Close Code Number 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket 연결 Close Code Number에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리의 일부로, IANA는 다음 정보를 유지합니다: 상태 코드 상태 코드는 이 문서의 섹션 7.4에 따라 WebSocket 연결 종료 이유를 나타냅니다. 상태 코드는 1000과 4999(포함) 사이의 정수입니다. 의미 상태 코드의 의미입니다. 각 상태 코드는 고유한 의미를 가져야 합니다. 연락처 상태 코드를 예약하는 엔터티에 대한 연락처입니다. 참조 상태 코드를 요청하고 그 의미를 정의하는 안정적인 문서입니다. 이는 1000-2999 범위의 상태 코드에 필수이며 3000-3999 범위의 상태 코드에 권장됩니다. WebSocket Close Code Number는 범위에 따라 다른 등록 요구 사항의 적용을 받습니다. 이 프로토콜 및 그 후속 버전 또는 확장에서 사용하기 위한 상태 코드 요청은 "Standards Action", "Specification Required"(이는 "Designated Expert"를 의미함) 또는 "IESG Review" IANA 등록 정책 중 하나의 적용을 받으며 1000-2999 범위에서 부여되어야 합니다. 라이브러리, 프레임워크 및 애플리케이션에서 사용하기 위한 상태 코드 요청은 "First Come First Served" IANA 등록 정책의 적용을 받으며 3000-3999 범위에서 부여되어야 합니다. 4000-4999 범위의 상태 코드는 개인 사용을 위해 지정되어 있습니다. 요청은 WebSocket 프로토콜(또는 프로토콜의 향후 버전), 확장 또는 라이브러리/프레임워크/애플리케이션에서 사용하기 위한 상태 코드를 요청하는지 여부를 명시해야 합니다. IANA는 다음과 같이 레지스트리에 초기 값을 추가했습니다. |상태 코드 | 의미 | 연락처 | 참조 | -+----------+---------------------+---------------+-----------| | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1001 | Going Away | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1002 | Protocol error | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1003 | Unsupported Data | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1006 | Abnormal Closure | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 | | | payload data | | | -+----------+---------------------+---------------+-----------| | 1008 | Policy Violation | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| | 1011 | Internal Server | hybi@ietf.org | RFC 6455 | | | Error | | | -+----------+---------------------+---------------+-----------| | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 | -+----------+---------------------+---------------+-----------| ## 11.8. WebSocket Opcode 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket Opcode에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리의 일부로, IANA는 다음 정보를 유지합니다: Opcode opcode는 섹션 5.2에 정의된 대로 WebSocket 프레임의 프레임 유형을 나타냅니다. opcode는 0과 15(포함) 사이의 정수입니다. 의미 opcode 값의 의미입니다. 참조 opcode를 요청하는 사양입니다. WebSocket Opcode 번호는 [[RFC5226| [RFC5226] ]]의 "Standards Action" IANA 등록 정책의 적용을 받습니다. IANA는 다음과 같이 레지스트리에 초기 값을 추가했습니다. ``` |Opcode | 의미 | 참조 | -+--------+-------------------------------------+-----------| | 0 | Continuation Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 1 | Text Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 2 | Binary Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 8 | Connection Close Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 9 | Ping Frame | RFC 6455 | -+--------+-------------------------------------+-----------| | 10 | Pong Frame | RFC 6455 | -+--------+-------------------------------------+-----------| ``` ## 11.9. WebSocket Framing Header Bits 레지스트리 이 사양은 RFC 5226 [[RFC5226| [RFC5226] ]]에 명시된 원칙에 따라 WebSocket Framing Header Bits에 대한 새로운 IANA 레지스트리를 생성합니다. 이 레지스트리는 섹션 5.2에서 RSV1, RSV2 및 RSV3로 표시된 비트의 할당을 제어합니다. 이러한 비트는 이 사양의 향후 버전 또는 확장을 위해 예약되어 있습니다. WebSocket Framing Header Bits 할당은 [[RFC5226| [RFC5226] ]]의 "Standards Action" IANA 등록 정책의 적용을 받습니다. # 12. 다른 사양에서 WebSocket 프로토콜 사용 WebSocket 프로토콜은 동적 작성자 정의 콘텐츠에 대한 일반 메커니즘을 제공하기 위해 다른 사양에서 사용될 수 있습니다. 예를 들어 스크립트 API를 정의하는 사양에서 사용됩니다. 이러한 사양은 먼저 `_WebSocket 연결 설정_`을 해야 하며, 해당 알고리즘에 다음을 제공해야 합니다: o /host/와 /port/로 구성된 대상. o /resource name/. 하나의 호스트와 포트에서 여러 서비스를 식별할 수 있도록 합니다. o /secure/ 플래그. 연결이 암호화되어야 하는 경우 true이고 그렇지 않으면 false입니다. o 연결을 담당하는 출처 [[RFC6454| [RFC6454] ]]의 ASCII 직렬화. o 선택적으로, WebSocket 연결 위에 계층화될 프로토콜을 식별하는 문자열. /host/, /port/, /resource name/ 및 /secure/ 플래그는 일반적으로 WebSocket URI의 구성 요소를 파싱하는 단계를 사용하여 URI로부터 얻어집니다. 이러한 단계는 URI가 WebSocket을 지정하지 않으면 실패합니다. 어느 시점에서든 연결을 닫아야 하는 경우, 사양은 `_WebSocket 연결 종료_` 알고리즘(섹션 7.1.1)을 사용해야 합니다. 섹션 7.1.4는 `_WebSocket 연결이 종료되었습니다_`가 언제인지 정의합니다. 연결이 열려 있는 동안, 사양은 `_WebSocket 메시지가 수신되었습니다_`(섹션 6.2)인 경우를 처리해야 합니다. 열린 연결에 일부 데이터 /data/를 보내려면, 사양은 `_WebSocket 메시지 보내기_`(섹션 6.1)를 수행해야 합니다. # 13. 감사의 말 이 프로토콜의 원저자이자 편집자였던 Ian Hickson에게 특별한 감사를 드립니다. 이 사양의 초기 설계는 WHATWG 및 WHATWG 메일링 리스트에 많은 사람들의 참여로 이익을 얻었습니다. 해당 사양에 대한 기여는 섹션별로 추적되지 않지만, 해당 사양에 기여한 모든 사람의 목록은 http://whatwg.org/html5 의 WHATWG HTML 사양에 제공되어 있습니다. 이 사양의 "데이터 프레이밍" 섹션에 대해 상당한 양의 텍스트를 제공한 John Tamplin에게도 특별한 감사를 드립니다. 이 사양의 "데이터 마스킹" 섹션에 대해 상당한 양의 텍스트와 배경 연구를 제공한 Adam Barth에게도 특별한 감사를 드립니다. Apps Area 검토(및 이 작업을 시작하는 데 도움을 준 것)에 대해 Lisa Dusseault에게, Gen-Art 검토에 대해 Richard Barnes에게, Transport Area 검토에 대해 Magnus Westerlund에게 특별한 감사를 드립니다. 이 작업을 완료로 이동시키기 위해 무대 뒤에서 지치지 않고 일한 과거 및 현재 HYBI WG 의장인 Joe Hildebrand, Salvatore Loreto 및 Gabriel Montenegro에게 특별한 감사를 드립니다. 그리고 마지막으로 책임 있는 Area Director Peter Saint-Andre에게 특별히 감사드립니다. HYBI WG 메일링 리스트에서 토론에 참여하고 아이디어를 기여하거나 상세한 검토를 제공한 다음 사람들에게 감사드립니다(목록이 불완전할 수 있습니다): Greg Wilkins, John Tamplin, Willy Tarreau, Maciej Stachowiak, Jamie Lokier, Scott Ferguson, Bjoern Hoehrmann, Julian Reschke, Dave Cridland, Andy Green, Eric Rescorla, Inaki Baz Castillo, Martin Thomson, Roberto Peon, Patrick McManus, Zhong Yu, Bruce Atherton, Takeshi Yoshino, Martin J. Duerst, James Graham, Simon Pieters, Roy T. Fielding, Mykyta Yevstifeyev, Len Holgate, Paul Colomiets, Piotr Kulaga, Brian Raymor, Jan Koehler, Joonas Lehtolahti, Sylvain Hellegouarch, Stephen Farrell, Sean Turner, Pete Resnick, Peter Thorson, Joe Mason, John Fallows 및 Alexander Philippou. 위에 나열된 사람들이 반드시 이 작업의 최종 결과를 지지한 것은 아닙니다. # 14. 참조 ## 14.1. 규범적 참조 `[ANSI.X3-4.1986]` American National Standards Institute, "Coded Character Set - 7-bit American Standard Code for Information Interchange", ANSI X3.4, 1986. `[FIPS.180-3]` National Institute of Standards and Technology, "Secure Hash Standard", FIPS PUB 180-3, October 2008, <http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf>. `[RFC1928]` Leech, M., Ganis, M., Lee, Y., Kuris, R., Koblas, D., and L. Jones, "SOCKS Protocol Version 5", RFC 1928, March 1996. `[RFC2119]` Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997. `[RFC2616]` Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. `[RFC2817]` Khare, R. and S. Lawrence, "Upgrading to TLS Within HTTP/1.1", RFC 2817, May 2000. `[RFC2818]` Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. `[RFC3629]` Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003. `[RFC3864]` Klyne, G., Nottingham, M., and J. Mogul, "Registration Procedures for Message Header Fields", BCP 90, RFC 3864, September 2004. `[RFC3986]` Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005. `[RFC3987]` Duerst, M. and M. Suignard, "Internationalized Resource Identifiers (IRIs)", RFC 3987, January 2005. `[RFC4086]` Eastlake, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, June 2005. `[RFC4648]` Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, October 2006. `[RFC5226]` Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 5226, May 2008. `[RFC5234]` Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, January 2008. `[RFC5246]` Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, August 2008. `[RFC6066]` Eastlake, D., "Transport Layer Security (TLS) Extensions: Extension Definitions", RFC 6066, January 2011. `[RFC6454]` Barth, A., "The Web Origin Concept", RFC 6454, December 2011. ## 14.2. 정보 제공 참조 `[RFC4122]` Leach, P., Mealling, M., and R. Salz, "A Universally Unique IDentifier (UUID) URN Namespace", RFC 4122, July 2005. `[RFC4270]` Hoffman, P. and B. Schneier, "Attacks on Cryptographic Hashes in Internet Protocols", RFC 4270, November 2005. `[RFC5321]` Klensin, J., "Simple Mail Transfer Protocol", RFC 5321, October 2008. `[RFC6202]` Loreto, S., Saint-Andre, P., Salsano, S., and G. Wilkins, "Known Issues and Best Practices for the Use of Long Polling and Streaming in Bidirectional HTTP", RFC 6202, April 2011. `[RFC6265]` Barth, A., "HTTP State Management Mechanism", RFC 6265, April 2011. `[TALKING]` Huang, L-S., Chen, E., Barth, A., Rescorla, E., and C. Jackson, "Talking to Yourself for Fun and Profit", 2010, <http://w2spconf.com/2011/papers/websocket.pdf>. `[W3C.REC-wsc-ui-20100812]` Roessler, T. and A. Saldhana, "Web Security Context: User Interface Guidelines", World Wide Web Consortium Recommendation REC-wsc-ui-20100812, August 2010, <http://www.w3.org/TR/2010/REC-wsc-ui-20100812/>. 최신 버전: <http://www.w3.org/TR/wsc-ui/>. `[WSAPI]` Hickson, I., "The WebSocket API", W3C Working Draft WD-websockets-20110929, September 2011, <http://www.w3.org/TR/2011/WD-websockets-20110929/>. 최신 버전: <http://www.w3.org/TR/websockets/>. `[XMLHttpRequest]` van Kesteren, A., Ed., "XMLHttpRequest", W3C Candidate Recommendation CR-XMLHttpRequest-20100803, August 2010, <http://www.w3.org/TR/2010/CR-XMLHttpRequest-20100803/>. 최신 버전: <http://www.w3.org/TR/XMLHttpRequest/>. **저자 주소** Ian Fette Google, Inc. EMail: ifette+ietf@google.com URI: http://www.ianfette.com/ Alexey Melnikov Isode Ltd. 5 Castle Business Village 36 Station Road Hampton, Middlesex TW12 2BX UK EMail: Alexey.Melnikov@isode.com