in src/main/java/com/microsoft/azure/proton/transport/ws/impl/WebSocketHandlerImpl.java [88:167]
public void wrapBuffer(ByteBuffer srcBuffer, ByteBuffer dstBuffer) {
if ((srcBuffer == null) || (dstBuffer == null)) {
throw new IllegalArgumentException("input parameter is null");
}
if (srcBuffer.remaining() > 0) {
// We always send masked data
// RFC: "client MUST mask all frames that it sends to the server"
final byte[] maskingKey = createRandomMaskingKey();
// Get data length
final int dataLength = srcBuffer.remaining();
// Auto growing buffer for the WS frame, initialized to minimum size
ByteArrayOutputStream webSocketFrame = new ByteArrayOutputStream(WebSocketHeader.MIN_HEADER_LENGTH_MASKED + dataLength);
// Create the first byte
// We always send final WebSocket frame
// We always send binary message (AMQP)
byte firstByte = (byte) (WebSocketHeader.FINBIT_MASK | WebSocketHeader.OPCODE_BINARY);
webSocketFrame.write(firstByte);
// Create the second byte
// RFC: "client MUST mask all frames that it sends to the server"
byte secondByte = WebSocketHeader.MASKBIT_MASK;
// RFC: The length of the "Payload data", in bytes: if 0-125, that is the payload length.
if (dataLength <= WebSocketHeader.PAYLOAD_SHORT_MAX) {
secondByte = (byte) (secondByte | dataLength);
webSocketFrame.write(secondByte);
} else if (dataLength <= WebSocketHeader.PAYLOAD_MEDIUM_MAX) {
// RFC: If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length
// Create payload byte
secondByte = (byte) (secondByte | WebSocketHeader.PAYLOAD_EXTENDED_16);
webSocketFrame.write(secondByte);
// Create extended length bytes
webSocketFrame.write((byte) (dataLength >>> 8));
webSocketFrame.write((byte) (dataLength));
} else {
// RFC: If 127, the following 8 bytes interpreted as a 64-bit unsigned integer
// (the most significant bit MUST be 0) are the payload length.
// No need for "else if" because if it is longer than what 8 byte length can hold... all bets are off anyway
secondByte = (byte) (secondByte | WebSocketHeader.PAYLOAD_EXTENDED_64);
webSocketFrame.write(secondByte);
webSocketFrame.write((byte) (dataLength >>> 56));
webSocketFrame.write((byte) (dataLength >>> 48));
webSocketFrame.write((byte) (dataLength >>> 40));
webSocketFrame.write((byte) (dataLength >>> 32));
webSocketFrame.write((byte) (dataLength >>> 24));
webSocketFrame.write((byte) (dataLength >>> 16));
webSocketFrame.write((byte) (dataLength >>> 8));
webSocketFrame.write((byte) (dataLength));
}
// Write mask
webSocketFrame.write(maskingKey[0]);
webSocketFrame.write(maskingKey[1]);
webSocketFrame.write(maskingKey[2]);
webSocketFrame.write(maskingKey[3]);
// Write masked data
for (int i = 0; i < dataLength; i++) {
byte nextByte = srcBuffer.get();
nextByte ^= maskingKey[i % 4];
webSocketFrame.write(nextByte);
}
// Copy frame to destination buffer
dstBuffer.clear();
if (dstBuffer.capacity() >= webSocketFrame.size()) {
dstBuffer.put(webSocketFrame.toByteArray());
} else {
throw new OutOfMemoryError("insufficient output buffer size");
}
} else {
dstBuffer.clear();
}
}