Minha estrutura não estava conseguindo levar em conta que o AMD64 é ordenado por little-endian, não big-endian.
Estou escrevendo um servidor WebSocket incorporado e trabalhando diretamente a partir do RFC relevante.
Meu servidor responde corretamente à solicitação de atualização do navegador e o navegador, em seu exemplo javascript, continua enviando uma mensagem curta pelo soquete recém-estabelecido. Então tudo está funcionando bem.
A mensagem é curta (o quadro completo tem apenas 21 bytes) e contém todos os campos relevantes que meu servidor decodifica felizmente.
O problema está nos bits 9 a 15, que devem conter o tamanho da carga útil.
Aqui está um dump hexadecimal da mensagem capturada no WireShark:
81 8f 11 ab d5 0b 5c ce a6 78 70 cc b0 2b 65 c4 f5 78 74 c5 b1
Como você pode ver, o primeiro byte contém FIN (1 bit), RSVD1 (1 bit), RSVD2 (1 bit), RSVD3 (1 bit) e os 4 bits do opcode. Até aí tudo bem.
8f
é o problema: contém o bit MASK e o comprimento da carga útil. O bit MASK é definido como 1, o que é bom, mas os 7 bits restantes têm um valor de 71 (0x47) quando o quadro inteiro tem apenas 21 bytes ea carga útil é de apenas 15 bytes.
Então, o que estou fazendo de errado?
Eu posso decodificar a mensagem aplicando a máscara XOR ao payload, mas o comprimento é o problema, pois ele governa o loop de decodificação e passa a 71 iterações, em vez das 15 que deveria.
Minha estrutura não estava conseguindo levar em conta que o AMD64 é ordenado por little-endian, não big-endian.