Multiple HTTP2 PDUs
authorJeff MAURY <jeffmaury@apache.org>
Mon, 23 Mar 2015 21:43:42 +0000 (22:43 +0100)
committerJeff MAURY <jeffmaury@apache.org>
Mon, 23 Mar 2015 21:43:42 +0000 (22:43 +0100)
29 files changed:
http2/src/main/java/org/apache/mina/http2/api/Http2Constants.java
http2/src/main/java/org/apache/mina/http2/api/Http2ContinuationFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2DataFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2Frame.java
http2/src/main/java/org/apache/mina/http2/api/Http2FrameHeadePartialDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2GoAwayFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2HeadersFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2PingFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2PriorityFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2PushPromiseFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2RstStreamFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2SettingsFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2UnknownFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/api/Http2WindowUpdateFrame.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2Connection.java
http2/src/main/java/org/apache/mina/http2/impl/Http2ContinuationFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2DataFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2FrameDecoder.java
http2/src/main/java/org/apache/mina/http2/impl/Http2GoAwayFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2HeadersFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2PingFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2PriorityFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2PushPromiseFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2RstStreamFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2SettingsFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2UnknownFrameDecoder.java [new file with mode: 0644]
http2/src/main/java/org/apache/mina/http2/impl/Http2WindowUpdateFrameDecoder.java [new file with mode: 0644]
http2/src/test/java/org/apache/mina/http2/api/Htp2PushPromiseFrameDecoderTest.java
http2/src/test/java/org/apache/mina/http2/api/Http2FrameHeaderPartialDecoderTest.java

index ba8a8fe..3bf58f6 100644 (file)
@@ -190,4 +190,6 @@ public final class Http2Constants {
     
     public static final Charset US_ASCII_CHARSET = Charset.forName("US-ASCII");
     
+    public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+    
 }
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2ContinuationFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2ContinuationFrame.java
new file mode 100644 (file)
index 0000000..0138d17
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2ContinuationFrame extends Http2Frame {
+
+    private final byte[] headerBlockFragment;
+    
+    
+    public byte[] getHeaderBlockFragment() {
+        return headerBlockFragment;
+    }
+
+    protected <T extends AbstractHttp2ContinuationFrameBuilder<T,V>, V extends Http2ContinuationFrame> Http2ContinuationFrame(AbstractHttp2ContinuationFrameBuilder<T, V> builder) {
+        super(builder);
+        this.headerBlockFragment = builder.getHeaderBlockFragment();
+    }
+
+    
+    public static abstract class AbstractHttp2ContinuationFrameBuilder<T extends AbstractHttp2ContinuationFrameBuilder<T,V>, V extends Http2ContinuationFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] headerBlockFragment = new byte[0];
+        
+        @SuppressWarnings("unchecked")
+        public T headerBlockFragment(byte[] headerBlockFragment) {
+            this.headerBlockFragment = headerBlockFragment;
+            return (T) this;
+        }
+        
+        public byte[] getHeaderBlockFragment() {
+            return headerBlockFragment;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2ContinuationFrameBuilder<Builder, Http2ContinuationFrame> {
+
+        @Override
+        public Http2ContinuationFrame build() {
+            return new Http2ContinuationFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2DataFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2DataFrame.java
new file mode 100644 (file)
index 0000000..2a4802d
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ *  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.mina.http2.api;
+
+import static org.apache.mina.http2.api.Http2Constants.EMPTY_BYTE_ARRAY;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2DataFrame extends Http2Frame {
+    private final byte[] data;
+    
+    private final byte[] padding;
+    
+    public byte[] getData() {
+        return data;
+    }
+    
+    public byte[] getPadding() {
+        return padding;
+    }
+    
+    protected <T extends AbstractHttp2DataFrameBuilder<T,V>, V extends Http2Frame> Http2DataFrame(AbstractHttp2DataFrameBuilder<T, V> builder) {
+        super(builder);
+        this.data = builder.getData();
+        this.padding = builder.getPadding();
+    }
+
+    
+    public static abstract class AbstractHttp2DataFrameBuilder<T extends AbstractHttp2DataFrameBuilder<T,V>, V extends Http2Frame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] data = EMPTY_BYTE_ARRAY;
+        
+        private byte[] padding = EMPTY_BYTE_ARRAY;
+
+        @SuppressWarnings("unchecked")
+        public T data(byte[] data) {
+            this.data = data;
+            return (T) this;
+        }
+        
+        public byte[] getData() {
+            return data;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T padding(byte[] padding) {
+            this.padding = padding;
+            return (T) this;
+        }
+        
+        public byte[] getPadding() {
+            return padding;
+        }
+}
+    
+    public static class Builder extends AbstractHttp2DataFrameBuilder<Builder, Http2Frame> {
+
+        @Override
+        public Http2Frame build() {
+            return new Http2DataFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
index b6a3ed2..356c2b8 100644 (file)
 package org.apache.mina.http2.api;
 
 /**
- * An SPY frame
+ * An SPY data frame
  * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a>
  * 
  */
-public class Http2Frame {
-    private int length;
+public abstract class Http2Frame {
+
+    private final int length;
     
-    private short type;
+    private final short type;
     
-    private short flags;
+    private final short flags;
     
-    private int streamID;
+    private final int streamID;
     
-    public byte[] payload;
-
-    /**
-     * @return the length
-     */
     public int getLength() {
         return length;
     }
 
-    /**
-     * @param length the length to set
-     */
-    public void setLength(int length) {
-        this.length = length;
-    }
-
-    /**
-     * @return the type
-     */
     public short getType() {
         return type;
     }
-
-    /**
-     * @param type the type to set
-     */
-    public void setType(short type) {
-        this.type = type;
-    }
-
-    /**
-     * @return the flags
-     */
+    
     public short getFlags() {
         return flags;
     }
 
-    /**
-     * @param flags the flags to set
-     */
-    public void setFlags(short flags) {
-        this.flags = flags;
-    }
-
-    /**
-     * @return the streamID
-     */
     public int getStreamID() {
         return streamID;
     }
 
-    /**
-     * @param streamID the streamID to set
-     */
-    public void setStreamID(int streamID) {
-        this.streamID = streamID;
+    protected <T extends AbstractHttp2FrameBuilder<T,V>, V extends Http2Frame> Http2Frame(AbstractHttp2FrameBuilder<T, V> builder) {
+        this.length = builder.getLength();
+        this.type = builder.getType();
+        this.flags = builder.getFlags();
+        this.streamID = builder.getStreamID();
     }
 
-    /**
-     * @return the payload
-     */
-    public byte[] getPayload() {
-        return payload;
-    }
+    public static abstract class AbstractHttp2FrameBuilder<T extends AbstractHttp2FrameBuilder<T,V>, V extends Http2Frame>  {
+        private int length;
+        
+        private short type;
+        
+        private short flags;
+        
+        private int streamID;
+        
+        @SuppressWarnings("unchecked")
+        public T length(int length) {
+            this.length = length;
+            return (T) this;
+        }
+        
+        public int getLength() {
+            return length;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T type(short type) {
+            this.type = type;
+            return (T) this;
+        }
+        
+        public short getType() {
+            return type;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T flags(short flags) {
+            this.flags = flags;
+            return (T) this;
+        }
+        
+        public short getFlags() {
+            return flags;
+        }
 
-    /**
-     * @param payload the payload to set
-     */
-    public void setPayload(byte[] payload) {
-        this.payload = payload;
+        @SuppressWarnings("unchecked")
+        public T streamID(int streamID) {
+            this.streamID = streamID;
+            return (T) this;
+        }
+        
+        public int getStreamID() {
+            return streamID;
+        }
+        
+        public abstract V build();
     }
 }
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2FrameHeadePartialDecoder.java b/http2/src/main/java/org/apache/mina/http2/api/Http2FrameHeadePartialDecoder.java
new file mode 100644 (file)
index 0000000..059f193
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.api;
+
+import java.nio.ByteBuffer;
+
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2FrameHeadePartialDecoder implements PartialDecoder<Http2FrameHeadePartialDecoder.Http2FrameHeader> {
+    public static class Http2FrameHeader {
+        private int length;
+        private short type;
+        private byte flags;
+        private int streamID;
+
+        public int getLength() {
+            return length;
+        }
+       
+        public void setLength(int length) {
+            this.length = length;
+        }
+        
+        public short getType() {
+            return type;
+        }
+        
+        public void setType(short type) {
+            this.type = type;
+        }
+        
+        public byte getFlags() {
+            return flags;
+        }
+        
+        public void setFlags(byte flags) {
+            this.flags = flags;
+        }
+        
+        public int getStreamID() {
+            return streamID;
+        }
+        
+        public void setStreamID(int streamID) {
+            this.streamID = streamID;
+        }
+    }
+    
+    private static enum State {
+        LENGTH,
+        TYPE_FLAGS,
+        STREAMID,
+        END
+    }
+    
+    private State state;
+    private PartialDecoder<?> decoder;
+    private Http2FrameHeader value;
+    
+    public Http2FrameHeadePartialDecoder() {
+        reset();
+    }
+    
+    public boolean consume(ByteBuffer buffer) {
+        while (buffer.hasRemaining() && state != State.END) {
+            if (decoder.consume(buffer)) {
+                switch (state) {
+                case LENGTH:
+                    value.setLength(((IntPartialDecoder)decoder).getValue().intValue());
+                    decoder = new BytePartialDecoder(2);
+                    state = State.TYPE_FLAGS;
+                    break;
+                case TYPE_FLAGS:
+                    value.setType(((BytePartialDecoder)decoder).getValue()[0]);
+                    value.setFlags(((BytePartialDecoder)decoder).getValue()[1]);
+                    decoder = new IntPartialDecoder(4);
+                    state = State.STREAMID;
+                    break;
+                case STREAMID:
+                    value.setStreamID(((IntPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK);
+                    state = State.END;
+                    break;
+                }
+            }
+        }
+        return state == State.END;
+    }
+    
+    public Http2FrameHeader getValue() {
+        return value;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+        state = State.LENGTH;
+        decoder = new IntPartialDecoder(3);
+        value = new Http2FrameHeader();
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2GoAwayFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2GoAwayFrame.java
new file mode 100644 (file)
index 0000000..fbc5ea1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2GoAwayFrame extends Http2Frame {
+    private final int lastStreamID;
+    
+    private final int errorCode;
+
+    private byte[] data;
+    
+    public int getLastStreamID() {
+        return lastStreamID;
+    }
+    
+    public int getErrorCode() {
+        return errorCode;
+    }
+
+    public byte[] getData() {
+        return data;
+    }
+    
+    protected <T extends AbstractHttp2GoAwayFrameBuilder<T,V>, V extends Http2GoAwayFrame> Http2GoAwayFrame(AbstractHttp2GoAwayFrameBuilder<T, V> builder) {
+        super(builder);
+        this.lastStreamID = builder.getLastStreamID();
+        this.errorCode = builder.getErrorCode();
+        this.data = builder.getData();
+    }
+
+    
+    public static abstract class AbstractHttp2GoAwayFrameBuilder<T extends AbstractHttp2GoAwayFrameBuilder<T,V>, V extends Http2GoAwayFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private int lastStreamID;
+        
+        private int errorCode;
+        
+        private byte[] data;
+        
+        @SuppressWarnings("unchecked")
+        public T lastStreamID(int lastStreamID) {
+            this.lastStreamID = lastStreamID;
+            return (T) this;
+        }
+        
+        public int getLastStreamID() {
+            return lastStreamID;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T errorCode(int errorCode) {
+            this.errorCode = errorCode;
+            return (T) this;
+        }
+        
+        public int getErrorCode() {
+            return errorCode;
+        }
+        
+        @SuppressWarnings("unchecked")
+        public T data(byte[] data) {
+            this.data = data;
+            return (T) this;
+        }
+        
+        public byte[] getData() {
+            return data;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2GoAwayFrameBuilder<Builder, Http2GoAwayFrame> {
+
+        @Override
+        public Http2GoAwayFrame build() {
+            return new Http2GoAwayFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2HeadersFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2HeadersFrame.java
new file mode 100644 (file)
index 0000000..aadb1bc
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2HeadersFrame extends Http2Frame {
+
+    private final byte[] padding;
+    
+    private final int streamDependencyID;
+    
+    private final boolean exclusiveMode;
+    
+    private final byte weight;
+    
+    private final byte[] headerBlockFragment;
+    
+    
+    public byte[] getPadding() {
+        return padding;
+    }
+
+    public int getStreamDependencyID() {
+        return streamDependencyID;
+    }
+    
+    public boolean getExclusiveMode() {
+        return exclusiveMode;
+    }
+
+    public byte getWeight() {
+        return weight;
+    }
+
+    public byte[] getHeaderBlockFragment() {
+        return headerBlockFragment;
+    }
+
+    protected <T extends AbstractHttp2HeadersFrameBuilder<T,V>, V extends Http2HeadersFrame> Http2HeadersFrame(AbstractHttp2HeadersFrameBuilder<T, V> builder) {
+        super(builder);
+        this.padding = builder.getPadding();
+        this.streamDependencyID = builder.getStreamDependencyID();
+        this.exclusiveMode = builder.getExclusiveMode();
+        this.weight = builder.getWeight();
+        this.headerBlockFragment = builder.getHeaderBlockFragment();
+    }
+
+    
+    public static abstract class AbstractHttp2HeadersFrameBuilder<T extends AbstractHttp2HeadersFrameBuilder<T,V>, V extends Http2HeadersFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] padding;
+        
+        private int streamDependencyID;
+        
+        private byte weight;
+        
+        private byte[] headerBlockFragment;
+        
+        private boolean exclusiveMode;
+        
+        @SuppressWarnings("unchecked")
+        public T padding(byte[] padding) {
+            this.padding = padding;
+            return (T) this;
+        }
+        
+        public byte[] getPadding() {
+            return padding;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T streamDependencyID(int streamDependencyID) {
+            this.streamDependencyID = streamDependencyID;
+            return (T) this;
+        }
+        
+        public int getStreamDependencyID() {
+            return streamDependencyID;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T exclusiveMode(boolean exclusiveMode) {
+            this.exclusiveMode = exclusiveMode;
+            return (T) this;
+        }
+        
+        public boolean getExclusiveMode() {
+            return exclusiveMode;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T weight(byte weight) {
+            this.weight = weight;
+            return (T) this;
+        }
+        
+        public byte getWeight() {
+            return weight;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T headerBlockFragment(byte[] headerBlockFragment) {
+            this.headerBlockFragment = headerBlockFragment;
+            return (T) this;
+        }
+        
+        public byte[] getHeaderBlockFragment() {
+            return headerBlockFragment;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2HeadersFrameBuilder<Builder, Http2HeadersFrame> {
+
+        @Override
+        public Http2HeadersFrame build() {
+            return new Http2HeadersFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2PingFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2PingFrame.java
new file mode 100644 (file)
index 0000000..c1df709
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2PingFrame extends Http2Frame {
+    private final byte[] data;
+    
+    public byte[] getData() {
+        return data;
+    }
+
+    protected <T extends AbstractHttp2PingFrameBuilder<T,V>, V extends Http2PingFrame> Http2PingFrame(AbstractHttp2PingFrameBuilder<T, V> builder) {
+        super(builder);
+        this.data = builder.getData();
+    }
+
+    
+    public static abstract class AbstractHttp2PingFrameBuilder<T extends AbstractHttp2PingFrameBuilder<T,V>, V extends Http2PingFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] data;
+        
+        @SuppressWarnings("unchecked")
+        public T data(byte[] data) {
+            this.data = data;
+            return (T) this;
+        }
+        
+        public byte[] getData() {
+            return data;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2PingFrameBuilder<Builder, Http2PingFrame> {
+
+        @Override
+        public Http2PingFrame build() {
+            return new Http2PingFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2PriorityFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2PriorityFrame.java
new file mode 100644 (file)
index 0000000..5478c69
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2PriorityFrame extends Http2Frame {
+    private final int streamDependencyID;
+    
+    private boolean exclusiveMode;
+    
+    private final short weight;
+    
+    public int getStreamDependencyID() {
+        return streamDependencyID;
+    }
+    
+    public boolean getExclusiveMode() {
+        return exclusiveMode;
+    }
+
+    public short getWeight() {
+        return weight;
+    }
+
+    protected <T extends AbstractHttp2PriorityFrameBuilder<T,V>, V extends Http2PriorityFrame> Http2PriorityFrame(AbstractHttp2PriorityFrameBuilder<T, V> builder) {
+        super(builder);
+        this.streamDependencyID = builder.getStreamDependencyID();
+        this.exclusiveMode = builder.exclusiveMode;
+        this.weight = builder.getWeight();
+    }
+
+    
+    public static abstract class AbstractHttp2PriorityFrameBuilder<T extends AbstractHttp2PriorityFrameBuilder<T,V>, V extends Http2PriorityFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private int streamDependencyID;
+        
+        private boolean exclusiveMode;
+        
+        private short weight;
+        
+        @SuppressWarnings("unchecked")
+        public T streamDependencyID(int streamDependencyID) {
+            this.streamDependencyID = streamDependencyID;
+            return (T) this;
+        }
+        
+        public int getStreamDependencyID() {
+            return streamDependencyID;
+        }
+        
+        @SuppressWarnings("unchecked")
+        public T exclusiveMode(boolean exclusiveMode) {
+            this.exclusiveMode = exclusiveMode;
+            return (T) this;
+        }
+        
+        public boolean getExclusiveMode() {
+            return exclusiveMode;
+        }
+
+
+        @SuppressWarnings("unchecked")
+        public T weight(short weight) {
+            this.weight = weight;
+            return (T) this;
+        }
+        
+        public short getWeight() {
+            return weight;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2PriorityFrameBuilder<Builder, Http2PriorityFrame> {
+
+        @Override
+        public Http2PriorityFrame build() {
+            return new Http2PriorityFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2PushPromiseFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2PushPromiseFrame.java
new file mode 100644 (file)
index 0000000..a2ab2c7
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  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.mina.http2.api;
+
+import static org.apache.mina.http2.api.Http2Constants.EMPTY_BYTE_ARRAY;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2PushPromiseFrame extends Http2Frame {
+
+    private final byte[] padding;
+    
+    private final int promisedStreamID;
+    
+    private final byte[] headerBlockFragment;
+    
+    
+    public byte[] getPadding() {
+        return padding;
+    }
+
+    public int getPromisedStreamID() {
+        return promisedStreamID;
+    }
+
+    public byte[] getHeaderBlockFragment() {
+        return headerBlockFragment;
+    }
+
+    protected <T extends AbstractHttp2PushPromiseFrameBuilder<T,V>, V extends Http2PushPromiseFrame> Http2PushPromiseFrame(AbstractHttp2PushPromiseFrameBuilder<T, V> builder) {
+        super(builder);
+        this.padding = builder.getPadding();
+        this.promisedStreamID = builder.getPromisedStreamID();
+        this.headerBlockFragment = builder.getHeaderBlockFragment();
+    }
+
+    public static abstract class AbstractHttp2PushPromiseFrameBuilder<T extends AbstractHttp2PushPromiseFrameBuilder<T,V>, V extends Http2PushPromiseFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] padding = EMPTY_BYTE_ARRAY;
+        
+        private int promisedStreamID;
+        
+        private byte[] headerBlockFragment = EMPTY_BYTE_ARRAY;
+        
+        @SuppressWarnings("unchecked")
+        public T padding(byte[] padding) {
+            this.padding = padding;
+            return (T) this;
+        }
+        
+        public byte[] getPadding() {
+            return padding;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T promisedStreamID(int promisedStreamID) {
+            this.promisedStreamID = promisedStreamID;
+            return (T) this;
+        }
+        
+        public int getPromisedStreamID() {
+            return promisedStreamID;
+        }
+
+        @SuppressWarnings("unchecked")
+        public T headerBlockFragment(byte[] headerBlockFragment) {
+            this.headerBlockFragment = headerBlockFragment;
+            return (T) this;
+        }
+        
+        public byte[] getHeaderBlockFragment() {
+            return headerBlockFragment;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2PushPromiseFrameBuilder<Builder, Http2PushPromiseFrame> {
+
+        @Override
+        public Http2PushPromiseFrame build() {
+            return new Http2PushPromiseFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2RstStreamFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2RstStreamFrame.java
new file mode 100644 (file)
index 0000000..1536594
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2RstStreamFrame extends Http2Frame {
+    private final long errorCode;
+    
+    public long getErrorCode() {
+        return errorCode;
+    }
+
+    protected <T extends AbstractHttp2RstStreamFrameBuilder<T,V>, V extends Http2RstStreamFrame> Http2RstStreamFrame(AbstractHttp2RstStreamFrameBuilder<T, V> builder) {
+        super(builder);
+        this.errorCode = builder.getErrorCode();
+    }
+
+    
+    public static abstract class AbstractHttp2RstStreamFrameBuilder<T extends AbstractHttp2RstStreamFrameBuilder<T,V>, V extends Http2RstStreamFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private long errorCode;
+        
+        @SuppressWarnings("unchecked")
+        public T errorCode(long errorCode) {
+            this.errorCode = errorCode;
+            return (T) this;
+        }
+        
+        public long getErrorCode() {
+            return errorCode;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2RstStreamFrameBuilder<Builder, Http2RstStreamFrame> {
+
+        @Override
+        public Http2RstStreamFrame build() {
+            return new Http2RstStreamFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2SettingsFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2SettingsFrame.java
new file mode 100644 (file)
index 0000000..66c44ba
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  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.mina.http2.api;
+
+import java.util.Collection;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2SettingsFrame extends Http2Frame {
+    private final Collection<Http2Setting> settings;
+    
+    public Collection<Http2Setting> getSettings() {
+        return settings;
+    }
+
+    protected <T extends AbstractHttp2SettingsFrameBuilder<T,V>, V extends Http2SettingsFrame> Http2SettingsFrame(AbstractHttp2SettingsFrameBuilder<T, V> builder) {
+        super(builder);
+        this.settings = builder.getSettings();
+    }
+
+    
+    public static abstract class AbstractHttp2SettingsFrameBuilder<T extends AbstractHttp2SettingsFrameBuilder<T,V>, V extends Http2SettingsFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private Collection<Http2Setting> settings;
+        
+        @SuppressWarnings("unchecked")
+        public T settings(Collection<Http2Setting> settings) {
+            this.settings = settings;
+            return (T) this;
+        }
+        
+        public Collection<Http2Setting> getSettings() {
+            return settings;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2SettingsFrameBuilder<Builder, Http2SettingsFrame> {
+
+        @Override
+        public Http2SettingsFrame build() {
+            return new Http2SettingsFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2UnknownFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2UnknownFrame.java
new file mode 100644 (file)
index 0000000..1cdcaef
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2UnknownFrame extends Http2Frame {
+    private final byte[] payload;
+    
+    public byte[] getPayload() {
+        return payload;
+    }
+
+    protected <T extends AbstractHttp2UnknownFrameBuilder<T,V>, V extends Http2UnknownFrame> Http2UnknownFrame(AbstractHttp2UnknownFrameBuilder<T, V> builder) {
+        super(builder);
+        this.payload = builder.getPayload();
+    }
+
+    
+    public static abstract class AbstractHttp2UnknownFrameBuilder<T extends AbstractHttp2UnknownFrameBuilder<T,V>, V extends Http2UnknownFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private byte[] payload = new byte[0];
+        
+        @SuppressWarnings("unchecked")
+        public T payload(byte[] payload) {
+            this.payload = payload;
+            return (T) this;
+        }
+        
+        public byte[] getPayload() {
+            return payload;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2UnknownFrameBuilder<Builder, Http2UnknownFrame> {
+
+        @Override
+        public Http2UnknownFrame build() {
+            return new Http2UnknownFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/api/Http2WindowUpdateFrame.java b/http2/src/main/java/org/apache/mina/http2/api/Http2WindowUpdateFrame.java
new file mode 100644 (file)
index 0000000..f552aaf
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  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.mina.http2.api;
+
+/**
+ * An SPY data frame
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * 
+ */
+public class Http2WindowUpdateFrame extends Http2Frame {
+    private final int windowUpdateIncrement;
+    
+    public int getWindowUpdateIncrement() {
+        return windowUpdateIncrement;
+    }
+
+    protected <T extends AbstractHttp2WindowUpdateFrameBuilder<T,V>, V extends Http2WindowUpdateFrame> Http2WindowUpdateFrame(AbstractHttp2WindowUpdateFrameBuilder<T, V> builder) {
+        super(builder);
+        this.windowUpdateIncrement = builder.getWindowUpdateIncrement();
+    }
+
+    
+    public static abstract class AbstractHttp2WindowUpdateFrameBuilder<T extends AbstractHttp2WindowUpdateFrameBuilder<T,V>, V extends Http2WindowUpdateFrame> extends AbstractHttp2FrameBuilder<T,V> {
+        private int windowUpdateIncrement;
+        
+        @SuppressWarnings("unchecked")
+        public T windowUpdateIncrement(int windowUpdateIncrement) {
+            this.windowUpdateIncrement = windowUpdateIncrement;
+            return (T) this;
+        }
+        
+        public int getWindowUpdateIncrement() {
+            return windowUpdateIncrement;
+        }
+    }
+    
+    public static class Builder extends AbstractHttp2WindowUpdateFrameBuilder<Builder, Http2WindowUpdateFrame> {
+
+        @Override
+        public Http2WindowUpdateFrame build() {
+            return new Http2WindowUpdateFrame(this);
+        }
+        
+        public static Builder builder() {
+            return new Builder();
+        }
+    }
+}
index 3019b20..9ec5b4f 100644 (file)
@@ -3,10 +3,29 @@ package org.apache.mina.http2.impl;
 import java.nio.ByteBuffer;
 
 import org.apache.mina.http2.api.Http2Frame;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_DATA;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_HEADERS;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_PRIORITY;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_RST_STREAM;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_SETTINGS;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_PUSH_PROMISE;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_PING;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_GOAWAY;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_WINDOW_UPDATE;
+import static org.apache.mina.http2.api.Http2Constants.FRAME_TYPE_CONTINUATION;
 
 public class Http2Connection {
 
-    private final Http2FrameDecoder decoder = new Http2FrameDecoder();
+    private static enum DecoderState {
+        HEADER,
+        FRAME
+    }
+    
+    private final Http2FrameHeadePartialDecoder headerDecoder = new Http2FrameHeadePartialDecoder();
+    private Http2FrameDecoder frameDecoder;
+    private DecoderState decoderState = DecoderState.HEADER;
 
     /**
      * Decode the incoming message and if all frame data has been received,
@@ -17,9 +36,59 @@ public class Http2Connection {
      */
     public Http2Frame decode(ByteBuffer input) {
         Http2Frame frame = null;
-        if (decoder.consume(input)) {
-            frame = decoder.getValue();
-            decoder.reset();
+        switch (decoderState) {
+        case HEADER:
+            if (headerDecoder.consume(input)) {
+                Http2FrameHeader header = headerDecoder.getValue();
+                headerDecoder.reset();
+                decoderState = DecoderState.FRAME;
+                switch (header.getType()) {
+                case FRAME_TYPE_DATA:
+                    frameDecoder = new Http2DataFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_HEADERS:
+                    frameDecoder = new Http2HeadersFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_PRIORITY:
+                    frameDecoder = new Http2PriorityFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_RST_STREAM:
+                    frameDecoder = new Http2RstStreamFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_SETTINGS:
+                    frameDecoder =new Http2SettingsFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_PUSH_PROMISE:
+                    frameDecoder = new Http2PushPromiseFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_PING:
+                    frameDecoder = new Http2PingFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_GOAWAY:
+                    frameDecoder = new Http2GoAwayFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_WINDOW_UPDATE:
+                    frameDecoder = new Http2WindowUpdateFrameDecoder(header);
+                    break;
+                case FRAME_TYPE_CONTINUATION:
+                    frameDecoder = new Http2ContinuationFrameDecoder(header);
+                    break;
+                default:
+                    frameDecoder = new Http2UnknownFrameDecoder(header);
+                    break;
+                }
+                if (frameDecoder.consume(input)) {
+                    frame = frameDecoder.getValue();
+                    decoderState = DecoderState.HEADER;
+                }
+            }
+            break;
+        case FRAME:
+            if (frameDecoder.consume(input)) {
+                frame = frameDecoder.getValue();
+                decoderState = DecoderState.HEADER;
+            }
+            break;
         }
         return frame;
     }
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2ContinuationFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2ContinuationFrameDecoder.java
new file mode 100644 (file)
index 0000000..39f6706
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2ContinuationFrame.Builder;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2ContinuationFrameDecoder extends Http2FrameDecoder {
+
+    private BytePartialDecoder decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2ContinuationFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        initBuilder(builder);
+        if (header.getLength() > 0) {
+            decoder = new BytePartialDecoder(header.getLength());
+        } else {
+            setValue(builder.build());
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        if ((decoder != null) && decoder.consume(buffer)) {
+            builder.headerBlockFragment(decoder.getValue());
+            setValue(builder.build());
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2DataFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2DataFrameDecoder.java
new file mode 100644 (file)
index 0000000..7a07812
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2DataFrame.Builder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.FLAGS_PADDING;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2DataFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        PAD_LENGTH,
+        DATA,
+        PADDING
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private int padLength;
+    
+    private Builder builder = new Builder();
+    
+    public Http2DataFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        initBuilder(builder);
+        if (isFlagSet(FLAGS_PADDING)) {
+            state = State.PAD_LENGTH;
+        } else if (header.getLength() > 0) {
+            state = State.DATA;
+            decoder = new BytePartialDecoder(header.getLength());
+        } else {
+            setValue(builder.build());
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case PAD_LENGTH:
+                padLength = buffer.get();
+                if ((getHeader().getLength() - 1 - padLength) > 0) {
+                    state = State.DATA;
+                    decoder = new BytePartialDecoder(getHeader().getLength() - 1 - padLength);
+                } else if (padLength > 0) {
+                    state = State.PADDING;
+                    decoder = new BytePartialDecoder(padLength);
+                } else {
+                    setValue(builder.build());
+                }
+                break;
+            case DATA:
+                if (decoder.consume(buffer)) {
+                    builder.data(((BytePartialDecoder)decoder).getValue());
+                    if (isFlagSet(FLAGS_PADDING) && (padLength > 0)) {
+                      state = State.PADDING;
+                      decoder = new BytePartialDecoder(padLength);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case PADDING:
+                if (decoder.consume(buffer)) {
+                    builder.padding(((BytePartialDecoder)decoder).getValue());
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
index ba32afc..e59c41e 100644 (file)
@@ -3,86 +3,50 @@
  */
 package org.apache.mina.http2.impl;
 
-import java.nio.ByteBuffer;
-
-import org.apache.mina.http2.api.BytePartialDecoder;
 import org.apache.mina.http2.api.Http2Frame;
-import org.apache.mina.http2.api.LongPartialDecoder;
+import org.apache.mina.http2.api.Http2Frame.AbstractHttp2FrameBuilder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
 import org.apache.mina.http2.api.PartialDecoder;
 
-import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
-
 /**
  * @author jeffmaury
  *
  */
-public class Http2FrameDecoder implements PartialDecoder<Http2Frame> {
+public abstract class Http2FrameDecoder implements PartialDecoder<Http2Frame> {
+    private Http2FrameHeader header;
+    
+    private Http2Frame frame;
 
-    private static enum State {
-        LENGTH_TYPE_FLAGS,
-        STREAMID,
-        PAYLOAD
+    public Http2FrameDecoder(Http2FrameHeader header) {
+        this.header = header;
     }
     
-    private State state;
-    
-    private PartialDecoder<?> decoder;
-    
-    private Http2Frame frame;
+    protected boolean isFlagSet(short mask) {
+        return (header.getFlags() & mask) == mask;
+    }
     
-    private boolean frameComplete;
-
-    public Http2FrameDecoder() {
-        reset();
+    protected void initBuilder(AbstractHttp2FrameBuilder builder) {
+        builder.length(header.getLength());
+        builder.type(header.getType());
+        builder.flags(header.getFlags());
+        builder.streamID(header.getStreamID());
     }
     
-    @Override
-    public boolean consume(ByteBuffer buffer) {
-        while (!frameComplete && buffer.remaining() > 0) {
-            switch (state) {
-            case LENGTH_TYPE_FLAGS:
-                if (decoder.consume(buffer)) {
-                    long val = ((LongPartialDecoder)decoder).getValue();
-                    frame.setLength((int) ((val >> 16) & 0xFFFFFFL));
-                    frame.setType((short) ((val >> 8) & 0xFF));
-                    frame.setFlags((short) (val & 0xFF));
-                    state = State.STREAMID;
-                    decoder = new LongPartialDecoder(4);
-                }
-                break;
-            case STREAMID:
-                if (decoder.consume(buffer)) {
-                    frame.setStreamID((int) (((LongPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK));
-                    if (frame.getLength() > 0) {
-                        decoder = new BytePartialDecoder(frame.getLength());
-                        state = State.PAYLOAD;
-                    } else {
-                       frameComplete = true;
-                    }
-                }
-                break;
-            case PAYLOAD:
-                if (decoder.consume(buffer)) {
-                    frame.setPayload(((BytePartialDecoder)decoder).getValue());
-                    frameComplete = true;
-                }
-                break;
-            }
-        }
-        return frameComplete;
+    protected Http2FrameHeader getHeader() {
+        return header;
     }
-
+    
     @Override
     public Http2Frame getValue() {
         return frame;
     }
+    
+    protected void setValue(Http2Frame frame) {
+        this.frame = frame;
+    }
 
     @Override
     public void reset() {
-        state = State.LENGTH_TYPE_FLAGS;
-        decoder = new LongPartialDecoder(5);
-        frame = new Http2Frame();
-        frameComplete = false;
     }
     
 }
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2GoAwayFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2GoAwayFrameDecoder.java
new file mode 100644 (file)
index 0000000..86187b2
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2GoAwayFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2GoAwayFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        LAST_STREAM_ID,
+        CODE,
+        EXTRA
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2GoAwayFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        state = State.LAST_STREAM_ID;
+        decoder = new IntPartialDecoder(4);
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case LAST_STREAM_ID:
+                if (decoder.consume(buffer)) {
+                    builder.lastStreamID(((IntPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK);
+                    state = State.CODE;
+                    decoder.reset();
+                }
+            case CODE:
+                if (decoder.consume(buffer)) {
+                    builder.errorCode(((IntPartialDecoder)decoder).getValue());
+                    if (getHeader().getLength() > 8) {
+                        state = State.EXTRA;
+                        decoder = new BytePartialDecoder(getHeader().getLength() - 8);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case EXTRA:
+                if (decoder.consume(buffer)) {
+                    builder.data(((BytePartialDecoder)decoder).getValue());
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2HeadersFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2HeadersFrameDecoder.java
new file mode 100644 (file)
index 0000000..72a89c3
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2HeadersFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.FLAGS_PADDING;
+import static org.apache.mina.http2.api.Http2Constants.FLAGS_PRIORITY;
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_EXCLUSIVE_MASK;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2HeadersFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        PAD_LENGTH,
+        STREAM_DEPENDENCY,
+        WEIGHT,
+        HEADER,
+        PADDING
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private int padLength;
+    
+    private Builder builder = new Builder();
+    
+    public Http2HeadersFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        if (isFlagSet(FLAGS_PADDING)) {
+            state = State.PAD_LENGTH;
+        } else if (isFlagSet(FLAGS_PRIORITY)) {
+            state = State.STREAM_DEPENDENCY;
+            decoder = new IntPartialDecoder(4);
+        } else {
+            state = State.HEADER;
+            decoder = new BytePartialDecoder(header.getLength());
+        }
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case PAD_LENGTH:
+                padLength = buffer.get();
+                if (isFlagSet(FLAGS_PRIORITY)) {
+                    decoder = new IntPartialDecoder(4);
+                    state = State.STREAM_DEPENDENCY;
+                } else {
+                    state = State.HEADER;
+                    decoder = new BytePartialDecoder(getHeader().getLength() - 1 - padLength);
+                }
+                break;
+            case STREAM_DEPENDENCY:
+                if (decoder.consume(buffer)) {
+                    builder.streamDependencyID(((IntPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK);
+                    builder.exclusiveMode((((IntPartialDecoder)decoder).getValue() & HTTP2_EXCLUSIVE_MASK) == HTTP2_EXCLUSIVE_MASK);
+                    state = State.WEIGHT;
+                }
+                break;
+            case WEIGHT:
+                builder.weight((byte) (buffer.get()+1));
+                state = State.HEADER;
+                int headerLength = getHeader().getLength() - 5;
+                if (isFlagSet(FLAGS_PADDING)) {
+                    headerLength -= (padLength + 1);
+                }
+                decoder = new BytePartialDecoder(headerLength);
+                break;
+            case HEADER:
+                if (decoder.consume(buffer)) {
+                    builder.headerBlockFragment(((BytePartialDecoder)decoder).getValue());
+                    if (isFlagSet(FLAGS_PADDING)) {
+                      state = State.PADDING;
+                      decoder = new BytePartialDecoder(padLength);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case PADDING:
+                if (decoder.consume(buffer)) {
+                    builder.padding(((BytePartialDecoder)decoder).getValue());
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2PingFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2PingFrameDecoder.java
new file mode 100644 (file)
index 0000000..4098d49
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2PingFrame.Builder;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2PingFrameDecoder extends Http2FrameDecoder {
+
+    private static enum State {
+        DATA,
+        EXTRA
+    }
+    
+    private State state;
+    
+    private BytePartialDecoder decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2PingFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        decoder = new BytePartialDecoder(8);
+        state = State.DATA;
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case DATA:
+                if (decoder.consume(buffer)) {
+                    builder.data(decoder.getValue());
+                    if (getHeader().getLength() > 8) {
+                        state = State.EXTRA;
+                        decoder = new BytePartialDecoder(getHeader().getLength() - 8);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case EXTRA:
+                if (decoder.consume(buffer)) {
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2PriorityFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2PriorityFrameDecoder.java
new file mode 100644 (file)
index 0000000..8fd1036
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2PriorityFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_EXCLUSIVE_MASK;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2PriorityFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        STREAM_DEPENDENCY,
+        WEIGHT,
+        EXTRA
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2PriorityFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        state = State.STREAM_DEPENDENCY;
+        decoder = new IntPartialDecoder(4);
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case STREAM_DEPENDENCY:
+                if (decoder.consume(buffer)) {
+                    builder.streamDependencyID(((IntPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK);
+                    builder.exclusiveMode((((IntPartialDecoder)decoder).getValue() & HTTP2_EXCLUSIVE_MASK) == HTTP2_EXCLUSIVE_MASK);
+                    state = State.WEIGHT;
+                }
+                break;
+            case WEIGHT:
+                builder.weight((short) ((buffer.get() & 0x00FF) + 1));
+                int extraLength = getHeader().getLength() - 5;
+                if (extraLength > 0) {
+                    decoder = new BytePartialDecoder(extraLength);
+                state = State.EXTRA;
+                } else {
+                    setValue(builder.build());
+                }
+                break;
+            case EXTRA:
+                if (decoder.consume(buffer)) {
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2PushPromiseFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2PushPromiseFrameDecoder.java
new file mode 100644 (file)
index 0000000..34863ce
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2PushPromiseFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.FLAGS_PADDING;
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2PushPromiseFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        PAD_LENGTH,
+        PROMISED_STREAM,
+        HEADER,
+        PADDING
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private int padLength;
+    
+    private Builder builder = new Builder();
+    
+    public Http2PushPromiseFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        if (isFlagSet(FLAGS_PADDING)) {
+            state = State.PAD_LENGTH;
+        } else {
+            state = State.PROMISED_STREAM;
+            decoder = new IntPartialDecoder(4);
+        }
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case PAD_LENGTH:
+                padLength = buffer.get();
+                state = State.PROMISED_STREAM;
+                decoder = new IntPartialDecoder(4);
+                break;
+            case PROMISED_STREAM:
+                if (decoder.consume(buffer)) {
+                    builder.promisedStreamID(((IntPartialDecoder)decoder).getValue() & HTTP2_31BITS_MASK);
+                    state = State.HEADER;
+                    int headerLength = getHeader().getLength() - 4;
+                    if (isFlagSet(FLAGS_PADDING)) {
+                        headerLength -= (padLength + 1);
+                    }
+                    decoder = new BytePartialDecoder(headerLength);
+                }
+                break;
+            case HEADER:
+                if (decoder.consume(buffer)) {
+                    builder.headerBlockFragment(((BytePartialDecoder)decoder).getValue());
+                    if (isFlagSet(FLAGS_PADDING)) {
+                      state = State.PADDING;
+                      decoder = new BytePartialDecoder(padLength);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case PADDING:
+                if (decoder.consume(buffer)) {
+                    builder.padding(((BytePartialDecoder)decoder).getValue());
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2RstStreamFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2RstStreamFrameDecoder.java
new file mode 100644 (file)
index 0000000..e16b0bc
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2RstStreamFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.LongPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+import static org.apache.mina.http2.api.Http2Constants.HTTP2_31BITS_MASK;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2RstStreamFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        ERROR_CODE,
+        EXTRA
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2RstStreamFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        state = State.ERROR_CODE;
+        decoder = new LongPartialDecoder(4);
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case ERROR_CODE:
+                if (decoder.consume(buffer)) {
+                    builder.errorCode(((LongPartialDecoder)decoder).getValue());
+                    if (getHeader().getLength() > 4) {
+                        state = State.EXTRA;
+                        decoder = new BytePartialDecoder(getHeader().getLength() - 4);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case EXTRA:
+                if (decoder.consume(buffer)) {
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2SettingsFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2SettingsFrameDecoder.java
new file mode 100644 (file)
index 0000000..6213c66
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2SettingsFrame.Builder;
+import org.apache.mina.http2.api.Http2Setting;
+import org.apache.mina.http2.api.LongPartialDecoder;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2SettingsFrameDecoder extends Http2FrameDecoder {
+
+    private int remaining;
+    
+    private LongPartialDecoder decoder = new LongPartialDecoder(6);
+    
+    private Builder builder = new Builder();
+    
+    private Collection<Http2Setting> settings = new ArrayList<Http2Setting>();
+    
+    public Http2SettingsFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        remaining = header.getLength() / 6;
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            if (decoder.consume(buffer)) {
+                remaining--;
+                Http2Setting setting = new Http2Setting();
+                setting.setID((int) ((decoder.getValue() & 0x00FFFF00000000L) >> 32));
+                setting.setValue(decoder.getValue() & 0x00FFFFFFFFL);
+                settings.add(setting);
+                decoder.reset();
+                if (remaining == 0) {
+                    builder.settings(settings);
+                    setValue(builder.build());
+                }
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2UnknownFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2UnknownFrameDecoder.java
new file mode 100644 (file)
index 0000000..f19e55f
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2UnknownFrame.Builder;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2UnknownFrameDecoder extends Http2FrameDecoder {
+
+    private BytePartialDecoder decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2UnknownFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        initBuilder(builder);
+        if (header.getLength() > 0) {
+            decoder = new BytePartialDecoder(header.getLength());
+        } else {
+            setValue(builder.build());
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            if (decoder.consume(buffer)) {
+                builder.payload(decoder.getValue());
+                setValue(builder.build());
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
diff --git a/http2/src/main/java/org/apache/mina/http2/impl/Http2WindowUpdateFrameDecoder.java b/http2/src/main/java/org/apache/mina/http2/impl/Http2WindowUpdateFrameDecoder.java
new file mode 100644 (file)
index 0000000..6d96327
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * 
+ */
+package org.apache.mina.http2.impl;
+
+import java.nio.ByteBuffer;
+
+import org.apache.mina.http2.api.BytePartialDecoder;
+import org.apache.mina.http2.api.Http2FrameHeadePartialDecoder.Http2FrameHeader;
+import org.apache.mina.http2.api.Http2WindowUpdateFrame.Builder;
+import org.apache.mina.http2.api.IntPartialDecoder;
+import org.apache.mina.http2.api.PartialDecoder;
+
+/**
+ * @author jeffmaury
+ *
+ */
+public class Http2WindowUpdateFrameDecoder extends Http2FrameDecoder {
+
+    private enum State {
+        INCREMENT,
+        EXTRA
+    }
+    
+    private State state;
+    
+    private PartialDecoder<?> decoder;
+    
+    private Builder builder = new Builder();
+    
+    public Http2WindowUpdateFrameDecoder(Http2FrameHeader header) {
+        super(header);
+        state = State.INCREMENT;
+        decoder = new IntPartialDecoder(4);
+        initBuilder(builder);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#consume(java.nio.ByteBuffer)
+     */
+    @Override
+    public boolean consume(ByteBuffer buffer) {
+        while ((getValue() == null) && buffer.remaining() > 0) {
+            switch (state) {
+            case INCREMENT:
+                if (decoder.consume(buffer)) {
+                    builder.windowUpdateIncrement(((IntPartialDecoder)decoder).getValue());
+                    if (getHeader().getLength() > 4) {
+                        state = State.EXTRA;
+                        decoder = new BytePartialDecoder(getHeader().getLength() - 4);
+                    } else {
+                        setValue(builder.build());
+                    }
+                }
+                break;
+            case EXTRA:
+                if (decoder.consume(buffer)) {
+                    setValue(builder.build());
+                }
+                break;
+            }
+        }
+        return getValue() != null;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.mina.http2.api.PartialDecoder#reset()
+     */
+    @Override
+    public void reset() {
+    }
+
+}
index 30b7299..02f7198 100644 (file)
@@ -11,10 +11,10 @@ import org.junit.Test;
 public class Htp2PushPromiseFrameDecoderTest {
 
     @Test
-    public void checkHeadersFrameWithNotPadding() {
+    public void checkWithNoPadding() {
         Http2Connection connection = new Http2Connection();
         ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x05, /*length*/
-                                                        0x01, /*type*/
+                                                        0x05, /*type*/
                                                         0x00, /*flags*/
                                                         0x00, 0x00, 0x00, 0x01, /*streamID*/
                                                         0x00, 0x00, 0x01, 0x00, /*promisedStreamID*/
@@ -32,49 +32,26 @@ public class Htp2PushPromiseFrameDecoderTest {
     
     
     @Test
-    public void checkHeadersFramePaddingPriority() {
+    public void checkWithPadding() {
         Http2Connection connection = new Http2Connection();
-        ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x17, /*length*/
-                                                        0x01, /*type*/
-                                                        0x28, /*flags*/
-                                                        0x00, 0x00, 0x00, 0x03, /*streamID*/
-                                                        0x10, /*padding length*/
-                                                        (byte)0x0080, 0x00, 0x00, 0x14, /*stream dependency*/
-                                                        0x09, /*weight*/
-                                                        (byte) 0x0082, /*headerFragment*/
-                                                        0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2E /*padding*/});
-        Http2HeadersFrame frame = (Http2HeadersFrame) connection.decode(buffer);
-        assertNotNull(frame);
-        assertEquals(23, frame.getLength());
-        assertEquals(1, frame.getType());
-        assertEquals(0x28, frame.getFlags());
-        assertEquals(3, frame.getStreamID());
-        assertEquals(10,  frame.getWeight());
-        assertEquals(1, frame.getHeaderBlockFragment().length);
-        assertEquals(0x0082, frame.getHeaderBlockFragment()[0] & 0x00FF);
-        assertEquals(16,  frame.getPadding().length);
-        assertArrayEquals(new byte[] {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2E}, frame.getPadding());
-    }
-    
-    @Test
-    public void checkHeadersFramePaddingNoPriority() {
-        Http2Connection connection = new Http2Connection();
-        ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x12, /*length*/
-                                                        0x01, /*type*/
+        ByteBuffer buffer = ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x16, /*length*/
+                                                        0x05, /*type*/
                                                         0x08, /*flags*/
                                                         0x00, 0x00, 0x00, 0x03, /*streamID*/
                                                         0x10, /*padding length*/
+                                                        0x00, 0x00, 0x00, 0x14, /*promisedStreamID*/
                                                         (byte) 0x0082, /*headerFragment*/
                                                         0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2E /*padding*/});
-        Http2HeadersFrame frame = (Http2HeadersFrame) connection.decode(buffer);
-        assertNotNull(frame);
-        assertEquals(18, frame.getLength());
-        assertEquals(1, frame.getType());
+        Http2PushPromiseFrame frame = (Http2PushPromiseFrame) connection.decode(buffer);
+        assertEquals(22, frame.getLength());
+        assertEquals(5, frame.getType());
         assertEquals(0x08, frame.getFlags());
         assertEquals(3, frame.getStreamID());
+        assertEquals(20, frame.getPromisedStreamID());
         assertEquals(1, frame.getHeaderBlockFragment().length);
         assertEquals(0x0082, frame.getHeaderBlockFragment()[0] & 0x00FF);
         assertEquals(16,  frame.getPadding().length);
         assertArrayEquals(new byte[] {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6E, 0x67, 0x2E}, frame.getPadding());
     }
+    
 }
index bda0c77..af684b4 100644 (file)
@@ -58,8 +58,6 @@ public class Http2FrameHeaderPartialDecoderTest {
         assertEquals(0, header.getType());
         assertEquals(0, header.getFlags());
         assertEquals(1, header.getStreamID());
-        assertEquals(1, header.getPayload().length);
-        assertEquals(0x40, header.getPayload()[0]       );
     }
 
 }