Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
  *
  *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
 package org.apache.tomcat.websocket;
 
 import static org.jboss.web.WebsocketsMessages.MESSAGES;
 
 import java.util.List;
 
 
Takes the ServletInputStream, processes the WebSocket frames it contains and extracts the messages. WebSocket Pings received will be responded to automatically without any action required by the application.
 
 public abstract class WsFrameBase {
 
     // Connection level attributes
     protected final WsSession wsSession;
     protected final byte[] inputBuffer;
     private final Transformation transformation;
 
     // Attributes for control messages
     // Control messages can appear in the middle of other messages so need
     // separate attributes
     private final ByteBuffer controlBufferBinary = ByteBuffer.allocate(125);
     private final CharBuffer controlBufferText = CharBuffer.allocate(125);
 
     // Attributes of the current message
     private final CharsetDecoder utf8DecoderControl = new Utf8Decoder().
             onMalformedInput(.).
     private final CharsetDecoder utf8DecoderMessage = new Utf8Decoder().
             onMalformedInput(.).
     private boolean continuationExpected = false;
     private boolean textMessage = false;
     private ByteBuffer messageBufferBinary;
     private CharBuffer messageBufferText;
     // Cache the message handler in force when the message starts so it is used
     // consistently for the entire message
     private MessageHandler binaryMsgHandler = null;
     private MessageHandler textMsgHandler = null;
 
     // Attributes of the current frame
     private boolean fin = false;
     private int rsv = 0;
     private byte opCode = 0;
     private final byte[] mask = new byte[4];
     private int maskIndex = 0;
     private long payloadLength = 0;
     private long payloadWritten = 0;
 
     // Attributes tracking state
     private State state = .;
     private volatile boolean open = true;
     private int readPos = 0;
     protected int writePos = 0;
 
     public WsFrameBase(WsSession wsSessionTransformation transformation) {
          = new byte[.];
          =
                 ByteBuffer.allocate(wsSession.getMaxBinaryMessageBufferSize());
          =
                 CharBuffer.allocate(wsSession.getMaxTextMessageBufferSize());
         this. = wsSession;
         Transformation finalTransformation;
         if (isMasked()) {
             finalTransformation = new UnmaskTransformation();
         } else {
             finalTransformation = new NoopTransformation();
         }
        if (transformation == null) {
            this. = finalTransformation;
        } else {
            transformation.setNext(finalTransformation);
            this. = transformation;
        }
    }
    protected void processInputBuffer() throws IOException {
        while (true) {
            .updateLastActive();
            if ( == .) {
                if (!processInitialHeader()) {
                    break;
                }
                // If a close frame has been received, no further data should
                // have seen
                if (!) {
                    throw new IOException(.receivedFrameAfterClose());
                }
            }
            if ( == .) {
                if (!processRemainingHeader()) {
                    break;
                }
            }
            if ( == .) {
                if (!processData()) {
                    break;
                }
            }
        }
    }


    

Returns:
true if sufficient data was present to process all of the initial header
    private boolean processInitialHeader() throws IOException {
        // Need at least two bytes of data to do this
        if ( -  < 2) {
            return false;
        }
        int b = [++];
         = (b & 0x80) > 0;
         = (b & 0x70) >>> 4;
         = (byte) (b & 0x0F);
        if (!.validateRsv()) {
            throw new WsIOException(new CloseReason(
                    .,
                    .unsupportedReservedBitsSet(Integer.valueOf())));
        }
        if (Util.isControl()) {
            if (!) {
                throw new WsIOException(new CloseReason(
                        .,
                        .invalidFragmentedControlFrame()));
            }
            if ( != . &&
                     != . &&
                     != .) {
                throw new WsIOException(new CloseReason(
                        .,
                        .invalidFrameOpcode()));
            }
        } else {
            if () {
                if (!Util.isContinuation()) {
                    throw new WsIOException(new CloseReason(
                            .,
                            .noContinuationFrame()));
                }
            } else {
                try {
                    if ( == .) {
                        // New binary message
                         = false;
                        int size = .getMaxBinaryMessageBufferSize();
                        if (size != .capacity()) {
                             = ByteBuffer.allocate(size);
                        }
                         = .getBinaryMessageHandler();
                         = null;
                    } else if ( == .) {
                        // New text message
                         = true;
                        int size = .getMaxTextMessageBufferSize();
                        if (size != .capacity()) {
                             = CharBuffer.allocate(size);
                        }
                         = null;
                         = .getTextMessageHandler();
                    } else {
                        throw new WsIOException(new CloseReason(
                                .,
                                .invalidFrameOpcode()));
                    }
                } catch (IllegalStateException ise) {
                    // Thrown if the session is already closed
                    throw new WsIOException(new CloseReason(
                            .,
                            .sessionClosed()));
                }
            }
             = !;
        }
        b = [++];
        // Client data must be masked
        if ((b & 0x80) == 0 && isMasked()) {
            throw new WsIOException(new CloseReason(
                    .,
                    .frameWithoutMask()));
        }
         = b & 0x7F;
         = .;
        return true;
    }
    protected abstract boolean isMasked();


    

Returns:
true if sufficient data was present to complete the processing of the header
    private boolean processRemainingHeader() throws IOException {
        // Ignore the 2 bytes already read. 4 for the mask
        int headerLength;
        if (isMasked()) {
            headerLength = 4;
        } else {
            headerLength = 0;
        }
        // Add additional bytes depending on length
        if ( == 126) {
            headerLength += 2;
        } else if ( == 127) {
            headerLength += 8;
        }
        if ( -  < headerLength) {
            return false;
        }
        // Calculate new payload length if necessary
        if ( == 126) {
             = byteArrayToLong(, 2);
             += 2;
        } else if ( == 127) {
             = byteArrayToLong(, 8);
             += 8;
        }
        if (Util.isControl()) {
            if ( > 125) {
                throw new WsIOException(new CloseReason(
                        .,
                        .controlFramePayloadTooLarge(Long.valueOf())));
            }
            if (!) {
                throw new WsIOException(new CloseReason(
                        .,
                        .controlFrameWithoutFin()));
            }
        }
        if (isMasked()) {
            System.arraycopy(, 0, 4);
             += 4;
        }
         = .;
        return true;
    }
    private boolean processData() throws IOException {
        boolean result;
        if (Util.isControl()) {
            result = processDataControl();
        } else if () {
            if ( == null) {
                result = swallowInput();
            } else {
                result = processDataText();
            }
        } else {
            if ( == null) {
                result = swallowInput();
            } else {
                result = processDataBinary();
            }
        }
        checkRoomPayload();
        return result;
    }
    private boolean processDataControl() throws IOException {
        if (..equals(tr)) {
            return false;
        }
        // Control messages have fixed message size so
        // TransformationResult.OVERFLOW is not possible here
        .flip();
        if ( == .) {
             = false;
            String reason = null;
            int code = ..getCode();
            if (.remaining() == 1) {
                .clear();
                // Payload must be zero or greater than 2
                throw new WsIOException(new CloseReason(
                        .,
                        .invalidOneByteClose()));
            }
            if (.remaining() > 1) {
                code = .getShort();
                if (.remaining() > 0) {
                    CoderResult cr = .decode(
                            true);
                    if (cr.isError()) {
                        .clear();
                        .clear();
                        throw new WsIOException(new CloseReason(
                                .,
                                .invalidUtf8Close()));
                    }
                    // There will be no overflow as the output buffer is big
                    // enough. There will be no underflow as all the data is
                    // passed to the decoder in a single call.
                    .flip();
                    reason = .toString();
                }
            }
            .onClose(new CloseReason(Util.getCloseCode(code), reason));
        } else if ( == .) {
            if (.isOpen()) {
                .getBasicRemote().sendPong();
            }
        } else if ( == .) {
            MessageHandler.Whole<PongMessagemhPong =
                    .getPongMessageHandler();
            if (mhPong != null) {
                try {
                    mhPong.onMessage(new WsPongMessage());
                } catch (Throwable t) {
                    handleThrowableOnSend(t);
                } finally {
                    .clear();
                }
            }
        } else {
            // Should have caught this earlier but just in case...
            .clear();
            throw new WsIOException(new CloseReason(
                    .,
                    .invalidFrameOpcode(Integer.valueOf())));
        }
        .clear();
        newFrame();
        return true;
    }
    @SuppressWarnings("unchecked")
    private void sendMessageText(boolean lastthrows WsIOException {
        if ( instanceof WrappedMessageHandler) {
            long maxMessageSize =
                    ((WrappedMessageHandler).getMaxMessageSize();
            if (maxMessageSize > -1 &&
                    .remaining() > maxMessageSize) {
                throw new WsIOException(new CloseReason(.,
                        .messageTooLarge(Long.valueOf(.remaining()),
                                Long.valueOf(maxMessageSize))));
            }
        }
        try {
            if ( instanceof MessageHandler.Partial<?>) {
                ((MessageHandler.Partial<String>) ).onMessage(
                        .toString(), last);
            } else {
                // Caller ensures last == true if this branch is used
                ((MessageHandler.Whole<String>) ).onMessage(
                        .toString());
            }
        } catch (Throwable t) {
            handleThrowableOnSend(t);
        } finally {
            .clear();
        }
    }
    private boolean processDataText() throws IOException {
        // Copy the available data to the buffer
        while (!..equals(tr)) {
            // Frame not complete - we ran out of something
            // Convert bytes to UTF-8
            .flip();
            while (true) {
                CoderResult cr = .decode(
                        false);
                if (cr.isError()) {
                    throw new WsIOException(new CloseReason(
                            .,
                            .invalidUtf8()));
                } else if (cr.isOverflow()) {
                    // Ran out of space in text buffer - flush it
                    if (usePartial()) {
                        .flip();
                        sendMessageText(false);
                        .clear();
                    } else {
                        throw new WsIOException(new CloseReason(
                                .,
                                .textMessageTooLarge()));
                    }
                } else if (cr.isUnderflow()) {
                    // Compact what we have to create as much space as possible
                    .compact();
                    // Need more input
                    // What did we run out of?
                    if (..equals(tr)) {
                        // Ran out of message buffer - exit inner loop and
                        // refill
                        break;
                    } else {
                        // TransformationResult.UNDERFLOW
                        // Ran out of input data - get some more
                        return false;
                    }
                }
            }
            // Read more input data
            tr = .getMoreData();
        }
        .flip();
        boolean last = false;
        // Frame is fully received
        // Convert bytes to UTF-8
        while (true) {
                    last);
            if (cr.isError()) {
                throw new WsIOException(new CloseReason(
                        .,
                        .invalidUtf8()));
            } else if (cr.isOverflow()) {
                // Ran out of space in text buffer - flush it
                if (usePartial()) {
                    .flip();
                    sendMessageText(false);
                    .clear();
                } else {
                    throw new WsIOException(new CloseReason(
                            .,
                            .textMessageTooLarge()));
                }
            } else if (cr.isUnderflow() & !last) {
                // End of frame and possible message as well.
                if () {
                    // If partial messages are supported, send what we have
                    // managed to decode
                    if (usePartial()) {
                        .flip();
                        sendMessageText(false);
                        .clear();
                    }
                    .compact();
                    newFrame();
                    // Process next frame
                    return true;
                } else {
                    // Make sure coder has flushed all output
                    last = true;
                }
            } else {
                // End of message
                .flip();
                sendMessageText(true);
                newMessage();
                return true;
            }
        }
    }
    private boolean processDataBinary() throws IOException {
        // Copy the available data to the buffer
        while (!..equals(tr)) {
            // Frame not complete - what did we run out of?
            if (..equals(tr)) {
                // Ran out of input data - get some more
                return false;
            }
            // Ran out of message buffer - flush it
            if (!usePartial()) {
                CloseReason cr = new CloseReason(.,
                            .bufferTooSmall(Integer.valueOf(.capacity()),
                                Long.valueOf()));
                throw new WsIOException(cr);
            }
            .flip();
            ByteBuffer copy =
                    ByteBuffer.allocate(.limit());
            copy.put();
            copy.flip();
            sendMessageBinary(copyfalse);
            .clear();
            // Read more data
            tr = .getMoreData();
        }
        // Frame is fully received
        // Send the message if either:
        // - partial messages are supported
        // - the message is complete
        if (usePartial() || !) {
            .flip();
            ByteBuffer copy =
                    ByteBuffer.allocate(.limit());
            copy.put();
            copy.flip();
            sendMessageBinary(copy, !);
            .clear();
        }
        if () {
            // More data for this message expected, start a new frame
            newFrame();
        } else {
            // Message is complete, start a new message
            newMessage();
        }
        return true;
    }
    private void handleThrowableOnSend(Throwable tthrows WsIOException {
        ExceptionUtils.handleThrowable(t);
        .getLocal().onError(t);
                .closeAfterError());
        throw new WsIOException(cr);
    }
    @SuppressWarnings("unchecked")
    private void sendMessageBinary(ByteBuffer msgboolean last)
            throws WsIOException {
        if ( instanceof WrappedMessageHandler) {
            long maxMessageSize =
                    ((WrappedMessageHandler).getMaxMessageSize();
            if (maxMessageSize > -1 && msg.remaining() > maxMessageSize) {
                throw new WsIOException(new CloseReason(.,
                        .messageTooLarge(
                                Long.valueOf(msg.remaining()),
                                Long.valueOf(maxMessageSize))));
            }
        }
        try {
            if ( instanceof MessageHandler.Partial<?>) {
                ((MessageHandler.Partial<ByteBuffer>) ).onMessage(msglast);
            } else {
                // Caller ensures last == true if this branch is used
                ((MessageHandler.Whole<ByteBuffer>) ).onMessage(msg);
            }
        } catch(Throwable t) {
            handleThrowableOnSend(t);
        }
    }
    private void newMessage() {
        .clear();
        .clear();
        .reset();
         = false;
        newFrame();
    }
    private void newFrame() {
        if ( == ) {
             = 0;
             = 0;
        }
         = 0;
         = 0;
         = .;
        // These get reset in processInitialHeader()
        // fin, rsv, opCode, payloadLength, mask
        checkRoomHeaders();
    }
    private void checkRoomHeaders() {
        // Is the start of the current frame too near the end of the input
        // buffer?
        if (. -  < 131) {
            // Limit based on a control frame with a full payload
            makeRoom();
        }
    }
    private void checkRoomPayload() {
        if (. -  -  +  < 0) {
            makeRoom();
        }
    }
    private void makeRoom() {
        System.arraycopy(, 0,
                 - );
         =  - ;
         = 0;
    }
    private boolean usePartial() {
        if (Util.isControl()) {
            return false;
        } else if () {
            return  instanceof MessageHandler.Partial<?>;
        } else {
            // Must be binary
            return  instanceof MessageHandler.Partial<?>;
        }
    }
    private boolean swallowInput() {
        long toSkip = Math.min( -  - );
         += toSkip;
         += toSkip;
        if ( == ) {
            if () {
                newFrame();
            } else {
                newMessage();
            }
            return true;
        } else {
            return false;
        }
    }
    protected static long byteArrayToLong(byte[] bint startint len)
            throws IOException {
        if (len > 8) {
            throw new IOException(.invalidLong(Long.valueOf(len)));
        }
        int shift = 0;
        long result = 0;
        for (int i = start + len - 1; i >= starti--) {
            result = result + ((b[i] & 0xFF) << shift);
            shift += 8;
        }
        return result;
    }
    protected boolean isOpen() {
        return ;
    }
    protected Transformation getTransformation() {
        return ;
    }
    private static enum State {
        NEW_FRAME, PARTIAL_HEADER, DATA
    }
    private abstract class TerminalTransformation implements Transformation {
        @Override
        public boolean validateRsvBits(int i) {
            // Terminal transformations don't use RSV bits and there is no next
            // transformation so always return true.
            return true;
        }
        @Override
        public Extension getExtensionResponse() {
            // Return null since terminal transformations are not extensions
            return null;
        }
        @Override
        public void setNext(Transformation t) {
            // NO-OP since this is the terminal transformation
        }

        

Anything other than a value of zero for rsv is invalid.

        @Override
        public boolean validateRsv(int rsvbyte opCode) {
            return rsv == 0;
        }
    }


    
For use by the client implementation that needs to obtain payload data without the need for unmasking.
    private final class NoopTransformation extends TerminalTransformation {
        @Override
        public TransformationResult getMoreData(byte opCodeboolean finint rsv,
                ByteBuffer dest) {
            // opCode is ignored as the transformation is the same for all
            // opCodes
            // rsv is ignored as it known to be zero at this point
            long toWrite = Math.min(
                     -  - );
            toWrite = Math.min(toWritedest.remaining());
            dest.put(, (inttoWrite);
             += toWrite;
             += toWrite;
            if ( == ) {
                return .;
            } else if ( == ) {
                return .;
            } else {
                // !dest.hasRemaining()
                return .;
            }
        }
        @Override
        public List<MessagePartsendMessagePart(List<MessagePartmessageParts) {
            // TODO Masking should move to this method
            // NO-OP send so simply return the message unchanged.
            return messageParts;
        }
    }


    
For use by the server implementation that needs to obtain payload data and unmask it before any further processing.
    private final class UnmaskTransformation extends TerminalTransformation {
        @Override
        public TransformationResult getMoreData(byte opCodeboolean finint rsv,
                ByteBuffer dest) {
            // opCode is ignored as the transformation is the same for all
            // opCodes
            // rsv is ignored as it known to be zero at this point
            while ( <  &&  <  &&
                    dest.hasRemaining()) {
                byte b = (byte) (([] ^ []) & 0xFF);
                ++;
                if ( == 4) {
                     = 0;
                }
                ++;
                ++;
                dest.put(b);
            }
            if ( == ) {
                return .;
            } else if ( == ) {
                return .;
            } else {
                // !dest.hasRemaining()
                return .;
            }
        }
        @Override
        public List<MessagePartsendMessagePart(List<MessagePartmessageParts) {
            // NO-OP send so simply return the message unchanged.
            return messageParts;
        }
    }
New to GrepCode? Check out our FAQ X