/*
 * 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.catalina.websocket;

import java.io.IOException;

import org.apache.catalina.util.Conversions;
import org.apache.coyote.http11.upgrade.UpgradeProcessor;

public class WsInputStream extends java.io.InputStream {

    private UpgradeProcessor<?> processor;
    private final WsFrameHeader wsFrameHeader;
    private long payloadLength = -1;
    private byte[] mask = new byte[4];


    private long remaining;
    private long read;

    public WsInputStream(UpgradeProcessor<?> processor) throws IOException {
        this.processor = processor;

        int i = processor.read();
        this.wsFrameHeader = new WsFrameHeader(i);

        // Client data must be masked
        i = processor.read();
        if ((i & 0x80) == 0) {
            // TODO: StringManager / i18n
            throw new IOException("Client frame not masked");
        }

        payloadLength = i & 0x7F;
        if (payloadLength == 126) {
            byte[] extended = new byte[2];
            processor.read(extended);
            payloadLength = Conversions.byteArrayToLong(extended);
        } else if (payloadLength == 127) {
            byte[] extended = new byte[8];
            processor.read(extended);
            payloadLength = Conversions.byteArrayToLong(extended);
        }
        remaining = payloadLength;

        processor.read(mask);


        // TODO: Doesn't currently handle multi-frame messages. That will need
        //       some refactoring.

        // TODO: Per frame extension handling is not currently supported.

        // TODO: Handle other control frames.

        // TODO: Handle control frames appearing in the middle of a multi-frame
        //       message
    }

    public WsFrameHeader getFrameHeader() {
        return wsFrameHeader;
    }


    // ----------------------------------------------------- InputStream methods

    @Override
    public int read() throws IOException {
        if (remaining == 0) {
            return -1;
        }

        remaining--;
        read++;

        int masked = processor.read();
        return masked ^ mask[(int) ((read - 1) % 4)];
    }
}

@@ -1086,14 +1086,8 @@
 ate 
-final 
 WsFr
@@ -1355,16 +1355,342 @@
 essor;%0A%0A
+        processFrameHeader();%0A    %7D%0A%0A%0A    private void processFrameHeader() throws IOException %7B%0A%0A        // TODO: Per frame extension handling is not currently supported.%0A%0A        // TODO: Handle other control frames.%0A%0A        // TODO: Handle control frames appearing in the middle of a multi-frame%0A        //       message%0A%0A
         
@@ -2470,371 +2470,368 @@
 k);%0A
+    %7D
 %0A%0A    
-    // TODO: Doesn't currently handle multi-frame messages. That will need%0A        //       some refactoring.%0A
+public WsFrameHeader getFrameHeader() %7B%0A        return wsFrameHeader;
 %0A    
+%7D%0A%0A%0A
     // 
-TODO: Per frame extension handling is not currently supported.%0A%0A        // TODO: Handle other control frames.%0A%0A        // TODO: Handle control frames appearing in the middle of a multi-frame%0A        //       messag
+----------------------------------------------------- InputStream methods%0A%0A    @Override%0A    public int read() throws IOException %7B%0A        while (remaining == 0 && !getFrameHeader().getFin()) %7B%0A            // Need more data - process next fram
 e%0A    
-%7D%0A%0A
+    
     p
-ublic W
+roces
 sFra
@@ -2842,190 +2842,148 @@
 ader
- getFrameHeader() %7B%0A        return wsFrameHeader;%0A    %7D%0A%0A%0A    // ----------------------------------------------------- InputStream methods%0A%0A    @Override%0A    public int read()
+();%0A%0A            if (getFrameHeader().getOpCode() != Constants.OPCODE_CONTINUATION) %7B%0A                // TODO i18n%0A               
  throw
-s
+ new
  IOE
@@ -2982,34 +2982,86 @@
  new IOException
- %7B
+(%22Not a continuation frame%22);%0A            %7D%0A        %7D%0A
 %0A        if (rem

Implement fragmentation support. Unfragmented messages still work correctly. Still trying to figure out how to test fragmented messages.