DefaultWebSocketFrameParser.java

package sprout.server.websocket;

import sprout.beans.annotation.Component;

import java.io.IOException;
import java.io.InputStream;

@Component
public class DefaultWebSocketFrameParser implements WebSocketFrameParser {

    @Override
    public WebSocketFrame parse(InputStream in) throws Exception {
        int b1 = in.read();
        int b2 = in.read();

        if (b1 == -1 || b2 == -1) {
            throw new RuntimeException("Unexpected end of stream while reading frame header.");
        }

        boolean fin = (b1 & 0x80) != 0;
        int opcode = b1 & 0x0F;

        boolean masked = (b2 & 0x80) != 0;
        int payloadLen = b2 & 0x7F;

        long actualPayloadLen;

        if (payloadLen == 126) {
            actualPayloadLen = ((in.read() & 0xFF) << 8) | (in.read() & 0xFF);
        } else if (payloadLen == 127) {
            actualPayloadLen = 0;
            for (int i = 0; i < 8; i++) {
                actualPayloadLen = (actualPayloadLen << 8) | (in.read() & 0xFF);
            }
        } else {
            actualPayloadLen = payloadLen;
        }

        byte[] maskingKey = new byte[4];
        if (masked) {
            if (in.read(maskingKey) != 4) {
                throw new IOException("Failed to read full 4-byte masking key from client stream.");
            }
        }

        // --- BUG FIX ---
        // [삭제] 불필요하게 페이로드를 미리 읽는 로직을 제거합니다.
        /*
        byte[] payloadData = new byte[(int) actualPayloadLen];
        int totalRead = 0;
        while (totalRead < actualPayloadLen) {
            int read = in.read(payloadData, totalRead, (int) (actualPayloadLen - totalRead));
            if (read == -1) break;
            totalRead += read;
        }
        */

        // 헤더와 마스킹 키 까지만 읽은 스트림을 LimitedInputStream으로 감쌉니다.
        InputStream payloadInputStream = new LimitedInputStream(in, actualPayloadLen);

        if (masked) {
            // 이 MaskingInputStream은 LimitedInputStream에서 읽은 바이트를 실시간으로 언마스킹합니다.
            payloadInputStream = new MaskingInputStream(payloadInputStream, maskingKey);
        }

        return new WebSocketFrame(fin, opcode, payloadInputStream);
    }
}