WebSocketReader.java revision a2cab72aa5ff730ba2ae987b45398faafffeb505
1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/* 2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2014 Square, Inc. 3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License. 6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at 7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software 11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and 14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License. 15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage com.squareup.okhttp.internal.ws; 17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.EOFException; 19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException; 20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.net.ProtocolException; 21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSource; 23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Okio; 24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Source; 25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Timeout; 26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 27a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport static com.squareup.okhttp.ws.WebSocket.PayloadType; 28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B0_FLAG_FIN; 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B0_FLAG_RSV1; 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B0_FLAG_RSV2; 31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B0_FLAG_RSV3; 32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B0_MASK_OPCODE; 33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B1_FLAG_MASK; 34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.B1_MASK_LENGTH; 35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_BINARY; 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_CONTINUATION; 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_CONTROL_CLOSE; 38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PING; 39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_CONTROL_PONG; 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_FLAG_CONTROL; 41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.OPCODE_TEXT; 42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.PAYLOAD_LONG; 43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.PAYLOAD_MAX; 44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.PAYLOAD_SHORT; 45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.ws.WebSocketProtocol.toggleMask; 46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static java.lang.Integer.toHexString; 47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/** 49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * An <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a>-compatible WebSocket frame reader. 50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class WebSocketReader { 52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public interface FrameCallback { 53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller void onMessage(BufferedSource source, PayloadType type) throws IOException; 54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller void onPing(Buffer buffer); 55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller void onPong(Buffer buffer); 56a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller void onClose(int code, String reason); 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final boolean isClient; 60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final BufferedSource source; 61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final FrameCallback frameCallback; 62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final Source framedMessageSource = new FramedMessageSource(); 64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean closed; 66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean messageClosed; 67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Stateful data about the current frame. 69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private int opcode; 70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private long frameLength; 71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private long frameBytesRead; 72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean isFinalFrame; 73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean isControlFrame; 74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private boolean isMasked; 75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final byte[] maskKey = new byte[4]; 77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final byte[] maskBuffer = new byte[2048]; 78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public WebSocketReader(boolean isClient, BufferedSource source, FrameCallback frameCallback) { 80a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (source == null) throw new NullPointerException("source == null"); 81a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (frameCallback == null) throw new NullPointerException("frameCallback == null"); 82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller this.isClient = isClient; 83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller this.source = source; 84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller this.frameCallback = frameCallback; 85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Process the next protocol frame. 89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <ul> 90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <li>If it is a control frame this will result in a single call to {@link FrameCallback}.</li> 91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * <li>If it is a message frame this will result in a single call to {@link 92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WebSocketListener#onMessage}. If the message spans multiple frames, each interleaved control 93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * frame will result in a corresponding call to {@link FrameCallback}. 94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * </ul> 95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public void processNextFrame() throws IOException { 97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readHeader(); 98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isControlFrame) { 99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readControlFrame(); 100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else { 101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readMessageFrame(); 102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private void readHeader() throws IOException { 106a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (closed) throw new IOException("closed"); 107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int b0 = source.readByte() & 0xff; 109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller opcode = b0 & B0_MASK_OPCODE; 111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller isFinalFrame = (b0 & B0_FLAG_FIN) != 0; 112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller isControlFrame = (b0 & OPCODE_FLAG_CONTROL) != 0; 113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Control frames must be final frames (cannot contain continuations). 115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isControlFrame && !isFinalFrame) { 116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new ProtocolException("Control frames must be final."); 117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean reservedFlag1 = (b0 & B0_FLAG_RSV1) != 0; 120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean reservedFlag2 = (b0 & B0_FLAG_RSV2) != 0; 121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean reservedFlag3 = (b0 & B0_FLAG_RSV3) != 0; 122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (reservedFlag1 || reservedFlag2 || reservedFlag3) { 123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Reserved flags are for extensions which we currently do not support. 124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new ProtocolException("Reserved flags are unsupported."); 125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int b1 = source.readByte() & 0xff; 128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller isMasked = (b1 & B1_FLAG_MASK) != 0; 130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isMasked == isClient) { 131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Masked payloads must be read on the server. Unmasked payloads must be read on the client. 132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new ProtocolException("Client-sent frames must be masked. Server sent must not."); 133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Get frame length, optionally reading from follow-up bytes if indicated by special values. 136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameLength = b1 & B1_MASK_LENGTH; 137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (frameLength == PAYLOAD_SHORT) { 138a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller frameLength = source.readShort() & 0xffffL; // Value is unsigned. 139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else if (frameLength == PAYLOAD_LONG) { 140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameLength = source.readLong(); 141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (frameLength < 0) { 142a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new ProtocolException( 143a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller "Frame length 0x" + Long.toHexString(frameLength) + " > 0x7FFFFFFFFFFFFFFF"); 144a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameBytesRead = 0; 147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isControlFrame && frameLength > PAYLOAD_MAX) { 149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new ProtocolException("Control frame must be less than " + PAYLOAD_MAX + "B."); 150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isMasked) { 153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Read the masking key as bytes so that they can be used directly for unmasking. 154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.readFully(maskKey); 155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private void readControlFrame() throws IOException { 159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer buffer = null; 160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (frameBytesRead < frameLength) { 161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller buffer = new Buffer(); 162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isClient) { 164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.readFully(buffer, frameLength); 165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else { 166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (frameBytesRead < frameLength) { 167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int toRead = (int) Math.min(frameLength - frameBytesRead, maskBuffer.length); 168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int read = source.read(maskBuffer, 0, toRead); 169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (read == -1) throw new EOFException(); 170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller toggleMask(maskBuffer, read, maskKey, frameBytesRead); 171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller buffer.write(maskBuffer, 0, read); 172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameBytesRead += read; 173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller switch (opcode) { 178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller case OPCODE_CONTROL_PING: 179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameCallback.onPing(buffer); 180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller case OPCODE_CONTROL_PONG: 182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameCallback.onPong(buffer); 183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller case OPCODE_CONTROL_CLOSE: 185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int code = 0; 186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller String reason = ""; 187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (buffer != null) { 188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller code = buffer.readShort(); 189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller reason = buffer.readUtf8(); 190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameCallback.onClose(code, reason); 192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller closed = true; 193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller default: 195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalStateException("Unknown control opcode: " + toHexString(opcode)); 196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private void readMessageFrame() throws IOException { 200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller PayloadType type; 201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller switch (opcode) { 202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller case OPCODE_TEXT: 203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller type = PayloadType.TEXT; 204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller case OPCODE_BINARY: 206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller type = PayloadType.BINARY; 207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller default: 209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalStateException("Unknown opcode: " + toHexString(opcode)); 210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller messageClosed = false; 213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameCallback.onMessage(Okio.buffer(framedMessageSource), type); 214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!messageClosed) { 215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new IllegalStateException("Listener failed to call close on message payload."); 216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** Read headers and process any control frames until we reach a non-control frame. */ 220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private void readUntilNonControlFrame() throws IOException { 221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (!closed) { 222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readHeader(); 223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (!isControlFrame) { 224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller break; 225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readControlFrame(); 227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller /** 231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * A special source which knows how to read a message body across one or more frames. Control 232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * frames that occur between fragments will be processed. If the message payload is masked this 233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * will unmask as it's being processed. 234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final class FramedMessageSource implements Source { 236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public long read(Buffer sink, long byteCount) throws IOException { 237a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (closed) throw new IOException("closed"); 238a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (messageClosed) throw new IllegalStateException("closed"); 239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (frameBytesRead == frameLength) { 241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isFinalFrame) return -1; // We are exhausted and have no continuations. 242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readUntilNonControlFrame(); 244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (opcode != OPCODE_CONTINUATION) { 245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throw new ProtocolException("Expected continuation opcode. Got: " + toHexString(opcode)); 246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isFinalFrame && frameLength == 0) { 248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return -1; // Fast-path for empty final frame. 249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long toRead = Math.min(byteCount, frameLength - frameBytesRead); 253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long read; 255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (isMasked) { 256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller toRead = Math.min(toRead, maskBuffer.length); 257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller read = source.read(maskBuffer, 0, (int) toRead); 258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (read == -1) throw new EOFException(); 259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller toggleMask(maskBuffer, read, maskKey, frameBytesRead); 260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink.write(maskBuffer, 0, (int) read); 261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } else { 262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller read = source.read(sink, toRead); 263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (read == -1) throw new EOFException(); 264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller frameBytesRead += read; 267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return read; 268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public Timeout timeout() { 271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return source.timeout(); 272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void close() throws IOException { 275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (messageClosed) return; 276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller messageClosed = true; 277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller if (closed) return; 278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Exhaust the remainder of the message, if any. 280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.skip(frameLength - frameBytesRead); 281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (!isFinalFrame) { 282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller readUntilNonControlFrame(); 283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.skip(frameLength); 284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller} 288