Full Code of twitter/netty-http2 for AI

master e8df896db610 cached
54 files
303.3 KB
67.2k tokens
589 symbols
1 requests
Download .txt
Showing preview only (323K chars total). Download the full file or copy to clipboard to get everything.
Repository: twitter/netty-http2
Branch: master
Commit: e8df896db610
Files: 54
Total size: 303.3 KB

Directory structure:
gitextract_lusmlr8n/

├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── com/
    │           └── twitter/
    │               └── http2/
    │                   ├── DefaultHttp2Headers.java
    │                   ├── DefaultHttpDataFrame.java
    │                   ├── DefaultHttpGoAwayFrame.java
    │                   ├── DefaultHttpHeaderBlockFrame.java
    │                   ├── DefaultHttpHeadersFrame.java
    │                   ├── DefaultHttpPingFrame.java
    │                   ├── DefaultHttpPriorityFrame.java
    │                   ├── DefaultHttpPushPromiseFrame.java
    │                   ├── DefaultHttpRstStreamFrame.java
    │                   ├── DefaultHttpSettingsFrame.java
    │                   ├── DefaultHttpStreamFrame.java
    │                   ├── DefaultHttpWindowUpdateFrame.java
    │                   ├── HttpCodecUtil.java
    │                   ├── HttpConnection.java
    │                   ├── HttpConnectionHandler.java
    │                   ├── HttpDataFrame.java
    │                   ├── HttpErrorCode.java
    │                   ├── HttpFrame.java
    │                   ├── HttpFrameDecoder.java
    │                   ├── HttpFrameDecoderDelegate.java
    │                   ├── HttpFrameEncoder.java
    │                   ├── HttpGoAwayFrame.java
    │                   ├── HttpHeaderBlockDecoder.java
    │                   ├── HttpHeaderBlockEncoder.java
    │                   ├── HttpHeaderBlockFrame.java
    │                   ├── HttpHeadersFrame.java
    │                   ├── HttpMessageProxy.java
    │                   ├── HttpPingFrame.java
    │                   ├── HttpPriorityFrame.java
    │                   ├── HttpProtocolException.java
    │                   ├── HttpPushPromiseFrame.java
    │                   ├── HttpRequestProxy.java
    │                   ├── HttpResponseProxy.java
    │                   ├── HttpRstStreamFrame.java
    │                   ├── HttpSettingsFrame.java
    │                   ├── HttpStreamDecoder.java
    │                   ├── HttpStreamEncoder.java
    │                   ├── HttpStreamFrame.java
    │                   ├── HttpWindowUpdateFrame.java
    │                   ├── Pipe.java
    │                   ├── StreamedHttpMessage.java
    │                   ├── StreamedHttpRequest.java
    │                   └── StreamedHttpResponse.java
    └── test/
        └── java/
            └── com/
                └── twitter/
                    └── http2/
                        ├── HttpFrameDecoderTest.java
                        ├── HttpFrameEncoderTest.java
                        ├── HttpHeaderCompressionTest.java
                        ├── HttpRequestProxyTest.java
                        ├── HttpResponseProxyTest.java
                        └── PipeTest.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
target
.idea
*.iml


================================================
FILE: .travis.yml
================================================
language: java
sudo: false
jdk:
  - openjdk7
after_success:
  - mvn clean cobertura:cobertura coveralls:cobertura


================================================
FILE: LICENSE
================================================
Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed 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.



================================================
FILE: README.md
================================================
# netty-http2 [![Build Status](https://travis-ci.org/twitter/netty-http2.svg)](https://travis-ci.org/twitter/netty-http2)

Legacy HTTP/2 codec for Netty. Use the codec shipped with Netty 4.1 instead https://github.com/netty/netty/tree/4.1/codec-http2


================================================
FILE: pom.xml
================================================
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.twitter</groupId>
    <artifactId>netty-http2</artifactId>
    <version>0.17.12-SNAPSHOT</version>
    <name>HTTP/2</name>
    <url>http://github.com/twitter/netty-http2</url>
    <description>HTTP/2 for Netty</description>

    <scm>
        <connection>scm:git:git@github.com:twitter/netty-http2.git</connection>
        <url>scm:git:git@github.com:twitter/netty-http2.git</url>
        <developerConnection>scm:git:git@github.com:twitter/netty-http2.git</developerConnection>
    </scm>

    <licenses>
        <license>
            <name>The Apache Software License, Version 2.0</name>
            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
        </license>
    </licenses>

    <developers>
        <developer>
            <name>Jeff Pinner</name>
            <email>jpinner@twitter.com</email>
        </developer>
    </developers>

    <properties>
        <maven.compiler.source>1.6</maven.compiler.source>
        <maven.compiler.target>1.6</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <encoding>UTF-8</encoding>
    </properties>

    <distributionManagement>
        <snapshotRepository>
            <id>sonatype-nexus-snapshots</id>
            <name>Sonatype OSS</name>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
        </snapshotRepository>
        <repository>
            <id>sonatype-nexus-staging</id>
            <name>Nexus Release Repository</name>
            <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
        </repository>
    </distributionManagement>

    <dependencies>
        <dependency>
            <groupId>com.twitter</groupId>
            <artifactId>hpack</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec-http</artifactId>
            <version>4.1.0.CR4</version>
        </dependency>
        <!-- Test Deps -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>1.9.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>sonatype-nexus-snapshots</id>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-release-plugin</artifactId>
                    <version>2.1</version>
                    <configuration>
                        <mavenExecutorId>forked-path</mavenExecutorId>
                        <useReleaseProfile>false</useReleaseProfile>
                        <arguments>-Psonatype-oss-release</arguments>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.12</version>
                <configuration>
                    <argLine>-Xmx1024m</argLine>
                    <redirectTestOutputToFile>false</redirectTestOutputToFile>
                    <includes>
                        <include>**/Test*.java</include>
                        <include>**/*Test.java</include>
                        <include>**/*Spec.java</include>
                    </includes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>cobertura-maven-plugin</artifactId>
                <version>2.5.2</version>
                <configuration>
                    <format>xml</format>
                    <maxmem>256m</maxmem>
                    <aggregate>true</aggregate>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.eluder.coveralls</groupId>
                <artifactId>coveralls-maven-plugin</artifactId>
                <version>2.2.0</version>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>sonatype-oss-release</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-source-plugin</artifactId>
                        <version>2.1.2</version>
                        <executions>
                            <execution>
                                <id>attach-sources</id>
                                <goals>
                                    <goal>jar-no-fork</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-javadoc-plugin</artifactId>
                        <version>2.7</version>
                        <executions>
                            <execution>
                                <id>attach-javadocs</id>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-gpg-plugin</artifactId>
                        <version>1.1</version>
                        <executions>
                            <execution>
                                <id>sign-artifacts</id>
                                <phase>verify</phase>
                                <goals>
                                    <goal>sign</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttp2Headers.java
================================================
package com.twitter.http2;

import io.netty.handler.codec.DefaultHeaders.NameValidator;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.internal.PlatformDependent;

import static io.netty.util.internal.ObjectUtil.checkNotNull;

public class DefaultHttp2Headers extends DefaultHttpHeaders {
  private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
    @Override
    public boolean process(byte value) throws Exception {
      validateChar((char) (value & 0xFF));
      return true;
    }
  };

  private static void validateChar(char character) {
    switch (character) {
      case '\t':
      case '\n':
      case 0x0b:
      case '\f':
      case '\r':
      case ' ':
      case ',':
      case ';':
      case '=':
        throw new IllegalArgumentException(
                "a header name cannot contain the following prohibited characters: =,; \\t\\r\\n\\v\\f: " +
                        character);
      default:
        // Check to see if the character is not an ASCII character, or invalid
        if (character > 127) {
          throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
                  character);
        }
    }
  }

  static final NameValidator<CharSequence> Http2NameValidator = new NameValidator<CharSequence>() {
    @Override
    public void validateName(CharSequence name) {
      if (name instanceof AsciiString) {
        try {
          ((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR);
        } catch (Exception e) {
          PlatformDependent.throwException(e);
        }
      } else {
        checkNotNull(name, "name");
        // Go through each character in the name
        for (int index = 0; index < name.length(); ++index) {
          validateChar(name.charAt(index));
        }
      }
    }
  };

  public DefaultHttp2Headers() {
    super(true, Http2NameValidator);
  }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpDataFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled;
import io.netty.util.IllegalReferenceCountException;
import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpDataFrame} implementation.
 */
public class DefaultHttpDataFrame extends DefaultHttpStreamFrame implements HttpDataFrame {

    private final ByteBuf data;
    private boolean last;

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     */
    public DefaultHttpDataFrame(int streamId) {
        this(streamId, Unpooled.buffer(0));
    }

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     * @param data     the payload of the frame. Can not exceed {@link HttpCodecUtil#HTTP_MAX_LENGTH}
     */
    public DefaultHttpDataFrame(int streamId, ByteBuf data) {
        super(streamId);
        if (data == null) {
            throw new NullPointerException("data");
        }
        this.data = validate(data);
    }

    private static ByteBuf validate(ByteBuf data) {
        if (data.readableBytes() > HttpCodecUtil.HTTP_MAX_LENGTH) {
            throw new IllegalArgumentException("data payload cannot exceed "
                    + HttpCodecUtil.HTTP_MAX_LENGTH + " bytes");
        }
        return data;
    }

    @Override
    public boolean isLast() {
        return last;
    }

    @Override
    public HttpDataFrame setLast(boolean last) {
        this.last = last;
        return this;
    }

    @Override
    public HttpDataFrame setStreamId(int streamId) {
        super.setStreamId(streamId);
        return this;
    }

    @Override
    public ByteBuf content() {
        if (data.refCnt() <= 0) {
            throw new IllegalReferenceCountException(data.refCnt());
        }
        return data;
    }

    @Override
    public HttpDataFrame copy() {
        HttpDataFrame frame = new DefaultHttpDataFrame(getStreamId(), content().copy());
        frame.setLast(isLast());
        return frame;
    }

    @Override
    public HttpDataFrame duplicate() {
        HttpDataFrame frame = new DefaultHttpDataFrame(getStreamId(), content().duplicate());
        frame.setLast(isLast());
        return frame;
    }

    @Override
    public int refCnt() {
        return data.refCnt();
    }

    @Override
    public HttpDataFrame retain() {
        data.retain();
        return this;
    }

    @Override
    public HttpDataFrame retain(int increment) {
        data.retain(increment);
        return this;
    }

    @Override
    public boolean release() {
        return data.release();
    }

    @Override
    public boolean release(int decrement) {
        return data.release(decrement);
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append("(last: ");
        buf.append(isLast());
        buf.append(')');
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Size = ");
        if (refCnt() == 0) {
            buf.append("(freed)");
        } else {
            buf.append(content().readableBytes());
        }
        return buf.toString();
    }

    @Override
    public ByteBufHolder touch() {
        data.touch();
        return this;
    }

    @Override
    public ByteBufHolder touch(Object o) {
        data.touch(o);
        return this;
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpGoAwayFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpGoAwayFrame} implementation.
 */
public class DefaultHttpGoAwayFrame implements HttpGoAwayFrame {

    private int lastStreamId;
    private HttpErrorCode errorCode;

    /**
     * Creates a new instance.
     *
     * @param lastStreamId the Last-Stream-ID of this frame
     * @param code         the error code of this frame
     */
    public DefaultHttpGoAwayFrame(int lastStreamId, int code) {
        this(lastStreamId, HttpErrorCode.valueOf(code));
    }

    /**
     * Creates a new instance.
     *
     * @param lastStreamId the Last-Stream-ID of this frame
     * @param errorCode    the error code of this frame
     */
    public DefaultHttpGoAwayFrame(int lastStreamId, HttpErrorCode errorCode) {
        setLastStreamId(lastStreamId);
        setErrorCode(errorCode);
    }

    @Override
    public int getLastStreamId() {
        return lastStreamId;
    }

    @Override
    public HttpGoAwayFrame setLastStreamId(int lastStreamId) {
        if (lastStreamId < 0) {
            throw new IllegalArgumentException(
                    "Last-Stream-ID cannot be negative: " + lastStreamId);
        }
        this.lastStreamId = lastStreamId;
        return this;
    }

    @Override
    public HttpErrorCode getErrorCode() {
        return errorCode;
    }

    @Override
    public HttpGoAwayFrame setErrorCode(HttpErrorCode errorCode) {
        this.errorCode = errorCode;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Last-Stream-ID = ");
        buf.append(getLastStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Error Code: ");
        buf.append(getErrorCode().toString());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpHeaderBlockFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.handler.codec.http.HttpHeaders;
import io.netty.util.internal.StringUtil;

import java.util.Map;

/**
 * The default {@link HttpHeaderBlockFrame} implementation.
 */
public abstract class DefaultHttpHeaderBlockFrame extends DefaultHttpStreamFrame
        implements HttpHeaderBlockFrame {

    private boolean invalid;
    private boolean truncated;
    private final HttpHeaders headers = new DefaultHttp2Headers();

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     */
    protected DefaultHttpHeaderBlockFrame(int streamId) {
        super(streamId);
    }

    @Override
    public boolean isInvalid() {
        return invalid;
    }

    @Override
    public HttpHeaderBlockFrame setInvalid() {
        invalid = true;
        return this;
    }

    @Override
    public boolean isTruncated() {
        return truncated;
    }

    @Override
    public HttpHeaderBlockFrame setTruncated() {
        truncated = true;
        return this;
    }

    @Override
    public HttpHeaders headers() {
        return headers;
    }

    protected void appendHeaders(StringBuilder buf) {
        for (Map.Entry<String, String> e : headers()) {
            buf.append("    ");
            buf.append(e.getKey());
            buf.append(": ");
            buf.append(e.getValue());
            buf.append(StringUtil.NEWLINE);
        }
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpHeadersFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_DEPENDENCY;
import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;

/**
 * The default {@link HttpHeadersFrame} implementation.
 */
public class DefaultHttpHeadersFrame extends DefaultHttpHeaderBlockFrame
        implements HttpHeadersFrame {

    private boolean last;
    private boolean exclusive = false;
    private int dependency = HTTP_DEFAULT_DEPENDENCY;
    private int weight = HTTP_DEFAULT_WEIGHT;

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     */
    public DefaultHttpHeadersFrame(int streamId) {
        super(streamId);
    }

    @Override
    public boolean isLast() {
        return last;
    }

    @Override
    public HttpHeadersFrame setLast(boolean last) {
        this.last = last;
        return this;
    }

    @Override
    public boolean isExclusive() {
        return exclusive;
    }

    @Override
    public HttpHeadersFrame setExclusive(boolean exclusive) {
        this.exclusive = exclusive;
        return this;
    }

    @Override
    public int getDependency() {
        return dependency;
    }

    @Override
    public HttpHeadersFrame setDependency(int dependency) {
        if (dependency < 0) {
            throw new IllegalArgumentException(
                    "Dependency cannot be negative: " + dependency);
        }
        this.dependency = dependency;
        return this;
    }

    @Override
    public int getWeight() {
        return weight;
    }

    @Override
    public HttpHeadersFrame setWeight(int weight) {
        if (weight <= 0 || weight > 256) {
            throw new IllegalArgumentException(
                    "Illegal weight: " + weight);
        }
        this.weight = weight;
        return this;
    }

    @Override
    public HttpHeadersFrame setStreamId(int streamId) {
        super.setStreamId(streamId);
        return this;
    }

    @Override
    public HttpHeadersFrame setInvalid() {
        super.setInvalid();
        return this;
    }

    @Override
    public HttpHeadersFrame setTruncated() {
        super.setTruncated();
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append("(last: ");
        buf.append(isLast());
        buf.append(')');
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Dependency = ");
        buf.append(getDependency());
        buf.append(" (exclusive: ");
        buf.append(isExclusive());
        buf.append(", weight: ");
        buf.append(getWeight());
        buf.append(')');
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Headers:");
        buf.append(StringUtil.NEWLINE);
        appendHeaders(buf);

        // Remove the last newline.
        buf.setLength(buf.length() - StringUtil.NEWLINE.length());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpPingFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpPingFrame} implementation.
 */
public class DefaultHttpPingFrame implements HttpPingFrame {

    private long data;
    private boolean pong;

    /**
     * Creates a new instance.
     *
     * @param data the data payload of this frame
     */
    public DefaultHttpPingFrame(long data) {
        setData(data);
    }

    @Override
    public long getData() {
        return data;
    }

    @Override
    public HttpPingFrame setData(long data) {
        this.data = data;
        return this;
    }

    @Override
    public boolean isPong() {
        return pong;
    }

    @Override
    public HttpPingFrame setPong(boolean pong) {
        this.pong = pong;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append("(pong: ");
        buf.append(isPong());
        buf.append(')');
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Data = ");
        buf.append(getData());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpPriorityFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpPriorityFrame} implementation.
 */
public class DefaultHttpPriorityFrame implements HttpPriorityFrame {

    private int streamId;
    private boolean exclusive;
    private int dependency;
    private int weight;

    /**
     * Creates a new instance.
     *
     * @param streamId   the stream identifier of this frame
     * @param exclusive  if the dependency of the stream is exclusive
     * @param dependency the dependency of the stream
     * @param weight     the weight of the dependency of the stream
     */
    public DefaultHttpPriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
        setStreamId(streamId);
        setExclusive(exclusive);
        setDependency(dependency);
        setWeight(weight);
    }

    @Override
    public int getStreamId() {
        return streamId;
    }

    @Override
    public HttpPriorityFrame setStreamId(int streamId) {
        if (streamId <= 0) {
            throw new IllegalArgumentException(
                    "Stream identifier must be positive: " + streamId);
        }
        this.streamId = streamId;
        return this;
    }

    @Override
    public boolean isExclusive() {
        return exclusive;
    }

    @Override
    public HttpPriorityFrame setExclusive(boolean exclusive) {
        this.exclusive = exclusive;
        return this;
    }

    @Override
    public int getDependency() {
        return dependency;
    }

    @Override
    public HttpPriorityFrame setDependency(int dependency) {
        if (dependency < 0) {
            throw new IllegalArgumentException(
                    "Dependency cannot be negative: " + dependency);
        }
        this.dependency = dependency;
        return this;
    }

    @Override
    public int getWeight() {
        return weight;
    }

    @Override
    public HttpPriorityFrame setWeight(int weight) {
        if (weight <= 0 || weight > 256) {
            throw new IllegalArgumentException(
                    "Illegal weight: " + weight);
        }
        this.weight = weight;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Dependency = ");
        buf.append(getDependency());
        buf.append(" (exclusive: ");
        buf.append(isExclusive());
        buf.append(", weight: ");
        buf.append(getWeight());
        buf.append(')');
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpPushPromiseFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpPushPromiseFrame} implementation.
 */
public class DefaultHttpPushPromiseFrame extends DefaultHttpHeaderBlockFrame
        implements HttpPushPromiseFrame {

    private int promisedStreamId;

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     */
    public DefaultHttpPushPromiseFrame(int streamId, int promisedStreamId) {
        super(streamId);
        setPromisedStreamId(promisedStreamId);
    }

    @Override
    public int getPromisedStreamId() {
        return promisedStreamId;
    }

    @Override
    public HttpPushPromiseFrame setPromisedStreamId(int promisedStreamId) {
        if (promisedStreamId <= 0) {
            throw new IllegalArgumentException(
                    "Promised-Stream-ID must be positive: " + promisedStreamId);
        }
        this.promisedStreamId = promisedStreamId;
        return this;
    }

    @Override
    public HttpPushPromiseFrame setStreamId(int streamId) {
        super.setStreamId(streamId);
        return this;
    }

    @Override
    public HttpPushPromiseFrame setInvalid() {
        super.setInvalid();
        return this;
    }

    @Override
    public HttpPushPromiseFrame setTruncated() {
        super.setTruncated();
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Promised-Stream-ID = ");
        buf.append(getPromisedStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Headers:");
        buf.append(StringUtil.NEWLINE);
        appendHeaders(buf);

        // Remove the last newline.
        buf.setLength(buf.length() - StringUtil.NEWLINE.length());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpRstStreamFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpRstStreamFrame} implementation.
 */
public class DefaultHttpRstStreamFrame implements HttpRstStreamFrame {

    private int streamId;
    private HttpErrorCode errorCode;

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     * @param code     the error code of this frame
     */
    public DefaultHttpRstStreamFrame(int streamId, int code) {
        this(streamId, HttpErrorCode.valueOf(code));
    }

    /**
     * Creates a new instance.
     *
     * @param streamId  the stream identifier of this frame
     * @param errorCode the error code of this frame
     */
    public DefaultHttpRstStreamFrame(int streamId, HttpErrorCode errorCode) {
        setStreamId(streamId);
        setErrorCode(errorCode);
    }

    @Override
    public int getStreamId() {
        return streamId;
    }

    @Override
    public HttpRstStreamFrame setStreamId(int streamId) {
        if (streamId <= 0) {
            throw new IllegalArgumentException(
                    "Stream identifier must be positive: " + streamId);
        }
        this.streamId = streamId;
        return this;
    }

    @Override
    public HttpErrorCode getErrorCode() {
        return errorCode;
    }

    @Override
    public HttpRstStreamFrame setErrorCode(HttpErrorCode errorCode) {
        this.errorCode = errorCode;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Error Code: ");
        buf.append(getErrorCode().toString());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpSettingsFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpSettingsFrame} implementation.
 */
public class DefaultHttpSettingsFrame implements HttpSettingsFrame {

    private final Map<Integer, Integer> settingsMap = new TreeMap<Integer, Integer>();
    private boolean ack;

    @Override
    public Set<Integer> getIds() {
        return settingsMap.keySet();
    }

    @Override
    public boolean isSet(int id) {
        return settingsMap.containsKey(id);
    }

    @Override
    public int getValue(int id) {
        if (settingsMap.containsKey(id)) {
            return settingsMap.get(id);
        } else {
            return -1;
        }
    }

    @Override
    public HttpSettingsFrame setValue(int id, int value) {
        if (id < 0 || id > HttpCodecUtil.HTTP_SETTINGS_MAX_ID) {
            throw new IllegalArgumentException("Setting ID is not valid: " + id);
        }
        settingsMap.put(id, value);
        return this;
    }

    @Override
    public HttpSettingsFrame removeValue(int id) {
        settingsMap.remove(id);
        return this;
    }

    @Override
    public boolean isAck() {
        return ack;
    }

    @Override
    public HttpSettingsFrame setAck(boolean ack) {
        this.ack = ack;
        return this;
    }

    private Set<Map.Entry<Integer, Integer>> getSettings() {
        return settingsMap.entrySet();
    }

    private void appendSettings(StringBuilder buf) {
        for (Map.Entry<Integer, Integer> e : getSettings()) {
            buf.append("--> ");
            buf.append(e.getKey().toString());
            buf.append(':');
            buf.append(e.getValue().toString());
            buf.append(StringUtil.NEWLINE);
        }
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append("(ack: ");
        buf.append(isAck());
        buf.append(')');
        buf.append(StringUtil.NEWLINE);
        appendSettings(buf);
        buf.setLength(buf.length() - StringUtil.NEWLINE.length());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpStreamFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * The default {@link HttpStreamFrame} implementation.
 */
public abstract class DefaultHttpStreamFrame implements HttpStreamFrame {

    private int streamId;

    /**
     * Creates a new instance.
     *
     * @param streamId the stream identifier of this frame
     */
    protected DefaultHttpStreamFrame(int streamId) {
        setStreamId(streamId);
    }

    @Override
    public int getStreamId() {
        return streamId;
    }

    @Override
    public HttpStreamFrame setStreamId(int streamId) {
        if (streamId <= 0) {
            throw new IllegalArgumentException(
                    "Stream identifier must be positive: " + streamId);
        }
        this.streamId = streamId;
        return this;
    }
}


================================================
FILE: src/main/java/com/twitter/http2/DefaultHttpWindowUpdateFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.util.internal.StringUtil;

/**
 * The default {@link HttpWindowUpdateFrame} implementation.
 */
public class DefaultHttpWindowUpdateFrame implements HttpWindowUpdateFrame {

    private int streamId;
    private int windowSizeIncrement;

    /**
     * Creates a new instance.
     *
     * @param streamId            the stream identifier of this frame
     * @param windowSizeIncrement the Window-Size-Increment of this frame
     */
    public DefaultHttpWindowUpdateFrame(int streamId, int windowSizeIncrement) {
        setStreamId(streamId);
        setWindowSizeIncrement(windowSizeIncrement);
    }

    @Override
    public int getStreamId() {
        return streamId;
    }

    @Override
    public DefaultHttpWindowUpdateFrame setStreamId(int streamId) {
        if (streamId < 0) {
            throw new IllegalArgumentException(
                    "Stream identifier cannot be negative: " + streamId);
        }
        this.streamId = streamId;
        return this;
    }

    @Override
    public int getWindowSizeIncrement() {
        return windowSizeIncrement;
    }

    @Override
    public DefaultHttpWindowUpdateFrame setWindowSizeIncrement(int windowSizeIncrement) {
        if (windowSizeIncrement <= 0) {
            throw new IllegalArgumentException(
                    "Window-Size-Increment must be positive: " + windowSizeIncrement);
        }
        this.windowSizeIncrement = windowSizeIncrement;
        return this;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(StringUtil.simpleClassName(this));
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Stream-ID = ");
        buf.append(getStreamId());
        buf.append(StringUtil.NEWLINE);
        buf.append("--> Window-Size-Increment = ");
        buf.append(getWindowSizeIncrement());
        return buf.toString();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpCodecUtil.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.buffer.ByteBuf;

final class HttpCodecUtil {

    static final int HTTP_FRAME_HEADER_SIZE = 9;
    static final int HTTP_MAX_LENGTH = 0x4000; // Initial MAX_FRAME_SIZE value is 2^14

    static final int HTTP_DATA_FRAME = 0x00;
    static final int HTTP_HEADERS_FRAME = 0x01;
    static final int HTTP_PRIORITY_FRAME = 0x02;
    static final int HTTP_RST_STREAM_FRAME = 0x03;
    static final int HTTP_SETTINGS_FRAME = 0x04;
    static final int HTTP_PUSH_PROMISE_FRAME = 0x05;
    static final int HTTP_PING_FRAME = 0x06;
    static final int HTTP_GOAWAY_FRAME = 0x07;
    static final int HTTP_WINDOW_UPDATE_FRAME = 0x08;
    static final int HTTP_CONTINUATION_FRAME = 0x09;

    static final byte HTTP_FLAG_ACK = 0x01;
    static final byte HTTP_FLAG_END_STREAM = 0x01;
    static final byte HTTP_FLAG_END_SEGMENT = 0x02;
    static final byte HTTP_FLAG_END_HEADERS = 0x04;
    static final byte HTTP_FLAG_PADDED = 0x08;
    static final byte HTTP_FLAG_PRIORITY = 0x20;

    static final int HTTP_DEFAULT_WEIGHT = 16;
    static final int HTTP_DEFAULT_DEPENDENCY = 0;

    static final int HTTP_SETTINGS_MAX_ID = 0xFFFF; // Identifier is a 16-bit field

    static final int HTTP_CONNECTION_STREAM_ID = 0;

    /**
     * Reads a big-endian unsigned short integer from the buffer.
     */
    static int getUnsignedShort(ByteBuf buf, int offset) {
        return (buf.getByte(offset) & 0xFF) << 8
                | buf.getByte(offset + 1) & 0xFF;
    }

    /**
     * Reads a big-endian unsigned medium integer from the buffer.
     */
    static int getUnsignedMedium(ByteBuf buf, int offset) {
        return (buf.getByte(offset) & 0xFF) << 16
                | (buf.getByte(offset + 1) & 0xFF) << 8
                | buf.getByte(offset + 2) & 0xFF;
    }

    /**
     * Reads a big-endian (31-bit) integer from the buffer.
     */
    static int getUnsignedInt(ByteBuf buf, int offset) {
        return (buf.getByte(offset) & 0x7F) << 24
                | (buf.getByte(offset + 1) & 0xFF) << 16
                | (buf.getByte(offset + 2) & 0xFF) << 8
                | buf.getByte(offset + 3) & 0xFF;
    }

    /**
     * Reads a big-endian signed integer from the buffer.
     */
    static int getSignedInt(ByteBuf buf, int offset) {
        return (buf.getByte(offset) & 0xFF) << 24
                | (buf.getByte(offset + 1) & 0xFF) << 16
                | (buf.getByte(offset + 2) & 0xFF) << 8
                | buf.getByte(offset + 3) & 0xFF;
    }

    /**
     * Reads a big-endian signed long from the buffer.
     */
    static long getSignedLong(ByteBuf buf, int offset) {
        return ((long) buf.getByte(offset) & 0xFF) << 56
                | ((long) buf.getByte(offset + 1) & 0xFF) << 48
                | ((long) buf.getByte(offset + 2) & 0xFF) << 40
                | ((long) buf.getByte(offset + 3) & 0xFF) << 32
                | ((long) buf.getByte(offset + 4) & 0xFF) << 24
                | ((long) buf.getByte(offset + 5) & 0xFF) << 16
                | ((long) buf.getByte(offset + 6) & 0xFF) << 8
                | (long) buf.getByte(offset + 7) & 0xFF;
    }

    /**
     * Returns {@code true} if the stream identifier is for a server initiated stream.
     */
    static boolean isServerId(int streamId) {
        // Server initiated streams have even stream identifiers
        return streamId % 2 == 0;
    }

    private HttpCodecUtil() {
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpConnection.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import io.netty.channel.ChannelPromise;
import io.netty.util.internal.EmptyArrays;

import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;
import static com.twitter.http2.HttpCodecUtil.HTTP_CONNECTION_STREAM_ID;

final class HttpConnection {

    private static final HttpProtocolException STREAM_CLOSED =
            new HttpProtocolException("Stream closed");

    static {
        STREAM_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
    }

    private final AtomicInteger activeLocalStreams = new AtomicInteger();
    private final AtomicInteger activeRemoteStreams = new AtomicInteger();
    private final Map<Integer, Node> streams = new ConcurrentHashMap<Integer, Node>();

    private final AtomicInteger sendWindowSize;
    private final AtomicInteger receiveWindowSize;

    public HttpConnection(int sendWindowSize, int receiveWindowSize) {
        streams.put(HTTP_CONNECTION_STREAM_ID, new Node(null));
        this.sendWindowSize = new AtomicInteger(sendWindowSize);
        this.receiveWindowSize = new AtomicInteger(receiveWindowSize);
    }

    int numActiveStreams(boolean remote) {
        if (remote) {
            return activeRemoteStreams.get();
        } else {
            return activeLocalStreams.get();
        }
    }

    boolean noActiveStreams() {
        return activeRemoteStreams.get() + activeLocalStreams.get() == 0;
    }

    void acceptStream(
            int streamId, boolean remoteSideClosed, boolean localSideClosed,
            int streamSendWindowSize, int streamReceiveWindowSize, boolean remote) {
        StreamState state = null;
        if (!remoteSideClosed || !localSideClosed) {
            state = new StreamState(
                    remoteSideClosed, localSideClosed, streamSendWindowSize, streamReceiveWindowSize);
        }
        Node node = new Node(state);
        node.parent = streams.get(HTTP_CONNECTION_STREAM_ID);
        streams.put(streamId, node);
        if (state != null) {
            if (remote) {
                activeRemoteStreams.incrementAndGet();
            } else {
                activeLocalStreams.incrementAndGet();
            }
        }
    }

    private StreamState removeActiveStream(int streamId, boolean remote) {
        Node stream = streams.remove(streamId);
        if (stream != null && stream.state != null) {
            StreamState state = stream.state;
            stream.close();
            if (remote) {
                activeRemoteStreams.decrementAndGet();
            } else {
                activeLocalStreams.decrementAndGet();
            }
            return state;
        }
        return null;
    }

    void removeStream(int streamId, boolean remote) {
        StreamState state = removeActiveStream(streamId, remote);
        if (state != null) {
            state.clearPendingWrites(STREAM_CLOSED);
        }
    }

    boolean isRemoteSideClosed(int streamId) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state == null || state.isRemoteSideClosed();
    }

    void closeRemoteSide(int streamId, boolean remote) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        if (state != null) {
            state.closeRemoteSide();
            if (state.isLocalSideClosed()) {
                removeActiveStream(streamId, remote);
            }
        }
    }

    boolean isLocalSideClosed(int streamId) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state == null || state.isLocalSideClosed();
    }

    void closeLocalSide(int streamId, boolean remote) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        if (state != null) {
            state.closeLocalSide();
            if (state.isRemoteSideClosed()) {
                removeActiveStream(streamId, remote);
            }
        }
    }

    int getSendWindowSize(int streamId) {
        if (streamId == HTTP_CONNECTION_STREAM_ID) {
            return sendWindowSize.get();
        }

        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null ? state.getSendWindowSize() : -1;
    }

    int updateSendWindowSize(int streamId, int deltaWindowSize) {
        if (streamId == HTTP_CONNECTION_STREAM_ID) {
            return sendWindowSize.addAndGet(deltaWindowSize);
        }

        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null ? state.updateSendWindowSize(deltaWindowSize) : -1;
    }

    int updateReceiveWindowSize(int streamId, int deltaWindowSize) {
        if (streamId == HTTP_CONNECTION_STREAM_ID) {
            return receiveWindowSize.addAndGet(deltaWindowSize);
        }

        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        if (state == null) {
            return -1;
        }
        if (deltaWindowSize > 0) {
            state.setReceiveWindowSizeLowerBound(0);
        }
        return state.updateReceiveWindowSize(deltaWindowSize);
    }

    int getReceiveWindowSizeLowerBound(int streamId) {
        if (streamId == HTTP_CONNECTION_STREAM_ID) {
            return 0;
        }

        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null ? state.getReceiveWindowSizeLowerBound() : 0;
    }

    void updateAllSendWindowSizes(int deltaWindowSize) {
        for (Node stream : streams.values()) {
            StreamState state = stream.state;
            if (state != null) {
                state.updateSendWindowSize(deltaWindowSize);
            }
        }
    }

    void updateAllReceiveWindowSizes(int deltaWindowSize) {
        for (Node stream : streams.values()) {
            StreamState state = stream.state;
            if (state != null) {
                state.updateReceiveWindowSize(deltaWindowSize);
                if (deltaWindowSize < 0) {
                    state.setReceiveWindowSizeLowerBound(deltaWindowSize);
                }
            }
        }
    }

    boolean putPendingWrite(int streamId, PendingWrite evt) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null && state.putPendingWrite(evt);
    }

    PendingWrite getPendingWrite(int streamId) {
        if (streamId == HTTP_CONNECTION_STREAM_ID) {
            Node connection = streams.get(HTTP_CONNECTION_STREAM_ID);
            return getPendingWrite(connection);
        }

        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null ? state.getPendingWrite() : null;
    }

    PendingWrite getPendingWrite(Node node) {
        PendingWrite e = null;
        if (node.state != null && node.state.getSendWindowSize() > 0) {
            e = node.state.getPendingWrite();
        }
        if (e == null) {
            for (Node child : node.children) {
                e = getPendingWrite(child);
                if (e != null) {
                    break;
                }
            }
        }
        return e;
    }

    PendingWrite removePendingWrite(int streamId) {
        Node stream = streams.get(streamId);
        StreamState state = stream == null ? null : stream.state;
        return state != null ? state.removePendingWrite() : null;
    }

    /**
     * Set the priority of the stream.
     */
    boolean setPriority(int streamId, boolean exclusive, int dependency, int weight) {
        Node stream = streams.get(streamId);
        if (stream == null) {
            // stream closed?
            return false;
        }

        Node parent = streams.get(dependency);
        if (parent == null) {
            // garbage collected?
            stream.parent.removeDependent(stream);

            // set to default priority
            Node root = streams.get(HTTP_CONNECTION_STREAM_ID);
            root.addDependent(false, stream);
            stream.setWeight(HTTP_DEFAULT_WEIGHT);
            return false;
        }

        // check if we need to restructure the tree
        if (parent == stream.parent) {
            if (exclusive) {
                // move dependents to stream
                parent.addDependent(true, stream);
            }
        } else {
            stream.parent.removeDependent(stream);
            parent.addDependent(exclusive, stream);
        }

        stream.setWeight(weight);
        return true;
    }

    private static final class StreamState {

        private boolean remoteSideClosed;
        private boolean localSideClosed;
        private final AtomicInteger sendWindowSize;
        private final AtomicInteger receiveWindowSize;
        private int receiveWindowSizeLowerBound;
        private final ConcurrentLinkedQueue<PendingWrite> pendingWriteQueue =
                new ConcurrentLinkedQueue<PendingWrite>();

        StreamState(
                boolean remoteSideClosed, boolean localSideClosed,
                int sendWindowSize, int receiveWindowSize) {
            this.remoteSideClosed = remoteSideClosed;
            this.localSideClosed = localSideClosed;
            this.sendWindowSize = new AtomicInteger(sendWindowSize);
            this.receiveWindowSize = new AtomicInteger(receiveWindowSize);
        }

        boolean isRemoteSideClosed() {
            return remoteSideClosed;
        }

        void closeRemoteSide() {
            remoteSideClosed = true;
        }

        boolean isLocalSideClosed() {
            return localSideClosed;
        }

        void closeLocalSide() {
            localSideClosed = true;
        }

        int getSendWindowSize() {
            return sendWindowSize.get();
        }

        int updateSendWindowSize(int deltaWindowSize) {
            return sendWindowSize.addAndGet(deltaWindowSize);
        }

        int updateReceiveWindowSize(int deltaWindowSize) {
            return receiveWindowSize.addAndGet(deltaWindowSize);
        }

        int getReceiveWindowSizeLowerBound() {
            return receiveWindowSizeLowerBound;
        }

        void setReceiveWindowSizeLowerBound(int receiveWindowSizeLowerBound) {
            this.receiveWindowSizeLowerBound = receiveWindowSizeLowerBound;
        }

        boolean putPendingWrite(PendingWrite msg) {
            return pendingWriteQueue.offer(msg);
        }

        PendingWrite getPendingWrite() {
            return pendingWriteQueue.peek();
        }

        PendingWrite removePendingWrite() {
            return pendingWriteQueue.poll();
        }

        void clearPendingWrites(Throwable cause) {
            for (; ; ) {
                PendingWrite pendingWrite = pendingWriteQueue.poll();
                if (pendingWrite == null) {
                    break;
                }
                pendingWrite.fail(cause);
            }
        }
    }

    private static final class Node {

        private static final Comparator<Node> COMPARATOR = new WeightComparator();

        public Node parent; // the dependency of the stream
        public int weight;  // the weight of the dependency

        // Children should be iterator in weighted order
        public Set<Node> children = new TreeSet<Node>(COMPARATOR); // the dependents of the stream
        public int dependentWeights; // the total weight of all the dependents of the stream

        public StreamState state;

        public Node(StreamState state) {
            this.state = state;
        }

        public void close() {
            this.state = null;
        }

        public void setWeight(int weight) {
            // Remove and re-add parent to maintain comparator order
            parent.removeDependent(this);
            this.weight = weight;
            parent.addDependent(false, this);
        }

        public void addDependent(boolean exclusive, Node node) {
            removeDependent(node);
            if (exclusive) {
                for (Node child : children) {
                    node.addDependent(false, child);
                }
                children.clear();
                dependentWeights = 0;
            }
            children.add(node);
            dependentWeights += node.weight;
        }

        public void removeDependent(Node node) {
            if (children.remove(node)) {
                dependentWeights -= node.weight;
            }
        }
    }

    private static final class WeightComparator implements Comparator<Node> {
        @Override
        public int compare(Node n1, Node n2) {
            return n2.weight - n1.weight;
        }
    }

    public static final class PendingWrite {
        public final HttpDataFrame httpDataFrame;
        public final ChannelPromise promise;

        PendingWrite(HttpDataFrame httpDataFrame, ChannelPromise promise) {
            this.httpDataFrame = httpDataFrame;
            this.promise = promise;
        }

        void fail(Throwable cause) {
            // httpDataFrame.release();
            promise.setFailure(cause);
        }
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpConnectionHandler.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.ClosedChannelException;
import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.internal.EmptyArrays;

import static com.twitter.http2.HttpCodecUtil.HTTP_CONNECTION_STREAM_ID;
import static com.twitter.http2.HttpCodecUtil.isServerId;

/**
 * Manages streams within an HTTP/2 connection.
 */
public class HttpConnectionHandler extends ByteToMessageDecoder
        implements HttpFrameDecoderDelegate, ChannelOutboundHandler {

    private static final HttpProtocolException PROTOCOL_EXCEPTION =
            new HttpProtocolException();

    static {
        PROTOCOL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
    }

    private static final HttpSettingsFrame SETTINGS_ACK_FRAME =
            new DefaultHttpSettingsFrame().setAck(true);

    private static final int DEFAULT_HEADER_TABLE_SIZE = 4096;

    private static final int DEFAULT_WINDOW_SIZE = 65535;
    private int initialSendWindowSize = DEFAULT_WINDOW_SIZE;
    private int initialReceiveWindowSize = DEFAULT_WINDOW_SIZE;
    private volatile int initialConnectionReceiveWindowSize = DEFAULT_WINDOW_SIZE;

    private final HttpConnection httpConnection =
            new HttpConnection(initialSendWindowSize, initialReceiveWindowSize);
    private int lastStreamId;

    private static final int DEFAULT_MAX_CONCURRENT_STREAMS = Integer.MAX_VALUE;
    private int remoteConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;
    private int localConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;

    private boolean sentGoAwayFrame;
    private boolean receivedGoAwayFrame;

    private final ChannelFutureListener connectionErrorListener =
            new ConnectionErrorFutureListener();
    private ChannelFutureListener closingChannelFutureListener;

    private final boolean server;
    private final boolean handleStreamWindowUpdates;

    private final HttpFrameDecoder httpFrameDecoder;
    private final HttpFrameEncoder httpFrameEncoder;
    private final HttpHeaderBlockDecoder httpHeaderBlockDecoder;
    private final HttpHeaderBlockEncoder httpHeaderBlockEncoder;

    private HttpHeaderBlockFrame httpHeaderBlockFrame;
    private HttpSettingsFrame httpSettingsFrame;

    private boolean needSettingsAck;

    private boolean changeDecoderHeaderTableSize;
    private int headerTableSize;

    private boolean changeEncoderHeaderTableSize;
    private int lastHeaderTableSize = Integer.MAX_VALUE;
    private int minHeaderTableSize = Integer.MAX_VALUE;
    private boolean pushEnabled = true;

    private ChannelHandlerContext context;

    /**
     * Creates a new connection handler.
     *
     * @param server {@code true} if and only if this connection handler should
     *               handle the server endpoint of the connection.
     *               {@code false} if and only if this connection handler should
     *               handle the client endpoint of the connection.
     */
    public HttpConnectionHandler(boolean server) {
        this(server, true);
    }

    /**
     * Creates a new connection handler with the specified options.
     */
    public HttpConnectionHandler(boolean server, boolean handleStreamWindowUpdates) {
        this(server, handleStreamWindowUpdates, 8192, 16384);
    }

    /**
     * Creates a new connection handler with the specified options.
     */
    public HttpConnectionHandler(boolean server, int maxChunkSize, int maxHeaderSize) {
        this(server, true, maxChunkSize, maxHeaderSize);
    }

    /**
     * Creates a new connection handler with the specified options.
     */
    public HttpConnectionHandler(
            boolean server, boolean handleStreamWindowUpdates, int maxChunkSize, int maxHeaderSize) {
        this.server = server;
        this.handleStreamWindowUpdates = handleStreamWindowUpdates;
        httpFrameDecoder = new HttpFrameDecoder(server, this, maxChunkSize);
        httpFrameEncoder = new HttpFrameEncoder();
        httpHeaderBlockDecoder = new HttpHeaderBlockDecoder(maxHeaderSize, DEFAULT_HEADER_TABLE_SIZE);
        httpHeaderBlockEncoder = new HttpHeaderBlockEncoder(DEFAULT_HEADER_TABLE_SIZE);
    }

    public void setConnectionReceiveWindowSize(int connectionReceiveWindowSize) {
      if (connectionReceiveWindowSize < 0) {
          throw new IllegalArgumentException("connectionReceiveWindowSize");
      }
      // This will not send a window update frame immediately.
      // If this value increases the allowed receive window size,
      // a WINDOW_UPDATE frame will be sent when only half of the
      // session window size remains during data frame processing.
      // If this value decreases the allowed receive window size,
      // the window will be reduced as data frames are processed.
      initialConnectionReceiveWindowSize = connectionReceiveWindowSize;
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        super.handlerAdded(ctx);
        context = ctx;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        httpFrameDecoder.decode(in);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readDataFramePadding(int streamId, boolean endStream, int padding) {
        int deltaWindowSize = -1 * padding;
        int newConnectionWindowSize = httpConnection.updateReceiveWindowSize(
                HTTP_CONNECTION_STREAM_ID, deltaWindowSize);

        // Check if connection window size is reduced beyond allowable lower bound
        if (newConnectionWindowSize < 0) {
            issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
            return;
        }

        // Send a WINDOW_UPDATE frame if less than half the connection window size remains
        if (newConnectionWindowSize <= initialConnectionReceiveWindowSize / 2) {
            int windowSizeIncrement = initialConnectionReceiveWindowSize - newConnectionWindowSize;
            httpConnection.updateReceiveWindowSize(HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
            ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(
                    HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
            context.writeAndFlush(frame);
        }

        // Check if we received a DATA frame for a stream which is half-closed (remote) or closed
        if (httpConnection.isRemoteSideClosed(streamId)) {
            if (streamId <= lastStreamId) {
                issueStreamError(streamId, HttpErrorCode.STREAM_CLOSED);
            } else if (!sentGoAwayFrame) {
                issueStreamError(streamId, HttpErrorCode.PROTOCOL_ERROR);
            }
            return;
        }

        // Update receive window size
        int newWindowSize = httpConnection.updateReceiveWindowSize(streamId, deltaWindowSize);

        // Window size can become negative if we sent a SETTINGS frame that reduces the
        // size of the transfer window after the peer has written data frames.
        // The value is bounded by the length that SETTINGS frame decrease the window.
        // This difference is stored for the connection when writing the SETTINGS frame
        // and is cleared once we send a WINDOW_UPDATE frame.
        if (newWindowSize < httpConnection.getReceiveWindowSizeLowerBound(streamId)) {
            issueStreamError(streamId, HttpErrorCode.FLOW_CONTROL_ERROR);
            return;
        }

        // Send a WINDOW_UPDATE frame if less than half the stream window size remains
        // Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
        if (handleStreamWindowUpdates && newWindowSize <= initialReceiveWindowSize / 2 && !endStream) {
            int windowSizeIncrement = initialReceiveWindowSize - newWindowSize;
            httpConnection.updateReceiveWindowSize(streamId, windowSizeIncrement);
            ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(streamId, windowSizeIncrement);
            context.writeAndFlush(frame);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readDataFrame(int streamId, boolean endStream, boolean endSegment, ByteBuf data) {
        // HTTP/2 DATA frame processing requirements:
        //
        // If an endpoint receives a data frame for a Stream-ID which is not open
        // and the endpoint has not sent a GOAWAY frame, it must issue a stream error
        // with the error code INVALID_STREAM for the Stream-ID.
        //
        // If an endpoint receives multiple data frames for invalid Stream-IDs,
        // it may close the connection.
        //
        // If an endpoint refuses a stream it must ignore any data frames for that stream.
        //
        // If an endpoint receives a data frame after the stream is half-closed (remote)
        // or closed, it must respond with a stream error of type STREAM_CLOSED.

        int deltaWindowSize = -1 * data.readableBytes();
        int newConnectionWindowSize = httpConnection.updateReceiveWindowSize(
                HTTP_CONNECTION_STREAM_ID, deltaWindowSize);

        // Check if connection window size is reduced beyond allowable lower bound
        if (newConnectionWindowSize < 0) {
            issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
            return;
        }

        // Send a WINDOW_UPDATE frame if less than half the connection window size remains
        if (newConnectionWindowSize <= initialConnectionReceiveWindowSize / 2) {
            int windowSizeIncrement = initialConnectionReceiveWindowSize - newConnectionWindowSize;
            httpConnection.updateReceiveWindowSize(HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
            ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(
                    HTTP_CONNECTION_STREAM_ID, windowSizeIncrement);
            context.writeAndFlush(frame);
        }

        // Check if we received a DATA frame for a stream which is half-closed (remote) or closed
        if (httpConnection.isRemoteSideClosed(streamId)) {
            if (streamId <= lastStreamId) {
                issueStreamError(streamId, HttpErrorCode.STREAM_CLOSED);
            } else if (!sentGoAwayFrame) {
                issueStreamError(streamId, HttpErrorCode.PROTOCOL_ERROR);
            }
            return;
        }

        // Update receive window size
        int newWindowSize = httpConnection.updateReceiveWindowSize(streamId, deltaWindowSize);

        // Window size can become negative if we sent a SETTINGS frame that reduces the
        // size of the transfer window after the peer has written data frames.
        // The value is bounded by the length that SETTINGS frame decrease the window.
        // This difference is stored for the connection when writing the SETTINGS frame
        // and is cleared once we send a WINDOW_UPDATE frame.
        if (newWindowSize < httpConnection.getReceiveWindowSizeLowerBound(streamId)) {
            issueStreamError(streamId, HttpErrorCode.FLOW_CONTROL_ERROR);
            return;
        }

        // Window size became negative due to sender writing frame before receiving SETTINGS
        // Send data frames upstream in initialReceiveWindowSize chunks
        if (newWindowSize < 0) {
            while (data.readableBytes() > initialReceiveWindowSize) {
                ByteBuf partialData = data.readBytes(initialReceiveWindowSize);
                HttpDataFrame partialDataFrame = new DefaultHttpDataFrame(streamId, partialData);
                context.fireChannelRead(partialDataFrame);
            }
        }

        // Send a WINDOW_UPDATE frame if less than half the stream window size remains
        // Recipient should not send a WINDOW_UPDATE frame as it consumes the last data frame.
        if (handleStreamWindowUpdates && newWindowSize <= initialReceiveWindowSize / 2 && !endStream) {
            int windowSizeIncrement = initialReceiveWindowSize - newWindowSize;
            httpConnection.updateReceiveWindowSize(streamId, windowSizeIncrement);
            ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(streamId, windowSizeIncrement);
            context.writeAndFlush(frame);
        }

        // Close the remote side of the stream if this is the last frame
        if (endStream) {
            halfCloseStream(streamId, true, context.channel().newSucceededFuture());
        }

        HttpDataFrame httpDataFrame = new DefaultHttpDataFrame(streamId, data);
        httpDataFrame.setLast(endStream);
        context.fireChannelRead(httpDataFrame);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readHeadersFrame(
            int streamId,
            boolean endStream,
            boolean endSegment,
            boolean exclusive,
            int dependency,
            int weight
    ) {
        // HTTP/2 HEADERS frame processing requirements:
        //
        // If an endpoint receives a HEADERS frame with a Stream-ID that is less than
        // any previously received HEADERS, it must issue a connection error of type
        // PROTOCOL_ERROR.
        //
        // If an endpoint receives multiple SYN_STREAM frames with the same active
        // Stream-ID, it must issue a stream error with the status code PROTOCOL_ERROR.
        //
        // The recipient can reject a stream by sending a stream error with the
        // status code REFUSED_STREAM.

        if (isRemoteInitiatedId(streamId)) {
            if (streamId <= lastStreamId) {
                // Check if we received a HEADERS frame for a stream which is half-closed (remote) or closed
                if (httpConnection.isRemoteSideClosed(streamId)) {
                    issueStreamError(streamId, HttpErrorCode.STREAM_CLOSED);
                    return;
                }
            } else {
                // Try to accept the stream
                if (!acceptStream(streamId, exclusive, dependency, weight)) {
                    issueStreamError(streamId, HttpErrorCode.REFUSED_STREAM);
                    return;
                }
            }
        } else {
            // Check if we received a HEADERS frame for a stream which is half-closed (remote) or closed
            if (httpConnection.isRemoteSideClosed(streamId)) {
                issueStreamError(streamId, HttpErrorCode.STREAM_CLOSED);
                return;
            }
        }

        // Close the remote side of the stream if this is the last frame
        if (endStream) {
            halfCloseStream(streamId, true, context.channel().newSucceededFuture());
        }

        HttpHeadersFrame httpHeadersFrame = new DefaultHttpHeadersFrame(streamId);
        httpHeadersFrame.setLast(endStream);
        httpHeadersFrame.setExclusive(exclusive);
        httpHeadersFrame.setDependency(dependency);
        httpHeadersFrame.setWeight(weight);
        httpHeaderBlockFrame = httpHeadersFrame;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readPriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
        if (streamId == dependency) {
            issueStreamError(streamId, HttpErrorCode.PROTOCOL_ERROR);
        } else {
            setPriority(streamId, exclusive, dependency, weight);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readRstStreamFrame(int streamId, int errorCode) {
        // If a RST_STREAM frame identifying an idle stream is received,
        // the recipient MUST treat this as a connection error of type
        // PROTOCOL_ERROR.
        removeStream(streamId, context.channel().newSucceededFuture());
        HttpRstStreamFrame httpRstStreamFrame = new DefaultHttpRstStreamFrame(streamId, errorCode);
        context.fireChannelRead(httpRstStreamFrame);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readSettingsFrame(boolean ack) {
        needSettingsAck = !ack;
        httpSettingsFrame = new DefaultHttpSettingsFrame();
        httpSettingsFrame.setAck(ack);
        if (ack && changeDecoderHeaderTableSize) {
            httpHeaderBlockDecoder.setMaxHeaderTableSize(headerTableSize);
            changeDecoderHeaderTableSize = false;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readSetting(int id, int value) {
        httpSettingsFrame.setValue(id, value);
        switch (id) {
        case HttpSettingsFrame.SETTINGS_HEADER_TABLE_SIZE:
            // Ignore 'negative' values -- they are too large for java
            if (value >= 0) {
                changeEncoderHeaderTableSize = true;
                lastHeaderTableSize = value;
                if (lastHeaderTableSize < minHeaderTableSize) {
                    minHeaderTableSize = lastHeaderTableSize;
                }
            }
            break;
        case HttpSettingsFrame.SETTINGS_ENABLE_PUSH:
            if (value == 0) {
                pushEnabled = false;
            } else if (value == 1) {
                pushEnabled = true;
            } else {
                issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
            }
            break;
        case HttpSettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS:
            if (value >= 0) {
                remoteConcurrentStreams = value;
            }
            break;
        case HttpSettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE:
            if (value >= 0) {
                updateInitialSendWindowSize(value);
            } else {
                issueConnectionError(HttpErrorCode.FLOW_CONTROL_ERROR);
            }
            break;
        case HttpSettingsFrame.SETTINGS_MAX_FRAME_SIZE:
            if (value != HttpCodecUtil.HTTP_MAX_LENGTH) {
                issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
            }
            break;
        default:
            // Ignore Unknown Settings
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readSettingsEnd() {
        if (changeEncoderHeaderTableSize) {
            synchronized (httpHeaderBlockEncoder) {
                httpHeaderBlockEncoder.setDecoderMaxHeaderTableSize(minHeaderTableSize);
                httpHeaderBlockEncoder.setDecoderMaxHeaderTableSize(lastHeaderTableSize);

                // Writes of settings ack must occur in order
                ByteBuf frame = httpFrameEncoder.encodeSettingsFrame(SETTINGS_ACK_FRAME);
                context.writeAndFlush(frame);
            }
            changeEncoderHeaderTableSize = false;
            lastHeaderTableSize = Integer.MAX_VALUE;
            minHeaderTableSize = Integer.MAX_VALUE;
        } else if (needSettingsAck) {
            ByteBuf frame = httpFrameEncoder.encodeSettingsFrame(SETTINGS_ACK_FRAME);
            context.writeAndFlush(frame);
        }
        Object frame = httpSettingsFrame;
        httpSettingsFrame = null;
        context.fireChannelRead(frame);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readPushPromiseFrame(int streamId, int promisedStreamId) {
        // TODO(jpinner) handle push promise frames
        // Any we receive must be associated with a "peer-initiated" stream.
        // Since we don't have a way currently to initiate streams, any
        // frame that we receive must be treated as a protocol error.
        issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readPingFrame(long data, boolean ack) {
        // HTTP/2 PING frame processing requirements:
        //
        // Receivers of a PING frame should send an identical frame to the sender
        // as soon as possible.
        //
        // Receivers of a PING frame must ignore frames that it did not initiate
        HttpPingFrame httpPingFrame = new DefaultHttpPingFrame(data);
        httpPingFrame.setPong(true);
        if (ack) {
            context.fireChannelRead(httpPingFrame);
        } else {
            ByteBuf frame = httpFrameEncoder.encodePingFrame(data, false);
            context.writeAndFlush(frame);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readGoAwayFrame(int lastStreamId, int errorCode) {
        receivedGoAwayFrame = true;
        HttpGoAwayFrame httpGoAwayFrame = new DefaultHttpGoAwayFrame(lastStreamId, errorCode);
        context.fireChannelRead(httpGoAwayFrame);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readWindowUpdateFrame(int streamId, int windowSizeIncrement) {
        // HTTP/2 WINDOW_UPDATE frame processing requirements:
        //
        // Receivers of a WINDOW_UPDATE that cause the window size to exceed 2^31
        // must send a RST_STREAM with the status code FLOW_CONTROL_ERROR.
        //
        // Sender should ignore all WINDOW_UPDATE frames associated with a stream
        // after sending the last frame for the stream.

        // Ignore frames for half-closed streams
        if (streamId != HTTP_CONNECTION_STREAM_ID && httpConnection.isLocalSideClosed(streamId)) {
            return;
        }

        // Check for numerical overflow
        if (httpConnection.getSendWindowSize(streamId) > Integer.MAX_VALUE - windowSizeIncrement) {
            if (streamId == HTTP_CONNECTION_STREAM_ID) {
                issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
            } else {
                issueStreamError(streamId, HttpErrorCode.FLOW_CONTROL_ERROR);
            }
            return;
        }

        updateSendWindowSize(context, streamId, windowSizeIncrement);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readHeaderBlock(ByteBuf headerBlockFragment) {
        try {
            httpHeaderBlockDecoder.decode(headerBlockFragment, httpHeaderBlockFrame);
        } catch (IOException e) {
            httpHeaderBlockFrame = null;
            issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readHeaderBlockEnd() {
        httpHeaderBlockDecoder.endHeaderBlock(httpHeaderBlockFrame);

        if (httpHeaderBlockFrame == null) {
            return;
        }

        // Check if we received a valid Header Block
        if (httpHeaderBlockFrame.isInvalid()) {
            issueStreamError(httpHeaderBlockFrame.getStreamId(), HttpErrorCode.PROTOCOL_ERROR);
            return;
        }

        Object frame = httpHeaderBlockFrame;
        httpHeaderBlockFrame = null;
        context.fireChannelRead(frame);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readFrameError(String message) {
        issueConnectionError(HttpErrorCode.PROTOCOL_ERROR);
    }

    @Override
    public void bind(
            ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
            throws Exception {
        ctx.bind(localAddress, promise);
    }

    @Override
    public void connect(
            ChannelHandlerContext ctx,
            SocketAddress remoteAddress,
            SocketAddress localAddress,
            ChannelPromise promise
    ) throws Exception {
        ctx.connect(remoteAddress, localAddress, promise);
    }

    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        // HTTP/2 connection requirements:
        //
        // When either endpoint closes the transport-level connection,
        // it must first send a GOAWAY frame.
        //
        // Avoid NotYetConnectedException
        if (!ctx.channel().isActive()) {
            ctx.disconnect(promise);
        } else {
            sendGoAwayFrame(ctx, promise);
        }
    }

    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        // HTTP/2 connection requirements:
        //
        // When either endpoint closes the transport-level connection,
        // it must first send a GOAWAY frame.
        //
        // Avoid NotYetConnectedException
        if (!ctx.channel().isActive()) {
            ctx.close(promise);
        } else {
            sendGoAwayFrame(ctx, promise);
        }
    }

    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        ctx.deregister(promise);
    }

    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
        ctx.read();
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
            throws Exception {
        if (msg instanceof HttpDataFrame) {

            HttpDataFrame httpDataFrame = (HttpDataFrame) msg;
            int streamId = httpDataFrame.getStreamId();

            // Frames must not be sent on half-closed streams
            if (httpConnection.isLocalSideClosed(streamId)) {
                promise.setFailure(PROTOCOL_EXCEPTION);
                return;
            }

            // HTTP/2 DATA frame flow control processing requirements:
            //
            // Sender must not send a data frame with data length greater
            // than the transfer window size.
            //
            // After sending each data frame, the sender decrements its
            // transfer window size by the amount of data transmitted.
            //
            // When the window size becomes less than or equal to 0, the
            // sender must pause transmitting data frames.

            int dataLength = httpDataFrame.content().readableBytes();
            int sendWindowSize = httpConnection.getSendWindowSize(streamId);
            int connectionSendWindowSize = httpConnection.getSendWindowSize(
                    HTTP_CONNECTION_STREAM_ID);
            sendWindowSize = Math.min(sendWindowSize, connectionSendWindowSize);

            if (sendWindowSize <= 0) {
                // Stream is stalled -- enqueue Data frame and return
                httpConnection.putPendingWrite(
                        streamId, new HttpConnection.PendingWrite(httpDataFrame, promise));
                return;
            } else if (sendWindowSize < dataLength) {
                // Stream is not stalled but we cannot send the entire frame
                httpConnection.updateSendWindowSize(streamId, -1 * sendWindowSize);
                httpConnection.updateSendWindowSize(HTTP_CONNECTION_STREAM_ID, -1 * sendWindowSize);

                // Create a partial data frame whose length is the current window size
                ByteBuf data = httpDataFrame.content().readSlice(sendWindowSize).retain();
                ByteBuf partialDataFrame = httpFrameEncoder.encodeDataFrame(streamId, false, data);

                // Enqueue the remaining data (will be the first frame queued)
                httpConnection.putPendingWrite(
                        streamId, new HttpConnection.PendingWrite(httpDataFrame, promise));

                ChannelPromise writeFuture = ctx.channel().newPromise();

                // The transfer window size is pre-decremented when sending a data frame downstream.
                // Close the connection on write failures that leaves the transfer window in a corrupt state.
                writeFuture.addListener(connectionErrorListener);

                ctx.write(partialDataFrame, writeFuture);
                return;
            } else {
                // Window size is large enough to send entire data frame
                httpConnection.updateSendWindowSize(streamId, -1 * dataLength);
                httpConnection.updateSendWindowSize(HTTP_CONNECTION_STREAM_ID, -1 * dataLength);

                // The transfer window size is pre-decremented when sending a data frame downstream.
                // Close the connection on write failures that leaves the transfer window in a corrupt state.
                promise.addListener(connectionErrorListener);
            }

            // Close the local side of the stream if this is the last frame
            if (httpDataFrame.isLast()) {
                halfCloseStream(streamId, false, promise);
            }

            ByteBuf frame = httpFrameEncoder.encodeDataFrame(
                    streamId,
                    httpDataFrame.isLast(),
                    httpDataFrame.content()
            );
            ctx.write(frame, promise);

        } else if (msg instanceof HttpHeadersFrame) {

            HttpHeadersFrame httpHeadersFrame = (HttpHeadersFrame) msg;
            int streamId = httpHeadersFrame.getStreamId();

            if (isRemoteInitiatedId(streamId)) {
                if (streamId <= lastStreamId) {
                    // Attempting to send headers for an older stream
                    // (older than the latest accepted remote initiated stream)
                    // Ensure that the frames are not sent on a half-closed (local) or closed streams
                    if (httpConnection.isLocalSideClosed(streamId)) {
                        promise.setFailure(PROTOCOL_EXCEPTION);
                        return;
                    }
                } else {
                    // If we are attempting to write to a remote initiated stream id which is greater than the latest
                    // accepted stream Id then we must throw a protocol exception! i.e we cannot write on a remote
                    // initiated stream which we have not accepted before
                    promise.setFailure(PROTOCOL_EXCEPTION);
                    return;
                }
            } else {
                // This is a locally initiated stream (Push)
                boolean exclusive = httpHeadersFrame.isExclusive();
                int dependency = httpHeadersFrame.getDependency();
                int weight = httpHeadersFrame.getWeight();
                if (!acceptStream(streamId, exclusive, dependency, weight)) {
                    promise.setFailure(PROTOCOL_EXCEPTION);
                    return;
                }
            }

            // Close the local side of the stream if this is the last frame
            if (httpHeadersFrame.isLast()) {
                halfCloseStream(streamId, false, promise);
            }

            synchronized (httpHeaderBlockEncoder) {
                ByteBuf frame = httpFrameEncoder.encodeHeadersFrame(
                        httpHeadersFrame.getStreamId(),
                        httpHeadersFrame.isLast(),
                        httpHeadersFrame.isExclusive(),
                        httpHeadersFrame.getDependency(),
                        httpHeadersFrame.getWeight(),
                        httpHeaderBlockEncoder.encode(ctx, httpHeadersFrame)
                );
                // Writes of compressed data must occur in order
                ctx.write(frame, promise);
            }

        } else if (msg instanceof HttpPriorityFrame) {

            HttpPriorityFrame httpPriorityFrame = (HttpPriorityFrame) msg;
            int streamId = httpPriorityFrame.getStreamId();
            boolean exclusive = httpPriorityFrame.isExclusive();
            int dependency = httpPriorityFrame.getDependency();
            int weight = httpPriorityFrame.getWeight();
            setPriority(streamId, exclusive, dependency, weight);
            ByteBuf frame = httpFrameEncoder.encodePriorityFrame(
                    streamId,
                    exclusive,
                    dependency,
                    weight
            );
            ctx.write(frame, promise);

        } else if (msg instanceof HttpRstStreamFrame) {

            HttpRstStreamFrame httpRstStreamFrame = (HttpRstStreamFrame) msg;
            removeStream(httpRstStreamFrame.getStreamId(), promise);
            ByteBuf frame = httpFrameEncoder.encodeRstStreamFrame(
                    httpRstStreamFrame.getStreamId(),
                    httpRstStreamFrame.getErrorCode().getCode());
            ctx.write(frame, promise);

        } else if (msg instanceof HttpSettingsFrame) {

            // TODO(jpinner) currently cannot have more than one settings frame outstanding at a time

            HttpSettingsFrame httpSettingsFrame = (HttpSettingsFrame) msg;
            if (httpSettingsFrame.isAck()) {
                // Cannot send an acknowledgement frame
                promise.setFailure(PROTOCOL_EXCEPTION);
                return;
            }

            int newHeaderTableSize =
                    httpSettingsFrame.getValue(HttpSettingsFrame.SETTINGS_HEADER_TABLE_SIZE);
            if (newHeaderTableSize >= 0) {
                headerTableSize = newHeaderTableSize;
                changeDecoderHeaderTableSize = true;
            }

            int newConcurrentStreams =
                    httpSettingsFrame.getValue(HttpSettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
            if (newConcurrentStreams >= 0) {
                localConcurrentStreams = newConcurrentStreams;
            }

            int newInitialWindowSize =
                    httpSettingsFrame.getValue(HttpSettingsFrame.SETTINGS_INITIAL_WINDOW_SIZE);
            if (newInitialWindowSize >= 0) {
                updateInitialReceiveWindowSize(newInitialWindowSize);
            }

            ByteBuf frame = httpFrameEncoder.encodeSettingsFrame(httpSettingsFrame);
            ctx.write(frame, promise);

        } else if (msg instanceof HttpPushPromiseFrame) {

            if (!pushEnabled) {
                promise.setFailure(PROTOCOL_EXCEPTION);
                return;
            }

            synchronized (httpHeaderBlockEncoder) {
              HttpPushPromiseFrame httpPushPromiseFrame = (HttpPushPromiseFrame) msg;
              ByteBuf frame = httpFrameEncoder.encodePushPromiseFrame(
                  httpPushPromiseFrame.getStreamId(),
                  httpPushPromiseFrame.getPromisedStreamId(),
                  httpHeaderBlockEncoder.encode(ctx, httpPushPromiseFrame)
                  );
              // Writes of compressed data must occur in order
              ctx.write(frame, promise);
            }

        } else if (msg instanceof HttpPingFrame) {

            HttpPingFrame httpPingFrame = (HttpPingFrame) msg;
            if (httpPingFrame.isPong()) {
                // Cannot send a PONG frame
                promise.setFailure(PROTOCOL_EXCEPTION);
            } else {
                ByteBuf frame = httpFrameEncoder.encodePingFrame(httpPingFrame.getData(), false);
                ctx.write(frame, promise);
            }

        } else if (msg instanceof HttpGoAwayFrame) {

            // Why is this being sent? Intercept it and fail the write.
            // Should have sent a CLOSE ChannelStateEvent
            promise.setFailure(PROTOCOL_EXCEPTION);

        } else if (msg instanceof HttpWindowUpdateFrame) {

            HttpWindowUpdateFrame httpWindowUpdateFrame = (HttpWindowUpdateFrame) msg;
            int streamId = httpWindowUpdateFrame.getStreamId();

            if (handleStreamWindowUpdates || streamId == HTTP_CONNECTION_STREAM_ID) {
                // Why is this being sent? Intercept it and fail the write.
                promise.setFailure(PROTOCOL_EXCEPTION);
            } else {
                int windowSizeIncrement = httpWindowUpdateFrame.getWindowSizeIncrement();
                httpConnection.updateReceiveWindowSize(streamId, windowSizeIncrement);
                ByteBuf frame = httpFrameEncoder.encodeWindowUpdateFrame(streamId, windowSizeIncrement);
                ctx.write(frame, promise);
            }

        } else {

            ctx.write(msg, promise);
        }
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    // HTTP/2 Connection Error Handling:
    //
    // When a connection error occurs, the endpoint encountering the error must first
    // send a GOAWAY frame with the stream identifier of the most recently received stream
    // from the remote endpoint, and the error code for why the connection is terminating.
    //
    // After sending the GOAWAY frame, the endpoint must close the TCP connection.
    private void issueConnectionError(HttpErrorCode status) {
        ChannelFuture future = sendGoAwayFrame(status);
        future.addListener(ChannelFutureListener.CLOSE);
    }

    // Http/2 Stream Error Handling:
    //
    // Upon a stream error, the endpoint must send a RST_STREAM frame which contains
    // the Stream-ID for the stream where the error occurred and the error status which
    // caused the error.
    //
    // After sending the RST_STREAM, the stream is closed to the sending endpoint.
    //
    // Note: this is only called by the worker thread
    private void issueStreamError(int streamId, HttpErrorCode errorCode) {

        boolean fireMessageReceived = !httpConnection.isRemoteSideClosed(streamId);
        ChannelPromise promise = context.channel().newPromise();
        removeStream(streamId, promise);

        ByteBuf frame = httpFrameEncoder.encodeRstStreamFrame(streamId, errorCode.getCode());
        context.writeAndFlush(frame, promise);
        if (fireMessageReceived) {
            HttpRstStreamFrame httpRstStreamFrame = new DefaultHttpRstStreamFrame(streamId, errorCode);
            context.fireChannelRead(httpRstStreamFrame);
        }
    }

    // Helper functions

    private boolean isRemoteInitiatedId(int id) {
        boolean serverId = isServerId(id);
        return server && !serverId || !server && serverId;
    }

    // need to synchronize to prevent new streams from being created while updating active streams
    private synchronized void updateInitialSendWindowSize(int newInitialWindowSize) {
        int deltaWindowSize = newInitialWindowSize - initialSendWindowSize;
        initialSendWindowSize = newInitialWindowSize;
        httpConnection.updateAllSendWindowSizes(deltaWindowSize);
    }

    // need to synchronize to prevent new streams from being created while updating active streams
    private synchronized void updateInitialReceiveWindowSize(int newInitialWindowSize) {
        int deltaWindowSize = newInitialWindowSize - initialReceiveWindowSize;
        initialReceiveWindowSize = newInitialWindowSize;
        httpConnection.updateAllReceiveWindowSizes(deltaWindowSize);
    }

    // need to synchronize accesses to sentGoAwayFrame, lastStreamId, and initial window sizes
    private synchronized boolean acceptStream(
            int streamId, boolean exclusive, int dependency, int weight) {
        // Cannot initiate any new streams after receiving or sending GOAWAY
        if (receivedGoAwayFrame || sentGoAwayFrame) {
            return false;
        }

        boolean remote = isRemoteInitiatedId(streamId);
        int maxConcurrentStreams = remote ? localConcurrentStreams : remoteConcurrentStreams;
        if (httpConnection.numActiveStreams(remote) >= maxConcurrentStreams) {
            return false;
        }
        httpConnection.acceptStream(
                streamId, false, false, initialSendWindowSize, initialReceiveWindowSize, remote);
        if (remote) {
            lastStreamId = streamId;
        }
        setPriority(streamId, exclusive, dependency, weight);
        return true;
    }

    private synchronized boolean setPriority(
            int streamId, boolean exclusive, int dependency, int weight) {
        return httpConnection.setPriority(streamId, exclusive, dependency, weight);
    }

    private void halfCloseStream(int streamId, boolean remote, ChannelFuture future) {
        if (remote) {
            httpConnection.closeRemoteSide(streamId, isRemoteInitiatedId(streamId));
        } else {
            httpConnection.closeLocalSide(streamId, isRemoteInitiatedId(streamId));
        }
        if (closingChannelFutureListener != null && httpConnection.noActiveStreams()) {
            future.addListener(closingChannelFutureListener);
        }
    }

    private void removeStream(int streamId, ChannelFuture future) {
        httpConnection.removeStream(streamId, isRemoteInitiatedId(streamId));
        if (closingChannelFutureListener != null && httpConnection.noActiveStreams()) {
            future.addListener(closingChannelFutureListener);
        }
    }

    private void updateSendWindowSize(
            ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
        httpConnection.updateSendWindowSize(streamId, windowSizeIncrement);

        while (true) {
            // Check if we have unblocked a stalled stream
            HttpConnection.PendingWrite e = httpConnection.getPendingWrite(streamId);
            if (e == null) {
                break;
            }

            HttpDataFrame httpDataFrame = e.httpDataFrame;
            int dataFrameSize = httpDataFrame.content().readableBytes();
            int writeStreamId = httpDataFrame.getStreamId();
            int sendWindowSize = httpConnection.getSendWindowSize(writeStreamId);
            int connectionSendWindowSize = httpConnection.getSendWindowSize(
                    HTTP_CONNECTION_STREAM_ID);
            sendWindowSize = Math.min(sendWindowSize, connectionSendWindowSize);

            if (sendWindowSize <= 0) {
                return;
            } else if (sendWindowSize < dataFrameSize) {
                // We can send a partial frame
                httpConnection.updateSendWindowSize(writeStreamId, -1 * sendWindowSize);
                httpConnection.updateSendWindowSize(HTTP_CONNECTION_STREAM_ID, -1 * sendWindowSize);

                // Create a partial data frame whose length is the current window size
                ByteBuf data = httpDataFrame.content().readSlice(sendWindowSize).retain();
                ByteBuf partialDataFrame = httpFrameEncoder.encodeDataFrame(writeStreamId, false, data);

                ChannelPromise writeFuture = ctx.channel().newPromise();

                // The transfer window size is pre-decremented when sending a data frame downstream.
                // Close the connection on write failures that leaves the transfer window in a corrupt state.
                writeFuture.addListener(connectionErrorListener);

                ctx.writeAndFlush(partialDataFrame, writeFuture);
            } else {
                // Window size is large enough to send entire data frame
                httpConnection.removePendingWrite(writeStreamId);
                httpConnection.updateSendWindowSize(writeStreamId, -1 * dataFrameSize);
                httpConnection.updateSendWindowSize(HTTP_CONNECTION_STREAM_ID, -1 * dataFrameSize);

                // The transfer window size is pre-decremented when sending a data frame downstream.
                // Close the connection on write failures that leaves the transfer window in a corrupt state.
                e.promise.addListener(connectionErrorListener);

                // Close the local side of the stream if this is the last frame
                if (httpDataFrame.isLast()) {
                    halfCloseStream(writeStreamId, false, e.promise);
                }

                ByteBuf frame = httpFrameEncoder.encodeDataFrame(
                        writeStreamId,
                        httpDataFrame.isLast(),
                        httpDataFrame.content()
                );

                ctx.writeAndFlush(frame, e.promise);
            }
        }
    }

    private void sendGoAwayFrame(ChannelHandlerContext ctx, ChannelPromise promise) {
        ChannelFuture future = sendGoAwayFrame(HttpErrorCode.NO_ERROR);
        if (httpConnection.noActiveStreams()) {
            future.addListener(new ClosingChannelFutureListener(ctx, promise));
        } else {
            closingChannelFutureListener = new ClosingChannelFutureListener(ctx, promise);
        }
    }

    private synchronized ChannelFuture sendGoAwayFrame(HttpErrorCode httpErrorCode) {
        if (!sentGoAwayFrame) {
            sentGoAwayFrame = true;
            ChannelPromise promise = context.channel().newPromise();
            ByteBuf frame = httpFrameEncoder.encodeGoAwayFrame(lastStreamId, httpErrorCode.getCode());
            context.writeAndFlush(frame, promise);
            return promise;
        }
        return context.channel().newSucceededFuture();
    }

    private final class ConnectionErrorFutureListener implements ChannelFutureListener {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (!future.isSuccess()) {
                issueConnectionError(HttpErrorCode.INTERNAL_ERROR);
            }
        }
    }

    private static final class ClosingChannelFutureListener implements ChannelFutureListener {
        private final ChannelHandlerContext ctx;
        private final ChannelPromise promise;

        ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
            this.ctx = ctx;
            this.promise = promise;
        }

        public void operationComplete(ChannelFuture sentGoAwayFuture) throws Exception {
            if (!(sentGoAwayFuture.cause() instanceof ClosedChannelException)) {
                ctx.close(promise);
            } else {
                promise.setSuccess();
            }
        }
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpDataFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;

/**
 * An HTTP/2 DATA Frame
 */
public interface HttpDataFrame extends ByteBufHolder, HttpStreamFrame {

    /**
     * Returns {@code true} if this frame is the last frame to be transmitted on the stream.
     */
    boolean isLast();

    /**
     * Sets if this frame is the last frame to be transmitted on the stream.
     */
    HttpDataFrame setLast(boolean last);

    @Override
    HttpDataFrame setStreamId(int streamId);

    /**
     * Returns the data payload of this frame.  If there is no data payload
     * {@link io.netty.buffer.Unpooled#EMPTY_BUFFER} is returned.
     * <p/>
     * The data payload cannot exceed 16384 bytes.
     */
    @Override
    ByteBuf content();

    @Override
    HttpDataFrame copy();

    @Override
    HttpDataFrame duplicate();

    @Override
    HttpDataFrame retain();

    @Override
    HttpDataFrame retain(int increment);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpErrorCode.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

public class HttpErrorCode implements Comparable<HttpErrorCode> {

    /**
     * 0 No Error
     */
    public static final HttpErrorCode NO_ERROR =
            new HttpErrorCode(0, "NO_ERROR");

    /**
     * 1 Protocol Error
     */
    public static final HttpErrorCode PROTOCOL_ERROR =
            new HttpErrorCode(1, "PROTOCOL_ERROR");

    /**
     * 2 Internal Error
     */
    public static final HttpErrorCode INTERNAL_ERROR =
            new HttpErrorCode(2, "INTERNAL_ERROR");

    /**
     * 3 Flow Control Error
     */
    public static final HttpErrorCode FLOW_CONTROL_ERROR =
            new HttpErrorCode(3, "FLOW_CONTROL_ERROR");

    /**
     * 4 Settings Timeout
     */
    public static final HttpErrorCode SETTINGS_TIMEOUT =
            new HttpErrorCode(4, "SETTINGS_TIMEOUT");

    /**
     * 5 Stream Closed
     */
    public static final HttpErrorCode STREAM_CLOSED =
            new HttpErrorCode(5, "STREAM_CLOSED");

    /**
     * 6 Frame Size Error
     */
    public static final HttpErrorCode FRAME_SIZE_ERROR =
            new HttpErrorCode(6, "FRAME_SIZE_ERROR");

    /**
     * 7 Refused Stream
     */
    public static final HttpErrorCode REFUSED_STREAM =
            new HttpErrorCode(7, "REFUSED_STREAM");

    /**
     * 8 Cancel
     */
    public static final HttpErrorCode CANCEL =
            new HttpErrorCode(8, "CANCEL");

    /**
     * 9 Compression Error
     */
    public static final HttpErrorCode COMPRESSION_ERROR =
            new HttpErrorCode(9, "COMPRESSION_ERROR");

    /**
     * 10 Connect Error
     */
    public static final HttpErrorCode CONNECT_ERROR =
            new HttpErrorCode(10, "CONNECT_ERROR");

    /**
     * 11 Enhance Your Calm (420)
     */
    public static final HttpErrorCode ENHANCE_YOUR_CALM =
            new HttpErrorCode(420, "ENHANCE_YOUR_CALM");

    /**
     * 12 Inadequate Security
     */
    public static final HttpErrorCode INADEQUATE_SECURITY =
            new HttpErrorCode(12, "INADEQUATE_SECURITY");

    /**
     * Returns the {@link HttpErrorCode} represented by the specified code.
     * If the specified code is a defined HTTP error code, a cached instance
     * will be returned.  Otherwise, a new instance will be returned.
     */
    public static HttpErrorCode valueOf(int code) {
        switch (code) {
        case 0:
            return NO_ERROR;
        case 1:
            return PROTOCOL_ERROR;
        case 2:
            return INTERNAL_ERROR;
        case 3:
            return FLOW_CONTROL_ERROR;
        case 4:
            return SETTINGS_TIMEOUT;
        case 5:
            return STREAM_CLOSED;
        case 6:
            return FRAME_SIZE_ERROR;
        case 7:
            return REFUSED_STREAM;
        case 8:
            return CANCEL;
        case 9:
            return COMPRESSION_ERROR;
        case 10:
            return CONNECT_ERROR;
        case 11:
            return ENHANCE_YOUR_CALM;
        case 12:
            return INADEQUATE_SECURITY;
        case 420:
            return ENHANCE_YOUR_CALM;
        default:
            return new HttpErrorCode(code, "UNKNOWN (" + code + ')');
        }
    }

    private final int code;

    private final String statusPhrase;

    /**
     * Creates a new instance with the specified {@code code} and its
     * {@code statusPhrase}.
     */
    public HttpErrorCode(int code, String statusPhrase) {
        if (statusPhrase == null) {
            throw new NullPointerException("statusPhrase");
        }

        this.code = code;
        this.statusPhrase = statusPhrase;
    }

    /**
     * Returns the code of this status.
     */
    public int getCode() {
        return code;
    }

    /**
     * Returns the status phrase of this status.
     */
    public String getStatusPhrase() {
        return statusPhrase;
    }

    @Override
    public int hashCode() {
        return getCode();
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof HttpErrorCode)) {
            return false;
        }

        return getCode() == ((HttpErrorCode) o).getCode();
    }

    @Override
    public String toString() {
        return getStatusPhrase();
    }

    @Override
    public int compareTo(HttpErrorCode o) {
        return getCode() - o.getCode();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 Frame
 */
public interface HttpFrame {
    // Tag interface
}


================================================
FILE: src/main/java/com/twitter/http2/HttpFrameDecoder.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.nio.charset.StandardCharsets;

import io.netty.buffer.ByteBuf;

import static com.twitter.http2.HttpCodecUtil.HTTP_CONTINUATION_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_DATA_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_ACK;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_HEADERS;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_SEGMENT;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_STREAM;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_PADDED;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_PRIORITY;
import static com.twitter.http2.HttpCodecUtil.HTTP_FRAME_HEADER_SIZE;
import static com.twitter.http2.HttpCodecUtil.HTTP_GOAWAY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_HEADERS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_MAX_LENGTH;
import static com.twitter.http2.HttpCodecUtil.HTTP_PING_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PRIORITY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PUSH_PROMISE_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_RST_STREAM_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_SETTINGS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_WINDOW_UPDATE_FRAME;
import static com.twitter.http2.HttpCodecUtil.getSignedInt;
import static com.twitter.http2.HttpCodecUtil.getSignedLong;
import static com.twitter.http2.HttpCodecUtil.getUnsignedInt;
import static com.twitter.http2.HttpCodecUtil.getUnsignedMedium;
import static com.twitter.http2.HttpCodecUtil.getUnsignedShort;

/**
 * Decodes {@link ByteBuf}s into HTTP/2 Frames.
 */
public class HttpFrameDecoder {

    private static final byte[] CLIENT_CONNECTION_PREFACE =
            "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.US_ASCII);

    private final int maxChunkSize;

    private final HttpFrameDecoderDelegate delegate;

    private State state;

    // HTTP/2 frame header fields
    private int length;
    private short type;
    private byte flags;
    private int streamId;

    // HTTP/2 frame padding length
    private int paddingLength;

    private enum State {
        READ_CONNECTION_HEADER,
        READ_FRAME_HEADER,
        READ_PADDING_LENGTH,
        READ_DATA_FRAME,
        READ_DATA,
        READ_HEADERS_FRAME,
        READ_PRIORITY_FRAME,
        READ_RST_STREAM_FRAME,
        READ_SETTINGS_FRAME,
        READ_SETTING,
        READ_PUSH_PROMISE_FRAME,
        READ_PING_FRAME,
        READ_GOAWAY_FRAME,
        READ_WINDOW_UPDATE_FRAME,
        READ_CONTINUATION_FRAME_HEADER,
        READ_HEADER_BLOCK,
        SKIP_FRAME_PADDING,
        SKIP_FRAME_PADDING_CONTINUATION,
        FRAME_ERROR
    }

    /**
     * Creates a new instance with the specified @{code HttpFrameDecoderDelegate}
     * and the default {@code maxChunkSize (8192)}.
     */
    public HttpFrameDecoder(boolean server, HttpFrameDecoderDelegate delegate) {
        this(server, delegate, 8192);
    }

    /**
     * Creates a new instance with the specified parameters.
     */
    public HttpFrameDecoder(boolean server, HttpFrameDecoderDelegate delegate, int maxChunkSize) {
        if (delegate == null) {
            throw new NullPointerException("delegate");
        }
        if (maxChunkSize <= 0) {
            throw new IllegalArgumentException(
                    "maxChunkSize must be a positive integer: " + maxChunkSize);
        }
        this.delegate = delegate;
        this.maxChunkSize = maxChunkSize;
        if (server) {
            state = State.READ_CONNECTION_HEADER;
        } else {
            state = State.READ_FRAME_HEADER;
        }
    }

    /**
     * Decode the byte buffer.
     */
    public void decode(ByteBuf buffer) {
        boolean endStream;
        boolean endSegment;
        int minLength;
        int dependency;
        int weight;
        boolean exclusive;
        int errorCode;

        while (true) {
            switch (state) {
            case READ_CONNECTION_HEADER:
                while (buffer.isReadable()) {
                    byte b = buffer.readByte();
                    if (b != CLIENT_CONNECTION_PREFACE[length++]) {
                        state = State.FRAME_ERROR;
                        delegate.readFrameError("Invalid Connection Header");
                        return;
                    }

                    if (length == CLIENT_CONNECTION_PREFACE.length) {
                        state = State.READ_FRAME_HEADER;
                        break;
                    }
                }
                if (buffer.isReadable()) {
                    break;
                } else {
                    return;
                }

            case READ_FRAME_HEADER:
                // Wait until entire header is readable
                if (buffer.readableBytes() < HTTP_FRAME_HEADER_SIZE) {
                    return;
                }

                // Read frame header fields
                readFrameHeader(buffer);

                // TODO(jpinner) Sec 4.2 FRAME_SIZE_ERROR

                if (!isValidFrameHeader(length, type, flags, streamId)) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Frame Header");
                } else if (frameHasPadding(type, flags)) {
                    state = State.READ_PADDING_LENGTH;
                } else {
                    paddingLength = 0;
                    state = getNextState(length, type);
                }
                break;

            case READ_PADDING_LENGTH:
                if (buffer.readableBytes() < 1) {
                    return;
                }

                paddingLength = buffer.readUnsignedByte();
                --length;

                if (!isValidPaddingLength(length, type, flags, paddingLength)) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Frame Padding Length");
                } else {
                    state = getNextState(length, type);
                }
                break;

            case READ_DATA_FRAME:
                endStream = hasFlag(flags, HTTP_FLAG_END_STREAM);
                state = State.READ_DATA;
                if (hasFlag(flags, HTTP_FLAG_PADDED)) {
                    delegate.readDataFramePadding(streamId, endStream, paddingLength + 1);
                }
                break;

            case READ_DATA:
                // Generate data frames that do not exceed maxChunkSize
                // maxChunkSize must be > 0 so we cannot infinitely loop
                int dataLength = Math.min(maxChunkSize, length - paddingLength);

                // Wait until entire frame is readable
                if (buffer.readableBytes() < dataLength) {
                    return;
                }

                ByteBuf data = buffer.readBytes(dataLength);
                length -= dataLength;

                if (length == paddingLength) {
                    if (paddingLength == 0) {
                        state = State.READ_FRAME_HEADER;
                    } else {
                        state = State.SKIP_FRAME_PADDING;
                    }
                }

                endStream = length == paddingLength && hasFlag(flags, HTTP_FLAG_END_STREAM);
                endSegment = length == paddingLength && hasFlag(flags, HTTP_FLAG_END_SEGMENT);

                delegate.readDataFrame(streamId, endStream, endSegment, data);
                break;

            case READ_HEADERS_FRAME:
                minLength = 0;
                if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                    minLength = 5;
                }
                if (buffer.readableBytes() < minLength) {
                    return;
                }

                endStream = hasFlag(flags, HTTP_FLAG_END_STREAM);
                endSegment = hasFlag(flags, HTTP_FLAG_END_SEGMENT);

                exclusive = false;
                dependency = 0;
                weight = 16;
                if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                    dependency = getSignedInt(buffer, buffer.readerIndex());
                    buffer.skipBytes(4);
                    weight = buffer.readUnsignedByte() + 1;
                    if (dependency < 0) {
                        dependency = dependency & 0x7FFFFFFF;
                        exclusive = true;
                    }
                    length -= 5;
                }

                state = State.READ_HEADER_BLOCK;
                delegate.readHeadersFrame(streamId, endStream, endSegment, exclusive, dependency, weight);
                break;

            case READ_PRIORITY_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                exclusive = false;
                dependency = getSignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(4);
                weight = buffer.readUnsignedByte() + 1;
                if (dependency < 0) {
                    dependency = dependency & 0x7FFFFFFF;
                    exclusive = true;
                }

                state = State.READ_FRAME_HEADER;
                delegate.readPriorityFrame(streamId, exclusive, dependency, weight);
                break;

            case READ_RST_STREAM_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                errorCode = getSignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                state = State.READ_FRAME_HEADER;
                delegate.readRstStreamFrame(streamId, errorCode);
                break;

            case READ_SETTINGS_FRAME:
                boolean ack = hasFlag(flags, HTTP_FLAG_ACK);

                state = State.READ_SETTING;
                delegate.readSettingsFrame(ack);
                break;

            case READ_SETTING:
                if (length == 0) {
                    state = State.READ_FRAME_HEADER;
                    delegate.readSettingsEnd();
                    break;
                }

                if (buffer.readableBytes() < 6) {
                    return;
                }

                int id = getUnsignedShort(buffer, buffer.readerIndex());
                int value = getSignedInt(buffer, buffer.readerIndex() + 2);
                buffer.skipBytes(6);
                length -= 6;

                delegate.readSetting(id, value);
                break;

            case READ_PUSH_PROMISE_FRAME:
                if (buffer.readableBytes() < 4) {
                    return;
                }

                int promisedStreamId = getUnsignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(4);
                length -= 4;

                if (promisedStreamId == 0) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Promised-Stream-ID");
                } else {
                    state = State.READ_HEADER_BLOCK;
                    delegate.readPushPromiseFrame(streamId, promisedStreamId);
                }
                break;

            case READ_PING_FRAME:
                if (buffer.readableBytes() < length) {
                    return;
                }

                long ping = getSignedLong(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                boolean pong = hasFlag(flags, HTTP_FLAG_ACK);

                state = State.READ_FRAME_HEADER;
                delegate.readPingFrame(ping, pong);
                break;

            case READ_GOAWAY_FRAME:
                if (buffer.readableBytes() < 8) {
                    return;
                }

                int lastStreamId = getUnsignedInt(buffer, buffer.readerIndex());
                errorCode = getSignedInt(buffer, buffer.readerIndex() + 4);
                buffer.skipBytes(8);
                length -= 8;

                if (length == 0) {
                    state = State.READ_FRAME_HEADER;
                } else {
                    paddingLength = length;
                    state = State.SKIP_FRAME_PADDING;
                }
                delegate.readGoAwayFrame(lastStreamId, errorCode);
                break;

            case READ_WINDOW_UPDATE_FRAME:
                // Wait until entire frame is readable
                if (buffer.readableBytes() < length) {
                    return;
                }

                int windowSizeIncrement = getUnsignedInt(buffer, buffer.readerIndex());
                buffer.skipBytes(length);

                if (windowSizeIncrement == 0) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Window Size Increment");
                } else {
                    state = State.READ_FRAME_HEADER;
                    delegate.readWindowUpdateFrame(streamId, windowSizeIncrement);
                }
                break;

            case READ_CONTINUATION_FRAME_HEADER:
                // Wait until entire frame header is readable
                if (buffer.readableBytes() < HTTP_FRAME_HEADER_SIZE) {
                    return;
                }

                // Read and validate continuation frame header fields
                int prevStreamId = streamId;
                readFrameHeader(buffer);

                // TODO(jpinner) Sec 4.2 FRAME_SIZE_ERROR
                // TODO(jpinner) invalid flags

                if (type != HTTP_CONTINUATION_FRAME || streamId != prevStreamId) {
                    state = State.FRAME_ERROR;
                    delegate.readFrameError("Invalid Continuation Frame");
                } else {
                    paddingLength = 0;
                    state = State.READ_HEADER_BLOCK;
                }
                break;

            case READ_HEADER_BLOCK:
                if (length == paddingLength) {
                    boolean endHeaders = hasFlag(flags, HTTP_FLAG_END_HEADERS);
                    if (endHeaders) {
                        state = State.SKIP_FRAME_PADDING;
                        delegate.readHeaderBlockEnd();
                    } else {
                        state = State.SKIP_FRAME_PADDING_CONTINUATION;
                    }
                    break;
                }

                if (!buffer.isReadable()) {
                    return;
                }

                int readableBytes = Math.min(buffer.readableBytes(), length - paddingLength);
                ByteBuf headerBlockFragment = buffer.readBytes(readableBytes);
                length -= readableBytes;

                delegate.readHeaderBlock(headerBlockFragment);
                break;

            case SKIP_FRAME_PADDING:
                int numBytes = Math.min(buffer.readableBytes(), length);
                buffer.skipBytes(numBytes);
                length -= numBytes;
                if (length == 0) {
                    state = State.READ_FRAME_HEADER;
                    break;
                }
                return;

            case SKIP_FRAME_PADDING_CONTINUATION:
                int numPaddingBytes = Math.min(buffer.readableBytes(), length);
                buffer.skipBytes(numPaddingBytes);
                length -= numPaddingBytes;
                if (length == 0) {
                    state = State.READ_CONTINUATION_FRAME_HEADER;
                    break;
                }
                return;

            case FRAME_ERROR:
                buffer.skipBytes(buffer.readableBytes());
                return;

            default:
                throw new Error("Shouldn't reach here.");
            }
        }
    }

    /**
     * Reads the HTTP/2 Frame Header and sets the length, type, flags, and streamId member variables.
     *
     * @param buffer input buffer containing the entire 9-octet header
     */
    private void readFrameHeader(ByteBuf buffer) {
        int frameOffset = buffer.readerIndex();
        length = getUnsignedMedium(buffer, frameOffset);
        type = buffer.getUnsignedByte(frameOffset + 3);
        flags = buffer.getByte(frameOffset + 4);
        streamId = getUnsignedInt(buffer, frameOffset + 5);
        buffer.skipBytes(HTTP_FRAME_HEADER_SIZE);
    }

    private static boolean hasFlag(byte flags, byte flag) {
        return (flags & flag) != 0;
    }

    private static boolean frameHasPadding(int type, byte flags) {
        switch (type) {
        case HTTP_DATA_FRAME:
        case HTTP_HEADERS_FRAME:
        case HTTP_PUSH_PROMISE_FRAME:
            return hasFlag(flags, HTTP_FLAG_PADDED);
        default:
            return false;
        }
    }

    private static State getNextState(int length, int type) {
        switch (type) {
        case HTTP_DATA_FRAME:
            return State.READ_DATA_FRAME;

        case HTTP_HEADERS_FRAME:
            return State.READ_HEADERS_FRAME;

        case HTTP_PRIORITY_FRAME:
            return State.READ_PRIORITY_FRAME;

        case HTTP_RST_STREAM_FRAME:
            return State.READ_RST_STREAM_FRAME;

        case HTTP_SETTINGS_FRAME:
            return State.READ_SETTINGS_FRAME;

        case HTTP_PUSH_PROMISE_FRAME:
            return State.READ_PUSH_PROMISE_FRAME;

        case HTTP_PING_FRAME:
            return State.READ_PING_FRAME;

        case HTTP_GOAWAY_FRAME:
            return State.READ_GOAWAY_FRAME;

        case HTTP_WINDOW_UPDATE_FRAME:
            return State.READ_WINDOW_UPDATE_FRAME;

        case HTTP_CONTINUATION_FRAME:
            throw new Error("Shouldn't reach here.");

        default:
            if (length != 0) {
                return State.SKIP_FRAME_PADDING;
            } else {
                return State.READ_FRAME_HEADER;
            }
        }
    }

    private static boolean isValidFrameHeader(int length, short type, byte flags, int streamId) {
        if (length > HTTP_MAX_LENGTH) {
            return false;
        }
        int minLength;
        switch (type) {
        case HTTP_DATA_FRAME:
            if (hasFlag(flags, HTTP_FLAG_PADDED)) {
                minLength = 1;
            } else {
                minLength = 0;
            }
            return length >= minLength && streamId != 0;

        case HTTP_HEADERS_FRAME:
            if (hasFlag(flags, HTTP_FLAG_PADDED)) {
                minLength = 1;
            } else {
                minLength = 0;
            }
            if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                minLength += 5;
            }
            return length >= minLength && streamId != 0;

        case HTTP_PRIORITY_FRAME:
            return length == 5 && streamId != 0;

        case HTTP_RST_STREAM_FRAME:
            return length == 4 && streamId != 0;

        case HTTP_SETTINGS_FRAME:
            boolean lengthValid = hasFlag(flags, HTTP_FLAG_ACK) ? length == 0 : (length % 6) == 0;
            return lengthValid && streamId == 0;

        case HTTP_PUSH_PROMISE_FRAME:
            if (hasFlag(flags, HTTP_FLAG_PADDED)) {
                minLength = 5;
            } else {
                minLength = 4;
            }
            return length >= minLength && streamId != 0;

        case HTTP_PING_FRAME:
            return length == 8 && streamId == 0;

        case HTTP_GOAWAY_FRAME:
            return length >= 8 && streamId == 0;

        case HTTP_WINDOW_UPDATE_FRAME:
            return length == 4;

        case HTTP_CONTINUATION_FRAME:
            return false;

        default:
            return true;
        }
    }

    private static boolean isValidPaddingLength(
            int length, short type, byte flags, int paddingLength) {
        switch (type) {
        case HTTP_DATA_FRAME:
            return length >= paddingLength;
        case HTTP_HEADERS_FRAME:
            if (hasFlag(flags, HTTP_FLAG_PRIORITY)) {
                return length >= paddingLength + 5;
            } else {
                return length >= paddingLength;
            }
        case HTTP_PUSH_PROMISE_FRAME:
            return length >= paddingLength + 4;
        default:
            throw new Error("Shouldn't reach here.");
        }
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpFrameDecoderDelegate.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.buffer.ByteBuf;

/**
 * Callback interface for {@link HttpFrameDecoder}.
 */
public interface HttpFrameDecoderDelegate {

    /**
     * Called when a DATA frame is received.
     */
    void readDataFramePadding(int streamId, boolean endStream, int padding);

    /**
     * Called when a DATA frame is received.
     */
    void readDataFrame(int streamId, boolean endStream, boolean endSegment, ByteBuf data);

    /**
     * Called when a HEADERS frame is received.
     * The Header Block Fragment is not included. See readHeaderBlock().
     */
    void readHeadersFrame(
            int streamId,
            boolean endStream,
            boolean endSegment,
            boolean exclusive,
            int dependency,
            int weight
    );

    /**
     * Called when a PRIORITY frame is received.
     */
    void readPriorityFrame(int streamId, boolean exclusive, int dependency, int weight);

    /**
     * Called when a RST_STREAM frame is received.
     */
    void readRstStreamFrame(int streamId, int errorCode);

    /**
     * Called when a SETTINGS frame is received.
     * Settings are not included. See readSetting().
     */
    void readSettingsFrame(boolean ack);

    /**
     * Called when an individual setting within a SETTINGS frame is received.
     */
    void readSetting(int id, int value);

    /**
     * Called when the entire SETTINGS frame has been received.
     */
    void readSettingsEnd();

    /**
     * Called when a PUSH_PROMISE frame is received.
     * The Header Block Fragment is not included. See readHeaderBlock().
     */
    void readPushPromiseFrame(int streamId, int promisedStreamId);

    /**
     * Called when a PING frame is received.
     */
    void readPingFrame(long data, boolean ack);

    /**
     * Called when a GOAWAY frame is received.
     */
    void readGoAwayFrame(int lastStreamId, int errorCode);

    /**
     * Called when a WINDOW_UPDATE frame is received.
     */
    void readWindowUpdateFrame(int streamId, int windowSizeIncrement);

    /**
     * Called when the header block fragment within a HEADERS,
     * PUSH_PROMISE, or CONTINUATION frame is received.
     */
    void readHeaderBlock(ByteBuf headerBlockFragment);

    /**
     * Called when an entire header block has been received.
     */
    void readHeaderBlockEnd();

    /**
     * Called when an unrecoverable connection error has occurred.
     */
    void readFrameError(String message);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpFrameEncoder.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.util.Set;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import static com.twitter.http2.HttpCodecUtil.HTTP_CONTINUATION_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_DATA_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_DEPENDENCY;
import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_ACK;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_HEADERS;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_STREAM;
import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_PRIORITY;
import static com.twitter.http2.HttpCodecUtil.HTTP_FRAME_HEADER_SIZE;
import static com.twitter.http2.HttpCodecUtil.HTTP_GOAWAY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_HEADERS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_MAX_LENGTH;
import static com.twitter.http2.HttpCodecUtil.HTTP_PING_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PRIORITY_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_PUSH_PROMISE_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_RST_STREAM_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_SETTINGS_FRAME;
import static com.twitter.http2.HttpCodecUtil.HTTP_WINDOW_UPDATE_FRAME;

/**
 * Encodes an HTTP/2 Frame into a {@link ByteBuf}.
 */
public class HttpFrameEncoder {

    /**
     * Encode an HTTP/2 DATA Frame
     */
    public ByteBuf encodeDataFrame(int streamId, boolean endStream, ByteBuf data) {
        byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
        ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
        writeFrameHeader(header, data.readableBytes(), HTTP_DATA_FRAME, flags, streamId);
        return Unpooled.wrappedBuffer(header, data);
    }

    /**
     * Encode an HTTP/2 HEADERS Frame
     */
    public ByteBuf encodeHeadersFrame(
            int streamId,
            boolean endStream,
            boolean exclusive,
            int dependency,
            int weight,
            ByteBuf headerBlock
    ) {
        byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
        boolean hasPriority = exclusive
                || dependency != HTTP_DEFAULT_DEPENDENCY || weight != HTTP_DEFAULT_WEIGHT;
        if (hasPriority) {
            flags |= HTTP_FLAG_PRIORITY;
        }
        int maxLength = hasPriority ? HTTP_MAX_LENGTH - 5 : HTTP_MAX_LENGTH;
        boolean needsContinuations = headerBlock.readableBytes() > maxLength;
        if (!needsContinuations) {
            flags |= HTTP_FLAG_END_HEADERS;
        }
        int length = needsContinuations ? maxLength : headerBlock.readableBytes();
        if (hasPriority) {
            length += 5;
        }
        int frameLength = hasPriority ? HTTP_FRAME_HEADER_SIZE + 5 : HTTP_FRAME_HEADER_SIZE;
        ByteBuf header = Unpooled.buffer(frameLength);
        writeFrameHeader(header, length, HTTP_HEADERS_FRAME, flags, streamId);
        if (hasPriority) {
            if (exclusive) {
                header.writeInt(dependency | 0x80000000);
            } else {
                header.writeInt(dependency);
            }
            header.writeByte(weight - 1);
            length -= 5;
        }
        ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
        if (needsContinuations) {
            while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
                header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
                writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
                frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
            }
            header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
            writeFrameHeader(
                    header,
                    headerBlock.readableBytes(),
                    HTTP_CONTINUATION_FRAME,
                    HTTP_FLAG_END_HEADERS,
                    streamId
            );
            frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
        }
        return frame;
    }

    /**
     * Encode an HTTP/2 PRIORITY Frame
     */
    public ByteBuf encodePriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
        int length = 5;
        byte flags = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_PRIORITY_FRAME, flags, streamId);
        if (exclusive) {
            frame.writeInt(dependency | 0x80000000);
        } else {
            frame.writeInt(dependency);
        }
        frame.writeByte(weight - 1);
        return frame;
    }

    /**
     * Encode an HTTP/2 RST_STREAM Frame
     */
    public ByteBuf encodeRstStreamFrame(int streamId, int errorCode) {
        int length = 4;
        byte flags = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_RST_STREAM_FRAME, flags, streamId);
        frame.writeInt(errorCode);
        return frame;
    }

    /**
     * Encode an HTTP/2 SETTINGS Frame
     */
    public ByteBuf encodeSettingsFrame(HttpSettingsFrame httpSettingsFrame) {
        Set<Integer> ids = httpSettingsFrame.getIds();
        int length = ids.size() * 6;
        byte flags = httpSettingsFrame.isAck() ? HTTP_FLAG_ACK : 0;
        int streamId = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_SETTINGS_FRAME, flags, streamId);
        for (int id : ids) {
            frame.writeShort(id);
            frame.writeInt(httpSettingsFrame.getValue(id));
        }
        return frame;
    }

    /**
     * Encode an HTTP/2 PUSH_PROMISE Frame
     */
    public ByteBuf encodePushPromiseFrame(int streamId, int promisedStreamId, ByteBuf headerBlock) {
        boolean needsContinuations = headerBlock.readableBytes() > HTTP_MAX_LENGTH - 4;
        int length = needsContinuations ? HTTP_MAX_LENGTH - 4 : headerBlock.readableBytes();
        byte flags = needsContinuations ? 0 : HTTP_FLAG_END_HEADERS;
        ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + 4);
        writeFrameHeader(header, length + 4, HTTP_PUSH_PROMISE_FRAME, flags, streamId);
        header.writeInt(promisedStreamId);
        ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
        if (needsContinuations) {
            while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
                header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
                writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
                frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
            }
            header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
            writeFrameHeader(
                    header,
                    headerBlock.readableBytes(),
                    HTTP_CONTINUATION_FRAME,
                    HTTP_FLAG_END_HEADERS,
                    streamId
            );
            frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
        }
        return frame;
    }

    /**
     * Encode an HTTP/2 PING Frame
     */
    public ByteBuf encodePingFrame(long data, boolean ack) {
        int length = 8;
        byte flags = ack ? HTTP_FLAG_ACK : 0;
        int streamId = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_PING_FRAME, flags, streamId);
        frame.writeLong(data);
        return frame;
    }

    /**
     * Encode an HTTP/2 GOAWAY Frame
     */
    public ByteBuf encodeGoAwayFrame(int lastStreamId, int errorCode) {
        int length = 8;
        byte flags = 0;
        int streamId = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_GOAWAY_FRAME, flags, streamId);
        frame.writeInt(lastStreamId);
        frame.writeInt(errorCode);
        return frame;
    }

    /**
     * Encode an HTTP/2 WINDOW_UPDATE Frame
     */
    public ByteBuf encodeWindowUpdateFrame(int streamId, int windowSizeIncrement) {
        int length = 4;
        byte flags = 0;
        ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
        writeFrameHeader(frame, length, HTTP_WINDOW_UPDATE_FRAME, flags, streamId);
        frame.writeInt(windowSizeIncrement);
        return frame;
    }

    private void writeFrameHeader(ByteBuf buffer, int length, int type, byte flags, int streamId) {
        buffer.writeMedium(length);
        buffer.writeByte(type);
        buffer.writeByte(flags);
        buffer.writeInt(streamId);
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpGoAwayFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 GOAWAY Frame
 */
public interface HttpGoAwayFrame extends HttpFrame {

    /**
     * Returns the Last-Stream-ID of this frame.
     */
    int getLastStreamId();

    /**
     * Sets the Last-Stream-ID of this frame.  The Last-Stream-ID cannot be negative.
     */
    HttpGoAwayFrame setLastStreamId(int lastStreamId);

    /**
     * Returns the error code of this frame.
     */
    HttpErrorCode getErrorCode();

    /**
     * Sets the error code of this frame.
     */
    HttpGoAwayFrame setErrorCode(HttpErrorCode errorCode);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpHeaderBlockDecoder.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.HttpHeaders;

import com.twitter.hpack.Decoder;
import com.twitter.hpack.HeaderListener;

final class HttpHeaderBlockDecoder {

    private static final HeaderListener NULL_HEADER_LISTENER = new NullHeaderListener();

    private Decoder decoder;
    private ByteBuf cumulation;

    public HttpHeaderBlockDecoder(int maxHeaderSize, int maxHeaderTableSize) {
        decoder = new Decoder(maxHeaderSize, maxHeaderTableSize);
    }

    /**
     * Set the maximum header table size allowed by the decoder.
     * This is the value of SETTINGS_HEADER_TABLE_SIZE sent to the peer.
     *
     * @param maxHeaderTableSize the maximum header table size allowed by the decoder
     */
    public void setMaxHeaderTableSize(int maxHeaderTableSize) {
        decoder.setMaxHeaderTableSize(maxHeaderTableSize);
    }

    public void decode(ByteBuf headerBlock, final HttpHeaderBlockFrame frame) throws IOException {
        HeaderListener headerListener = NULL_HEADER_LISTENER;
        if (frame != null) {
            headerListener = new HeaderListenerImpl(frame.headers());
        }

        if (cumulation == null) {
            decoder.decode(new ByteBufInputStream(headerBlock), headerListener);
            if (headerBlock.isReadable()) {
                cumulation = Unpooled.buffer(headerBlock.readableBytes());
                cumulation.writeBytes(headerBlock);
            }
        } else {
            cumulation.writeBytes(headerBlock);
            decoder.decode(new ByteBufInputStream(cumulation), headerListener);
            if (cumulation.isReadable()) {
                cumulation.discardReadBytes();
            } else {
                cumulation.release();
                cumulation = null;
            }
        }
    }

    public void endHeaderBlock(final HttpHeaderBlockFrame frame) {
        if (cumulation != null) {
            if (cumulation.isReadable() && frame != null) {
                frame.setInvalid();
            }
            cumulation.release();
            cumulation = null;
        }

        boolean truncated = decoder.endHeaderBlock();

        if (truncated && frame != null) {
            frame.setTruncated();
        }
    }

    private static final class NullHeaderListener implements HeaderListener {
        @Override
        public void addHeader(byte[] name, byte[] value, boolean sensitive) {
            // No Op
        }
    }

    private static final class HeaderListenerImpl implements HeaderListener {

        private final HttpHeaders headers;

        HeaderListenerImpl(HttpHeaders headers) {
            this.headers = headers;
        }

        @Override
        public void addHeader(byte[] name, byte[] value, boolean sensitive) {
            String nameStr = new String(name, StandardCharsets.UTF_8);

            // check for empty value
            if (value.length == 0) {
                addHeader(nameStr, "");
                return;
            }

            // Sec. 8.1.3.3. Header Field Ordering
            int index = 0;
            int offset = 0;
            while (index < value.length) {
                while (index < value.length && value[index] != (byte) 0) {
                    index++;
                }
                if (index - offset == 0) {
                    addHeader(nameStr, "");
                } else {
                    String valueStr = new String(value, offset, index - offset, StandardCharsets.UTF_8);
                    addHeader(nameStr, valueStr);
                }
                index++;
                offset = index;
            }
        }

        private void addHeader(String name, String value) {
            boolean crumb = "cookie".equalsIgnoreCase(name);
            if (value.length() == 0) {
                if (crumb || headers.contains(name)) {
                    return;
                }
            }
            if (crumb) {
                // Sec. 8.1.3.4. Cookie Header Field
                String cookie = headers.get(name);
                if (cookie == null) {
                    headers.set(name, value);
                } else {
                    headers.set(name, cookie + "; " + value);
                }
            } else {
                headers.add(name, value);
            }
        }
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpHeaderBlockEncoder.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;

import com.twitter.hpack.Encoder;

public class HttpHeaderBlockEncoder {

    private static final byte[] COOKIE = {'c', 'o', 'o', 'k', 'i', 'e'};
    private static final byte[] EMPTY = {};

    private int encoderMaxHeaderTableSize;
    private int decoderMaxHeaderTableSize;
    private int maxHeaderTableSize;
    private Encoder encoder;

    /**
     * Create a new instance.
     */
    public HttpHeaderBlockEncoder(int maxHeaderTableSize) {
        encoderMaxHeaderTableSize = maxHeaderTableSize;
        decoderMaxHeaderTableSize = maxHeaderTableSize;
        this.maxHeaderTableSize = maxHeaderTableSize;
        encoder = new Encoder(maxHeaderTableSize);
    }

    /**
     * Set the maximum header table size allowed by the encoder.
     *
     * @param encoderMaxHeaderTableSize the maximum header table size allowed by the encoder
     */
    public void setEncoderMaxHeaderTableSize(int encoderMaxHeaderTableSize) {
        this.encoderMaxHeaderTableSize = encoderMaxHeaderTableSize;
        if (encoderMaxHeaderTableSize < maxHeaderTableSize) {
            maxHeaderTableSize = encoderMaxHeaderTableSize;
        }
    }

    /**
     * Set the maximum header table size allowed by the peer's encoder.
     * This is the value of SETTINGS_HEADER_TABLE_SIZE received from the peer.
     *
     * @param decoderMaxHeaderTableSize the maximum header table size allowed by the decoder
     */
    public void setDecoderMaxHeaderTableSize(int decoderMaxHeaderTableSize) {
        this.decoderMaxHeaderTableSize = decoderMaxHeaderTableSize;
        if (decoderMaxHeaderTableSize < maxHeaderTableSize) {
            maxHeaderTableSize = decoderMaxHeaderTableSize;
        }
    }

    /**
     * Encode the header block frame.
     */
    public ByteBuf encode(ChannelHandlerContext ctx, HttpHeaderBlockFrame frame) throws IOException {
        ByteBuf buf = Unpooled.buffer();
        ByteBufOutputStream out = new ByteBufOutputStream(buf);

        // The current allowable max header table size is the
        // minimum of the encoder and decoder allowable sizes
        int allowableHeaderTableSize = Math.min(encoderMaxHeaderTableSize, decoderMaxHeaderTableSize);

        // maxHeaderTableSize will hold the smallest size seen the
        // last call to encode. This might be smaller than the
        // current allowable max header table size
        if (maxHeaderTableSize < allowableHeaderTableSize) {
            encoder.setMaxHeaderTableSize(out, maxHeaderTableSize);
        }

        // Check if the current allowable size is equal to the encoder's
        // capacity and set the new size if necessary
        if (allowableHeaderTableSize != encoder.getMaxHeaderTableSize()) {
            encoder.setMaxHeaderTableSize(out, allowableHeaderTableSize);
        }

        // Store the current allowable size for the next call
        maxHeaderTableSize = allowableHeaderTableSize;

        // Now we can encode headers
        for (String name : frame.headers().names()) {
            if ("cookie".equalsIgnoreCase(name)) {
                // Sec. 8.1.3.4. Cookie Header Field
                for (String value : frame.headers().getAll(name)) {
                    for (String crumb : value.split(";")) {
                        byte[] valueBytes = crumb.trim().getBytes(StandardCharsets.UTF_8);
                        encoder.encodeHeader(out, COOKIE, valueBytes, true);
                    }
                }
            } else {
                byte[] nameBytes = name.toLowerCase(Locale.ENGLISH).getBytes(StandardCharsets.UTF_8);
                // Sec. 8.1.3.3. Header Field Ordering
                List<String> values = frame.headers().getAll(name);
                if (values.size() == 0) {
                    encoder.encodeHeader(out, nameBytes, EMPTY, false);
                } else {
                    for (String value : values) {
                        byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
                        encoder.encodeHeader(out, nameBytes, valueBytes, false);
                    }
                }
            }
        }

        return buf;
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpHeaderBlockFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.handler.codec.http.HttpHeaders;

public interface HttpHeaderBlockFrame extends HttpStreamFrame {

    /**
     * Returns {@code true} if this header block is invalid.
     * A RST_STREAM frame with code PROTOCOL_ERROR should be sent.
     */
    boolean isInvalid();

    /**
     * Marks this header block as invalid.
     */
    HttpHeaderBlockFrame setInvalid();

    /**
     * Returns {@code true} if this header block has been truncated due to length restrictions.
     */
    boolean isTruncated();

    /**
     * Mark this header block as truncated.
     */
    HttpHeaderBlockFrame setTruncated();

    /**
     * Returns the {@link HttpHeaders} of this frame.
     */
    HttpHeaders headers();
}


================================================
FILE: src/main/java/com/twitter/http2/HttpHeadersFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 HEADERS Frame
 */
public interface HttpHeadersFrame extends HttpHeaderBlockFrame {

    /**
     * Returns {@code true} if this frame is the last frame to be transmitted on the stream.
     */
    boolean isLast();

    /**
     * Sets if this frame is the last frame to be transmitted on the stream.
     */
    HttpHeadersFrame setLast(boolean last);

    /**
     * Returns {@code true} if the dependency of the stream is exclusive.
     */
    boolean isExclusive();

    /**
     * Sets if the dependency of the stream is exclusive.
     */
    HttpHeadersFrame setExclusive(boolean exclusive);

    /**
     * Returns the dependency of the stream.
     */
    int getDependency();

    /**
     * Sets the dependency of the stream.  The dependency cannot be negative.
     */
    HttpHeadersFrame setDependency(int dependency);

    /**
     * Returns the weight of the dependency of the stream.
     */
    int getWeight();

    /**
     * Sets the weight of the dependency of the stream.
     * The weight must be positive and cannot exceed 256 bytes.
     */
    HttpHeadersFrame setWeight(int weight);

    @Override
    HttpHeadersFrame setStreamId(int streamId);

    @Override
    HttpHeadersFrame setInvalid();

    @Override
    HttpHeadersFrame setTruncated();
}


================================================
FILE: src/main/java/com/twitter/http2/HttpMessageProxy.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpVersion;

/**
 * An {@link HttpMessage} decorator.
 */
class HttpMessageProxy implements HttpMessage {

    private final HttpMessage message;

    protected HttpMessageProxy(HttpMessage message) {
        this.message = message;
    }

    @Override
    public HttpVersion getProtocolVersion() {
        return message.getProtocolVersion();
    }

    @Override
    public HttpMessage setProtocolVersion(HttpVersion version) {
        message.setProtocolVersion(version);
        return this;
    }

    @Override
    public HttpHeaders headers() {
        return message.headers();
    }

    @Override
    @Deprecated
    public DecoderResult getDecoderResult() {
        return message.getDecoderResult();
    }

    @Override
    public void setDecoderResult(DecoderResult result) {
        message.setDecoderResult(result);
    }

    @Override
    public HttpVersion protocolVersion() {
        return message.protocolVersion();
    }

    @Override
    public DecoderResult decoderResult() {
        return message.decoderResult();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpPingFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 PING Frame
 */
public interface HttpPingFrame extends HttpFrame {

    /**
     * Returns the data payload of this frame.
     */
    long getData();

    /**
     * Sets the data payload of this frame.
     */
    HttpPingFrame setData(long data);

    /**
     * Returns {@code true} if this frame is in response to a PING.
     */
    boolean isPong();

    /**
     * Sets if this frame is in response to a PING.
     */
    HttpPingFrame setPong(boolean pong);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpPriorityFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 PRIORITY Frame
 */
public interface HttpPriorityFrame extends HttpFrame {

    /**
     * Returns the stream identifier of this frame.
     */
    int getStreamId();

    /**
     * Sets the stream identifier of this frame.  The stream identifier must be positive.
     */
    HttpPriorityFrame setStreamId(int streamId);

    /**
     * Returns {@code true} if the dependency of the stream is exclusive.
     */
    boolean isExclusive();

    /**
     * Sets if the dependency of the stream is exclusive.
     */
    HttpPriorityFrame setExclusive(boolean exclusive);

    /**
     * Returns the dependency of the stream.
     */
    int getDependency();

    /**
     * Sets the dependency of the stream.  The dependency cannot be negative.
     */
    HttpPriorityFrame setDependency(int dependency);

    /**
     * Returns the weight of the dependency of the stream.
     */
    int getWeight();

    /**
     * Sets the weight of the dependency of the stream.
     * The weight must be positive and cannot exceed 256 bytes.
     */
    HttpPriorityFrame setWeight(int weight);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpProtocolException.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

public class HttpProtocolException extends Exception {

    /**
     * Creates a new instance.
     */
    public HttpProtocolException() {
        super();
    }

    /**
     * Creates a new instance.
     */
    public HttpProtocolException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * Creates a new instance.
     */
    public HttpProtocolException(String message) {
        super(message);
    }

    /**
     * Creates a new instance.
     */
    public HttpProtocolException(Throwable cause) {
        super(cause);
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpPushPromiseFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 PUSH_PROMISE Frame
 */
public interface HttpPushPromiseFrame extends HttpHeaderBlockFrame {

    /**
     * Returns the Promised-Stream-ID of this frame.
     */
    int getPromisedStreamId();

    /**
     * Sets the Promised-Stream-ID of this frame.  The Promised-Stream-ID must be positive.
     */
    HttpPushPromiseFrame setPromisedStreamId(int promisedStreamId);

    @Override
    HttpPushPromiseFrame setStreamId(int streamId);

    @Override
    HttpPushPromiseFrame setInvalid();

    @Override
    HttpPushPromiseFrame setTruncated();
}


================================================
FILE: src/main/java/com/twitter/http2/HttpRequestProxy.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;

/**
 * An {@link HttpRequest} decorator.
 */
class HttpRequestProxy extends HttpMessageProxy implements HttpRequest {

    private final HttpRequest request;

    public HttpRequestProxy(HttpRequest request) {
        super(request);
        this.request = request;
    }

    public HttpRequest httpRequest() {
        return request;
    }

    @Override
    @Deprecated
    public HttpMethod getMethod() {
        return request.getMethod();
    }

    @Override
    public HttpRequest setMethod(HttpMethod method) {
        request.setMethod(method);
        return this;
    }

    @Override
    @Deprecated
    public String getUri() {
        return request.getUri();
    }

    @Override
    public HttpRequest setUri(String uri) {
        request.setUri(uri);
        return this;
    }

    @Override
    public HttpRequest setProtocolVersion(HttpVersion version) {
        request.setProtocolVersion(version);
        return this;
    }

    @Override
    public String toString() {
        return request.toString();
    }

    @Override
    public HttpMethod method() {
        return request.method();
    }

    @Override
    public String uri() {
        return request.uri();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpResponseProxy.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;

/**
 * An {@link HttpResponse} decorator.
 */
public class HttpResponseProxy extends HttpMessageProxy implements HttpResponse {

    private final HttpResponse response;

    public HttpResponseProxy(HttpResponse response) {
        super(response);
        this.response = response;
    }

    @Override
    @Deprecated
    public HttpResponseStatus getStatus() {
        return response.getStatus();
    }

    @Override
    public HttpResponse setStatus(HttpResponseStatus status) {
        response.setStatus(status);
        return this;
    }

    @Override
    public HttpResponse setProtocolVersion(HttpVersion version) {
        response.setProtocolVersion(version);
        return this;
    }

    @Override
    public String toString() {
        return response.toString();
    }

    @Override
    public HttpResponseStatus status() {
        return response.status();
    }
}


================================================
FILE: src/main/java/com/twitter/http2/HttpRstStreamFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

/**
 * An HTTP/2 RST_STREAM Frame
 */
public interface HttpRstStreamFrame extends HttpFrame {

    /**
     * Returns the stream identifier of this frame.
     */
    int getStreamId();

    /**
     * Sets the stream identifier of this frame.  The stream identifier must be positive.
     */
    HttpRstStreamFrame setStreamId(int streamId);

    /**
     * Returns the error code of this frame.
     */
    HttpErrorCode getErrorCode();

    /**
     * Sets the error code of this frame.
     */
    HttpRstStreamFrame setErrorCode(HttpErrorCode errorCode);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpSettingsFrame.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.util.Set;

/**
 * An HTTP/2 SETTINGS Frame
 */
public interface HttpSettingsFrame extends HttpFrame {

    int SETTINGS_HEADER_TABLE_SIZE = 1;
    int SETTINGS_ENABLE_PUSH = 2;
    int SETTINGS_MAX_CONCURRENT_STREAMS = 3;
    int SETTINGS_INITIAL_WINDOW_SIZE = 4;
    int SETTINGS_MAX_FRAME_SIZE = 5;
    int SETTINGS_MAX_HEADER_LIST_SIZE = 6;

    /**
     * Returns a {@code Set} of the setting IDs.
     * The set's iterator will return the IDs in ascending order.
     */
    Set<Integer> getIds();

    /**
     * Returns {@code true} if the setting ID has a value.
     */
    boolean isSet(int id);

    /**
     * Returns the value of the setting ID.
     * Returns -1 if the setting ID is not set.
     */
    int getValue(int id);

    /**
     * Sets the value of the setting ID.
     * The ID cannot be negative and cannot exceed 65535.
     */
    HttpSettingsFrame setValue(int id, int value);

    /**
     * Removes the value of the setting ID.
     */
    HttpSettingsFrame removeValue(int id);

    /**
     * Returns {@code true} if this frame is an acknowledgement frame.
     */
    boolean isAck();

    /**
     * Sets if this frame is acknowledgement frame.
     */
    HttpSettingsFrame setAck(boolean ack);
}


================================================
FILE: src/main/java/com/twitter/http2/HttpStreamDecoder.java
================================================
/*
 * Copyright 2015 Twitter, Inc.
 *
 * Licensed 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 com.twitter.http2;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;

/**
 * Decodes {@link HttpFrame}s into {@link StreamedHttpRequest}s.
 */
public class HttpStreamDecoder extends MessageToMessageDecoder<Object> {

    private static final CancellationException CANCELLATION_EXCEPTION =
            new CancellationException("HTTP/2 RST_STREAM Frame Received");

    private final Map<Integer, StreamedHttpMessage> messageMap =
            new ConcurrentHashMap<Integer, StreamedHttpMessage>();
    private final Map<Integer, LastHttpContent> trailerMap =
            new ConcurrentHashMap<Integer, LastHttpContent>();

    @Override
    protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
        if (msg instanceof HttpHeadersFrame) {

            HttpHeadersFrame httpHeadersFrame = (HttpHeadersFrame) msg;
            int streamId = httpHeadersFrame.getStreamId();
            StreamedHttpMessage message = messageMap.get(streamId);

            if (message == null) {
                if (httpHeadersFrame.headers().contains(":status")) {

                    // If a client receives a reply with a truncated header block,
                    // reply with a RST_STREAM frame with error code INTERNAL_ERROR.
                    if (httpHeadersFrame.isTruncated()) {
                        HttpRstStreamFrame httpRstStreamFrame =
                                new DefaultHttpRstStreamFrame(streamId, HttpErrorCode.INTERNAL_ERROR);
                        out.add(httpRstStreamFrame);
                        return;
                    }

                    try {
                        StreamedHttpResponse response = createHttpResponse(httpHeadersFrame);

                        if (httpHeadersFrame.isLast()) {
                            HttpHeaders.setContentLength(response, 0);
                            response.getContent().close();
                        } else {
                            // Response body will follow in a series of Data Frames
                            if (!HttpHeaders.isContentLengthSet(response)) {
                                HttpHeaders.setTransferEncodingChunked(response);
                            }
                            messageMap.put(streamId, response);
                        }
                        out.add(response);
                    } catch (Exception e) {
                        // If a client receives a SYN_REPLY without valid getStatus and version headers
                        // the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR
                        HttpRstStreamFrame httpRstStreamFrame =
                                new DefaultHttpRstStreamFrame(streamId, HttpErrorCode.PROTOCOL_ERROR);
                        ctx.writeAndFlush(httpRstStreamFrame);
                        out.add(httpRstStreamFrame);
                    }

                } else {

                    // If a client sends a request with a truncated header block, the server must
                    // reply with a HTTP 431 REQUEST HEADER FIELDS TOO LARGE reply.
                    if (httpHeadersFrame.isTruncated()) {
                        httpHeadersFrame = new DefaultHttpHeadersFrame(streamId);
                        httpHeadersFrame.setLast(true);
                        httpHeadersFrame.headers().set(
                                ":status", HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.code());
                        ctx.writeAndFlush(httpHeadersFrame);
                        return;
                    }

                    try {
                        message = createHttpRequest(httpHeadersFrame);

                        if (httpHeadersFrame.isLast()) {
                            message.setDecoderResult(DecoderResult.SUCCESS);
                            message.getContent().close();
                        } else {
                            // Request body will follow in a series of Data Frames
                            messageMap.put(streamId, message);
                        }

                        out.add(message);

                    } catch (Exception e) {
                        // If a client sends a SYN_STREAM without all of the method, url (host and path),
                        // scheme, and version headers the server must reply with a HTTP 400 BAD REQUEST reply.
                        // Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid
                        httpHeadersFrame = new DefaultHttpHeadersFrame(streamId);
                        httpHeadersFrame.setLast(true);
                        httpHeadersFrame.headers().set(":status", HttpResponseStatus.BAD_REQUEST.code());
                        ctx.writeAndFlush(httpHeadersFrame);
                    }
                }
            } else {
                LastHttpContent trailer = trailerMap.remove(streamId);
                if (trailer == null) {
                    trailer = new DefaultLastHttpContent();
                }

                // Ignore trailers in a truncated HEADERS frame.
                if (!httpHeadersFrame.isTruncated()) {
                    for (Map.Entry<String, String> e: httpHeadersFrame.headers()) {
                        trailer.trailingHeaders().add(e.getKey(), e.getValue());
                    }
                }

                if (httpHeadersFrame.isLast()) {
                    messageMap.remove(streamId);
                    message.addContent(trailer);
                } else {
                    trailerMap.put(streamId, trailer);
                }
            }

        } else if (msg instanceof HttpDataFrame) {

            HttpDataFrame httpDataFrame = (HttpDataFrame) msg;
            int streamId = httpDataFrame.getStreamId();
            StreamedHttpMessage message = messageMap.get(streamId);

            // If message is not in map discard Data Frame.
            if (message == null) {
                return;
            }

            ByteBuf content = httpDataFrame.content();
            if (content.isReadable()) {
                content.retain();
                message.addContent(new DefaultHttpContent(content));
            }

            if (httpDataFrame.isLast()) {
                messageMap.remove(streamId);
                message.addContent(LastHttpContent.EMPTY_LAST_CONTENT);
                message.setDecoderResult(DecoderResult.SUCCESS);
            }

        } else if (msg instanceof HttpRstStreamFrame) {

            HttpRstStreamFrame httpRstStreamFrame = (HttpRstStreamFrame) msg;
            int streamId = httpRstStreamFrame.getStreamId();
            StreamedHttpMessage message = messageMap.remove(streamId);

            if (message != null) {
                message.getContent().close();
                message.setDecoderResult(DecoderResult.failure(CANCELLATION_EXCEPTION));
            }

        } else {
            // HttpGoAwayFrame
            out.add(msg);
        }
    }

    private StreamedHttpRequest createHttpRequest(HttpHeadersFrame httpHeadersFrame)
            throws Exception {
        // Create the first line of the request from the name/value pairs
        HttpMethod method = HttpMethod.valueOf(httpHeadersFrame.headers().get(":method"));
        String url = httpHeadersFrame.headers().get(":path");

        httpHeadersFrame.headers().remove(":method");
        httpHeadersFrame.headers().remove(":path");

        StreamedHttpRequest request = new StreamedHttpRequest(HttpVersion.HTTP_1_1, method, url);

        // Remove the scheme header
        httpHeadersFrame.headers().remove(":scheme");

        // Replace the SPDY host header with the HTTP host header
        String host = httpHeadersFrame.headers().get(":authority");
        httpHeadersFrame.headers().remove(":authority");
        httpHeadersFrame.headers().set("host", host);

        for (Map.Entry<String, String> e : httpHeadersFrame.headers()) {
            String name = e.getKey();
            String value = e.getValue();
            if (name.charAt(0) != ':') {
                request.headers().add(name, value);
            }
        }

        // Set the Stream-ID as a header
        request.headers().set("X-SPDY-Stream-ID", httpHeadersFrame.getStreamId());

        // The Connection and Keep-Alive headers are no longer valid
        HttpHeaders.setKeepAlive(request, true);

        // Transfer-Encoding header is not valid
        request.headers().remove(HttpHeaders.Names.TRANSFER_ENCODING);

        if (httpHeadersFrame.isLast()) {
            request.getContent().c
Download .txt
gitextract_lusmlr8n/

├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── com/
    │           └── twitter/
    │               └── http2/
    │                   ├── DefaultHttp2Headers.java
    │                   ├── DefaultHttpDataFrame.java
    │                   ├── DefaultHttpGoAwayFrame.java
    │                   ├── DefaultHttpHeaderBlockFrame.java
    │                   ├── DefaultHttpHeadersFrame.java
    │                   ├── DefaultHttpPingFrame.java
    │                   ├── DefaultHttpPriorityFrame.java
    │                   ├── DefaultHttpPushPromiseFrame.java
    │                   ├── DefaultHttpRstStreamFrame.java
    │                   ├── DefaultHttpSettingsFrame.java
    │                   ├── DefaultHttpStreamFrame.java
    │                   ├── DefaultHttpWindowUpdateFrame.java
    │                   ├── HttpCodecUtil.java
    │                   ├── HttpConnection.java
    │                   ├── HttpConnectionHandler.java
    │                   ├── HttpDataFrame.java
    │                   ├── HttpErrorCode.java
    │                   ├── HttpFrame.java
    │                   ├── HttpFrameDecoder.java
    │                   ├── HttpFrameDecoderDelegate.java
    │                   ├── HttpFrameEncoder.java
    │                   ├── HttpGoAwayFrame.java
    │                   ├── HttpHeaderBlockDecoder.java
    │                   ├── HttpHeaderBlockEncoder.java
    │                   ├── HttpHeaderBlockFrame.java
    │                   ├── HttpHeadersFrame.java
    │                   ├── HttpMessageProxy.java
    │                   ├── HttpPingFrame.java
    │                   ├── HttpPriorityFrame.java
    │                   ├── HttpProtocolException.java
    │                   ├── HttpPushPromiseFrame.java
    │                   ├── HttpRequestProxy.java
    │                   ├── HttpResponseProxy.java
    │                   ├── HttpRstStreamFrame.java
    │                   ├── HttpSettingsFrame.java
    │                   ├── HttpStreamDecoder.java
    │                   ├── HttpStreamEncoder.java
    │                   ├── HttpStreamFrame.java
    │                   ├── HttpWindowUpdateFrame.java
    │                   ├── Pipe.java
    │                   ├── StreamedHttpMessage.java
    │                   ├── StreamedHttpRequest.java
    │                   └── StreamedHttpResponse.java
    └── test/
        └── java/
            └── com/
                └── twitter/
                    └── http2/
                        ├── HttpFrameDecoderTest.java
                        ├── HttpFrameEncoderTest.java
                        ├── HttpHeaderCompressionTest.java
                        ├── HttpRequestProxyTest.java
                        ├── HttpResponseProxyTest.java
                        └── PipeTest.java
Download .txt
SYMBOL INDEX (589 symbols across 49 files)

FILE: src/main/java/com/twitter/http2/DefaultHttp2Headers.java
  class DefaultHttp2Headers (line 11) | public class DefaultHttp2Headers extends DefaultHttpHeaders {
    method process (line 13) | @Override
    method validateChar (line 20) | private static void validateChar(char character) {
    method validateName (line 44) | @Override
    method DefaultHttp2Headers (line 62) | public DefaultHttp2Headers() {

FILE: src/main/java/com/twitter/http2/DefaultHttpDataFrame.java
  class DefaultHttpDataFrame (line 27) | public class DefaultHttpDataFrame extends DefaultHttpStreamFrame impleme...
    method DefaultHttpDataFrame (line 37) | public DefaultHttpDataFrame(int streamId) {
    method DefaultHttpDataFrame (line 47) | public DefaultHttpDataFrame(int streamId, ByteBuf data) {
    method validate (line 55) | private static ByteBuf validate(ByteBuf data) {
    method isLast (line 63) | @Override
    method setLast (line 68) | @Override
    method setStreamId (line 74) | @Override
    method content (line 80) | @Override
    method copy (line 88) | @Override
    method duplicate (line 95) | @Override
    method refCnt (line 102) | @Override
    method retain (line 107) | @Override
    method retain (line 113) | @Override
    method release (line 119) | @Override
    method release (line 124) | @Override
    method toString (line 129) | @Override
    method touch (line 149) | @Override
    method touch (line 155) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpGoAwayFrame.java
  class DefaultHttpGoAwayFrame (line 23) | public class DefaultHttpGoAwayFrame implements HttpGoAwayFrame {
    method DefaultHttpGoAwayFrame (line 34) | public DefaultHttpGoAwayFrame(int lastStreamId, int code) {
    method DefaultHttpGoAwayFrame (line 44) | public DefaultHttpGoAwayFrame(int lastStreamId, HttpErrorCode errorCod...
    method getLastStreamId (line 49) | @Override
    method setLastStreamId (line 54) | @Override
    method getErrorCode (line 64) | @Override
    method setErrorCode (line 69) | @Override
    method toString (line 75) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpHeaderBlockFrame.java
  class DefaultHttpHeaderBlockFrame (line 26) | public abstract class DefaultHttpHeaderBlockFrame extends DefaultHttpStr...
    method DefaultHttpHeaderBlockFrame (line 38) | protected DefaultHttpHeaderBlockFrame(int streamId) {
    method isInvalid (line 42) | @Override
    method setInvalid (line 47) | @Override
    method isTruncated (line 53) | @Override
    method setTruncated (line 58) | @Override
    method headers (line 64) | @Override
    method appendHeaders (line 69) | protected void appendHeaders(StringBuilder buf) {

FILE: src/main/java/com/twitter/http2/DefaultHttpHeadersFrame.java
  class DefaultHttpHeadersFrame (line 26) | public class DefaultHttpHeadersFrame extends DefaultHttpHeaderBlockFrame
    method DefaultHttpHeadersFrame (line 39) | public DefaultHttpHeadersFrame(int streamId) {
    method isLast (line 43) | @Override
    method setLast (line 48) | @Override
    method isExclusive (line 54) | @Override
    method setExclusive (line 59) | @Override
    method getDependency (line 65) | @Override
    method setDependency (line 70) | @Override
    method getWeight (line 80) | @Override
    method setWeight (line 85) | @Override
    method setStreamId (line 95) | @Override
    method setInvalid (line 101) | @Override
    method setTruncated (line 107) | @Override
    method toString (line 113) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpPingFrame.java
  class DefaultHttpPingFrame (line 23) | public class DefaultHttpPingFrame implements HttpPingFrame {
    method DefaultHttpPingFrame (line 33) | public DefaultHttpPingFrame(long data) {
    method getData (line 37) | @Override
    method setData (line 42) | @Override
    method isPong (line 48) | @Override
    method setPong (line 53) | @Override
    method toString (line 59) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpPriorityFrame.java
  class DefaultHttpPriorityFrame (line 23) | public class DefaultHttpPriorityFrame implements HttpPriorityFrame {
    method DefaultHttpPriorityFrame (line 38) | public DefaultHttpPriorityFrame(int streamId, boolean exclusive, int d...
    method getStreamId (line 45) | @Override
    method setStreamId (line 50) | @Override
    method isExclusive (line 60) | @Override
    method setExclusive (line 65) | @Override
    method getDependency (line 71) | @Override
    method setDependency (line 76) | @Override
    method getWeight (line 86) | @Override
    method setWeight (line 91) | @Override
    method toString (line 101) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpPushPromiseFrame.java
  class DefaultHttpPushPromiseFrame (line 23) | public class DefaultHttpPushPromiseFrame extends DefaultHttpHeaderBlockF...
    method DefaultHttpPushPromiseFrame (line 33) | public DefaultHttpPushPromiseFrame(int streamId, int promisedStreamId) {
    method getPromisedStreamId (line 38) | @Override
    method setPromisedStreamId (line 43) | @Override
    method setStreamId (line 53) | @Override
    method setInvalid (line 59) | @Override
    method setTruncated (line 65) | @Override
    method toString (line 71) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpRstStreamFrame.java
  class DefaultHttpRstStreamFrame (line 23) | public class DefaultHttpRstStreamFrame implements HttpRstStreamFrame {
    method DefaultHttpRstStreamFrame (line 34) | public DefaultHttpRstStreamFrame(int streamId, int code) {
    method DefaultHttpRstStreamFrame (line 44) | public DefaultHttpRstStreamFrame(int streamId, HttpErrorCode errorCode) {
    method getStreamId (line 49) | @Override
    method setStreamId (line 54) | @Override
    method getErrorCode (line 64) | @Override
    method setErrorCode (line 69) | @Override
    method toString (line 75) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpSettingsFrame.java
  class DefaultHttpSettingsFrame (line 27) | public class DefaultHttpSettingsFrame implements HttpSettingsFrame {
    method getIds (line 32) | @Override
    method isSet (line 37) | @Override
    method getValue (line 42) | @Override
    method setValue (line 51) | @Override
    method removeValue (line 60) | @Override
    method isAck (line 66) | @Override
    method setAck (line 71) | @Override
    method getSettings (line 77) | private Set<Map.Entry<Integer, Integer>> getSettings() {
    method appendSettings (line 81) | private void appendSettings(StringBuilder buf) {
    method toString (line 91) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpStreamFrame.java
  class DefaultHttpStreamFrame (line 21) | public abstract class DefaultHttpStreamFrame implements HttpStreamFrame {
    method DefaultHttpStreamFrame (line 30) | protected DefaultHttpStreamFrame(int streamId) {
    method getStreamId (line 34) | @Override
    method setStreamId (line 39) | @Override

FILE: src/main/java/com/twitter/http2/DefaultHttpWindowUpdateFrame.java
  class DefaultHttpWindowUpdateFrame (line 23) | public class DefaultHttpWindowUpdateFrame implements HttpWindowUpdateFra...
    method DefaultHttpWindowUpdateFrame (line 34) | public DefaultHttpWindowUpdateFrame(int streamId, int windowSizeIncrem...
    method getStreamId (line 39) | @Override
    method setStreamId (line 44) | @Override
    method getWindowSizeIncrement (line 54) | @Override
    method setWindowSizeIncrement (line 59) | @Override
    method toString (line 69) | @Override

FILE: src/main/java/com/twitter/http2/HttpCodecUtil.java
  class HttpCodecUtil (line 20) | final class HttpCodecUtil {
    method getUnsignedShort (line 53) | static int getUnsignedShort(ByteBuf buf, int offset) {
    method getUnsignedMedium (line 61) | static int getUnsignedMedium(ByteBuf buf, int offset) {
    method getUnsignedInt (line 70) | static int getUnsignedInt(ByteBuf buf, int offset) {
    method getSignedInt (line 80) | static int getSignedInt(ByteBuf buf, int offset) {
    method getSignedLong (line 90) | static long getSignedLong(ByteBuf buf, int offset) {
    method isServerId (line 104) | static boolean isServerId(int streamId) {
    method HttpCodecUtil (line 109) | private HttpCodecUtil() {

FILE: src/main/java/com/twitter/http2/HttpConnection.java
  class HttpConnection (line 32) | final class HttpConnection {
    method HttpConnection (line 48) | public HttpConnection(int sendWindowSize, int receiveWindowSize) {
    method numActiveStreams (line 54) | int numActiveStreams(boolean remote) {
    method noActiveStreams (line 62) | boolean noActiveStreams() {
    method acceptStream (line 66) | void acceptStream(
    method removeActiveStream (line 86) | private StreamState removeActiveStream(int streamId, boolean remote) {
    method removeStream (line 101) | void removeStream(int streamId, boolean remote) {
    method isRemoteSideClosed (line 108) | boolean isRemoteSideClosed(int streamId) {
    method closeRemoteSide (line 114) | void closeRemoteSide(int streamId, boolean remote) {
    method isLocalSideClosed (line 125) | boolean isLocalSideClosed(int streamId) {
    method closeLocalSide (line 131) | void closeLocalSide(int streamId, boolean remote) {
    method getSendWindowSize (line 142) | int getSendWindowSize(int streamId) {
    method updateSendWindowSize (line 152) | int updateSendWindowSize(int streamId, int deltaWindowSize) {
    method updateReceiveWindowSize (line 162) | int updateReceiveWindowSize(int streamId, int deltaWindowSize) {
    method getReceiveWindowSizeLowerBound (line 178) | int getReceiveWindowSizeLowerBound(int streamId) {
    method updateAllSendWindowSizes (line 188) | void updateAllSendWindowSizes(int deltaWindowSize) {
    method updateAllReceiveWindowSizes (line 197) | void updateAllReceiveWindowSizes(int deltaWindowSize) {
    method putPendingWrite (line 209) | boolean putPendingWrite(int streamId, PendingWrite evt) {
    method getPendingWrite (line 215) | PendingWrite getPendingWrite(int streamId) {
    method getPendingWrite (line 226) | PendingWrite getPendingWrite(Node node) {
    method removePendingWrite (line 242) | PendingWrite removePendingWrite(int streamId) {
    method setPriority (line 251) | boolean setPriority(int streamId, boolean exclusive, int dependency, i...
    class StreamState (line 285) | private static final class StreamState {
      method StreamState (line 295) | StreamState(
      method isRemoteSideClosed (line 304) | boolean isRemoteSideClosed() {
      method closeRemoteSide (line 308) | void closeRemoteSide() {
      method isLocalSideClosed (line 312) | boolean isLocalSideClosed() {
      method closeLocalSide (line 316) | void closeLocalSide() {
      method getSendWindowSize (line 320) | int getSendWindowSize() {
      method updateSendWindowSize (line 324) | int updateSendWindowSize(int deltaWindowSize) {
      method updateReceiveWindowSize (line 328) | int updateReceiveWindowSize(int deltaWindowSize) {
      method getReceiveWindowSizeLowerBound (line 332) | int getReceiveWindowSizeLowerBound() {
      method setReceiveWindowSizeLowerBound (line 336) | void setReceiveWindowSizeLowerBound(int receiveWindowSizeLowerBound) {
      method putPendingWrite (line 340) | boolean putPendingWrite(PendingWrite msg) {
      method getPendingWrite (line 344) | PendingWrite getPendingWrite() {
      method removePendingWrite (line 348) | PendingWrite removePendingWrite() {
      method clearPendingWrites (line 352) | void clearPendingWrites(Throwable cause) {
    class Node (line 363) | private static final class Node {
      method Node (line 376) | public Node(StreamState state) {
      method close (line 380) | public void close() {
      method setWeight (line 384) | public void setWeight(int weight) {
      method addDependent (line 391) | public void addDependent(boolean exclusive, Node node) {
      method removeDependent (line 404) | public void removeDependent(Node node) {
    class WeightComparator (line 411) | private static final class WeightComparator implements Comparator<Node> {
      method compare (line 412) | @Override
    class PendingWrite (line 418) | public static final class PendingWrite {
      method PendingWrite (line 422) | PendingWrite(HttpDataFrame httpDataFrame, ChannelPromise promise) {
      method fail (line 427) | void fail(Throwable cause) {

FILE: src/main/java/com/twitter/http2/HttpConnectionHandler.java
  class HttpConnectionHandler (line 38) | public class HttpConnectionHandler extends ByteToMessageDecoder
    method HttpConnectionHandler (line 104) | public HttpConnectionHandler(boolean server) {
    method HttpConnectionHandler (line 111) | public HttpConnectionHandler(boolean server, boolean handleStreamWindo...
    method HttpConnectionHandler (line 118) | public HttpConnectionHandler(boolean server, int maxChunkSize, int max...
    method HttpConnectionHandler (line 125) | public HttpConnectionHandler(
    method setConnectionReceiveWindowSize (line 135) | public void setConnectionReceiveWindowSize(int connectionReceiveWindow...
    method handlerAdded (line 148) | @Override
    method decode (line 154) | @Override
    method readDataFramePadding (line 162) | @Override
    method readDataFrame (line 219) | @Override
    method readHeadersFrame (line 309) | @Override
    method readPriorityFrame (line 368) | @Override
    method readRstStreamFrame (line 380) | @Override
    method readSettingsFrame (line 393) | @Override
    method readSetting (line 407) | @Override
    method readSettingsEnd (line 455) | @Override
    method readPushPromiseFrame (line 481) | @Override
    method readPingFrame (line 493) | @Override
    method readGoAwayFrame (line 514) | @Override
    method readWindowUpdateFrame (line 524) | @Override
    method readHeaderBlock (line 555) | @Override
    method readHeaderBlockEnd (line 568) | @Override
    method readFrameError (line 590) | @Override
    method bind (line 595) | @Override
    method connect (line 602) | @Override
    method disconnect (line 612) | @Override
    method close (line 627) | @Override
    method deregister (line 642) | @Override
    method read (line 647) | @Override
    method write (line 652) | @Override
    method flush (line 895) | @Override
    method issueConnectionError (line 907) | private void issueConnectionError(HttpErrorCode status) {
    method issueStreamError (line 921) | private void issueStreamError(int streamId, HttpErrorCode errorCode) {
    method isRemoteInitiatedId (line 937) | private boolean isRemoteInitiatedId(int id) {
    method updateInitialSendWindowSize (line 943) | private synchronized void updateInitialSendWindowSize(int newInitialWi...
    method updateInitialReceiveWindowSize (line 950) | private synchronized void updateInitialReceiveWindowSize(int newInitia...
    method acceptStream (line 957) | private synchronized boolean acceptStream(
    method setPriority (line 978) | private synchronized boolean setPriority(
    method halfCloseStream (line 983) | private void halfCloseStream(int streamId, boolean remote, ChannelFutu...
    method removeStream (line 994) | private void removeStream(int streamId, ChannelFuture future) {
    method updateSendWindowSize (line 1001) | private void updateSendWindowSize(
    method sendGoAwayFrame (line 1064) | private void sendGoAwayFrame(ChannelHandlerContext ctx, ChannelPromise...
    method sendGoAwayFrame (line 1073) | private synchronized ChannelFuture sendGoAwayFrame(HttpErrorCode httpE...
    class ConnectionErrorFutureListener (line 1084) | private final class ConnectionErrorFutureListener implements ChannelFu...
      method operationComplete (line 1085) | @Override
    class ClosingChannelFutureListener (line 1093) | private static final class ClosingChannelFutureListener implements Cha...
      method ClosingChannelFutureListener (line 1097) | ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromi...
      method operationComplete (line 1102) | public void operationComplete(ChannelFuture sentGoAwayFuture) throws...

FILE: src/main/java/com/twitter/http2/HttpDataFrame.java
  type HttpDataFrame (line 24) | public interface HttpDataFrame extends ByteBufHolder, HttpStreamFrame {
    method isLast (line 29) | boolean isLast();
    method setLast (line 34) | HttpDataFrame setLast(boolean last);
    method setStreamId (line 36) | @Override
    method content (line 45) | @Override
    method copy (line 48) | @Override
    method duplicate (line 51) | @Override
    method retain (line 54) | @Override
    method retain (line 57) | @Override

FILE: src/main/java/com/twitter/http2/HttpErrorCode.java
  class HttpErrorCode (line 18) | public class HttpErrorCode implements Comparable<HttpErrorCode> {
    method valueOf (line 103) | public static HttpErrorCode valueOf(int code) {
    method HttpErrorCode (line 146) | public HttpErrorCode(int code, String statusPhrase) {
    method getCode (line 158) | public int getCode() {
    method getStatusPhrase (line 165) | public String getStatusPhrase() {
    method hashCode (line 169) | @Override
    method equals (line 174) | @Override
    method toString (line 183) | @Override
    method compareTo (line 188) | @Override

FILE: src/main/java/com/twitter/http2/HttpFrame.java
  type HttpFrame (line 21) | public interface HttpFrame {

FILE: src/main/java/com/twitter/http2/HttpFrameDecoder.java
  class HttpFrameDecoder (line 49) | public class HttpFrameDecoder {
    type State (line 69) | private enum State {
    method HttpFrameDecoder (line 95) | public HttpFrameDecoder(boolean server, HttpFrameDecoderDelegate deleg...
    method HttpFrameDecoder (line 102) | public HttpFrameDecoder(boolean server, HttpFrameDecoderDelegate deleg...
    method decode (line 122) | public void decode(ByteBuf buffer) {
    method readFrameHeader (line 461) | private void readFrameHeader(ByteBuf buffer) {
    method hasFlag (line 470) | private static boolean hasFlag(byte flags, byte flag) {
    method frameHasPadding (line 474) | private static boolean frameHasPadding(int type, byte flags) {
    method getNextState (line 485) | private static State getNextState(int length, int type) {
    method isValidFrameHeader (line 526) | private static boolean isValidFrameHeader(int length, short type, byte...
    method isValidPaddingLength (line 586) | private static boolean isValidPaddingLength(

FILE: src/main/java/com/twitter/http2/HttpFrameDecoderDelegate.java
  type HttpFrameDecoderDelegate (line 23) | public interface HttpFrameDecoderDelegate {
    method readDataFramePadding (line 28) | void readDataFramePadding(int streamId, boolean endStream, int padding);
    method readDataFrame (line 33) | void readDataFrame(int streamId, boolean endStream, boolean endSegment...
    method readHeadersFrame (line 39) | void readHeadersFrame(
    method readPriorityFrame (line 51) | void readPriorityFrame(int streamId, boolean exclusive, int dependency...
    method readRstStreamFrame (line 56) | void readRstStreamFrame(int streamId, int errorCode);
    method readSettingsFrame (line 62) | void readSettingsFrame(boolean ack);
    method readSetting (line 67) | void readSetting(int id, int value);
    method readSettingsEnd (line 72) | void readSettingsEnd();
    method readPushPromiseFrame (line 78) | void readPushPromiseFrame(int streamId, int promisedStreamId);
    method readPingFrame (line 83) | void readPingFrame(long data, boolean ack);
    method readGoAwayFrame (line 88) | void readGoAwayFrame(int lastStreamId, int errorCode);
    method readWindowUpdateFrame (line 93) | void readWindowUpdateFrame(int streamId, int windowSizeIncrement);
    method readHeaderBlock (line 99) | void readHeaderBlock(ByteBuf headerBlockFragment);
    method readHeaderBlockEnd (line 104) | void readHeaderBlockEnd();
    method readFrameError (line 109) | void readFrameError(String message);

FILE: src/main/java/com/twitter/http2/HttpFrameEncoder.java
  class HttpFrameEncoder (line 45) | public class HttpFrameEncoder {
    method encodeDataFrame (line 50) | public ByteBuf encodeDataFrame(int streamId, boolean endStream, ByteBu...
    method encodeHeadersFrame (line 60) | public ByteBuf encodeHeadersFrame(
    method encodePriorityFrame (line 118) | public ByteBuf encodePriorityFrame(int streamId, boolean exclusive, in...
    method encodeRstStreamFrame (line 135) | public ByteBuf encodeRstStreamFrame(int streamId, int errorCode) {
    method encodeSettingsFrame (line 147) | public ByteBuf encodeSettingsFrame(HttpSettingsFrame httpSettingsFrame) {
    method encodePushPromiseFrame (line 164) | public ByteBuf encodePushPromiseFrame(int streamId, int promisedStream...
    method encodePingFrame (line 194) | public ByteBuf encodePingFrame(long data, boolean ack) {
    method encodeGoAwayFrame (line 207) | public ByteBuf encodeGoAwayFrame(int lastStreamId, int errorCode) {
    method encodeWindowUpdateFrame (line 221) | public ByteBuf encodeWindowUpdateFrame(int streamId, int windowSizeInc...
    method writeFrameHeader (line 230) | private void writeFrameHeader(ByteBuf buffer, int length, int type, by...

FILE: src/main/java/com/twitter/http2/HttpGoAwayFrame.java
  type HttpGoAwayFrame (line 21) | public interface HttpGoAwayFrame extends HttpFrame {
    method getLastStreamId (line 26) | int getLastStreamId();
    method setLastStreamId (line 31) | HttpGoAwayFrame setLastStreamId(int lastStreamId);
    method getErrorCode (line 36) | HttpErrorCode getErrorCode();
    method setErrorCode (line 41) | HttpGoAwayFrame setErrorCode(HttpErrorCode errorCode);

FILE: src/main/java/com/twitter/http2/HttpHeaderBlockDecoder.java
  class HttpHeaderBlockDecoder (line 29) | final class HttpHeaderBlockDecoder {
    method HttpHeaderBlockDecoder (line 36) | public HttpHeaderBlockDecoder(int maxHeaderSize, int maxHeaderTableSiz...
    method setMaxHeaderTableSize (line 46) | public void setMaxHeaderTableSize(int maxHeaderTableSize) {
    method decode (line 50) | public void decode(ByteBuf headerBlock, final HttpHeaderBlockFrame fra...
    method endHeaderBlock (line 74) | public void endHeaderBlock(final HttpHeaderBlockFrame frame) {
    class NullHeaderListener (line 90) | private static final class NullHeaderListener implements HeaderListener {
      method addHeader (line 91) | @Override
    class HeaderListenerImpl (line 97) | private static final class HeaderListenerImpl implements HeaderListener {
      method HeaderListenerImpl (line 101) | HeaderListenerImpl(HttpHeaders headers) {
      method addHeader (line 105) | @Override
      method addHeader (line 133) | private void addHeader(String name, String value) {

FILE: src/main/java/com/twitter/http2/HttpHeaderBlockEncoder.java
  class HttpHeaderBlockEncoder (line 30) | public class HttpHeaderBlockEncoder {
    method HttpHeaderBlockEncoder (line 43) | public HttpHeaderBlockEncoder(int maxHeaderTableSize) {
    method setEncoderMaxHeaderTableSize (line 55) | public void setEncoderMaxHeaderTableSize(int encoderMaxHeaderTableSize) {
    method setDecoderMaxHeaderTableSize (line 68) | public void setDecoderMaxHeaderTableSize(int decoderMaxHeaderTableSize) {
    method encode (line 78) | public ByteBuf encode(ChannelHandlerContext ctx, HttpHeaderBlockFrame ...

FILE: src/main/java/com/twitter/http2/HttpHeaderBlockFrame.java
  type HttpHeaderBlockFrame (line 20) | public interface HttpHeaderBlockFrame extends HttpStreamFrame {
    method isInvalid (line 26) | boolean isInvalid();
    method setInvalid (line 31) | HttpHeaderBlockFrame setInvalid();
    method isTruncated (line 36) | boolean isTruncated();
    method setTruncated (line 41) | HttpHeaderBlockFrame setTruncated();
    method headers (line 46) | HttpHeaders headers();

FILE: src/main/java/com/twitter/http2/HttpHeadersFrame.java
  type HttpHeadersFrame (line 21) | public interface HttpHeadersFrame extends HttpHeaderBlockFrame {
    method isLast (line 26) | boolean isLast();
    method setLast (line 31) | HttpHeadersFrame setLast(boolean last);
    method isExclusive (line 36) | boolean isExclusive();
    method setExclusive (line 41) | HttpHeadersFrame setExclusive(boolean exclusive);
    method getDependency (line 46) | int getDependency();
    method setDependency (line 51) | HttpHeadersFrame setDependency(int dependency);
    method getWeight (line 56) | int getWeight();
    method setWeight (line 62) | HttpHeadersFrame setWeight(int weight);
    method setStreamId (line 64) | @Override
    method setInvalid (line 67) | @Override
    method setTruncated (line 70) | @Override

FILE: src/main/java/com/twitter/http2/HttpMessageProxy.java
  class HttpMessageProxy (line 26) | class HttpMessageProxy implements HttpMessage {
    method HttpMessageProxy (line 30) | protected HttpMessageProxy(HttpMessage message) {
    method getProtocolVersion (line 34) | @Override
    method setProtocolVersion (line 39) | @Override
    method headers (line 45) | @Override
    method getDecoderResult (line 50) | @Override
    method setDecoderResult (line 56) | @Override
    method protocolVersion (line 61) | @Override
    method decoderResult (line 66) | @Override

FILE: src/main/java/com/twitter/http2/HttpPingFrame.java
  type HttpPingFrame (line 21) | public interface HttpPingFrame extends HttpFrame {
    method getData (line 26) | long getData();
    method setData (line 31) | HttpPingFrame setData(long data);
    method isPong (line 36) | boolean isPong();
    method setPong (line 41) | HttpPingFrame setPong(boolean pong);

FILE: src/main/java/com/twitter/http2/HttpPriorityFrame.java
  type HttpPriorityFrame (line 21) | public interface HttpPriorityFrame extends HttpFrame {
    method getStreamId (line 26) | int getStreamId();
    method setStreamId (line 31) | HttpPriorityFrame setStreamId(int streamId);
    method isExclusive (line 36) | boolean isExclusive();
    method setExclusive (line 41) | HttpPriorityFrame setExclusive(boolean exclusive);
    method getDependency (line 46) | int getDependency();
    method setDependency (line 51) | HttpPriorityFrame setDependency(int dependency);
    method getWeight (line 56) | int getWeight();
    method setWeight (line 62) | HttpPriorityFrame setWeight(int weight);

FILE: src/main/java/com/twitter/http2/HttpProtocolException.java
  class HttpProtocolException (line 18) | public class HttpProtocolException extends Exception {
    method HttpProtocolException (line 23) | public HttpProtocolException() {
    method HttpProtocolException (line 30) | public HttpProtocolException(String message, Throwable cause) {
    method HttpProtocolException (line 37) | public HttpProtocolException(String message) {
    method HttpProtocolException (line 44) | public HttpProtocolException(Throwable cause) {

FILE: src/main/java/com/twitter/http2/HttpPushPromiseFrame.java
  type HttpPushPromiseFrame (line 21) | public interface HttpPushPromiseFrame extends HttpHeaderBlockFrame {
    method getPromisedStreamId (line 26) | int getPromisedStreamId();
    method setPromisedStreamId (line 31) | HttpPushPromiseFrame setPromisedStreamId(int promisedStreamId);
    method setStreamId (line 33) | @Override
    method setInvalid (line 36) | @Override
    method setTruncated (line 39) | @Override

FILE: src/main/java/com/twitter/http2/HttpRequestProxy.java
  class HttpRequestProxy (line 25) | class HttpRequestProxy extends HttpMessageProxy implements HttpRequest {
    method HttpRequestProxy (line 29) | public HttpRequestProxy(HttpRequest request) {
    method httpRequest (line 34) | public HttpRequest httpRequest() {
    method getMethod (line 38) | @Override
    method setMethod (line 44) | @Override
    method getUri (line 50) | @Override
    method setUri (line 56) | @Override
    method setProtocolVersion (line 62) | @Override
    method toString (line 68) | @Override
    method method (line 73) | @Override
    method uri (line 78) | @Override

FILE: src/main/java/com/twitter/http2/HttpResponseProxy.java
  class HttpResponseProxy (line 25) | public class HttpResponseProxy extends HttpMessageProxy implements HttpR...
    method HttpResponseProxy (line 29) | public HttpResponseProxy(HttpResponse response) {
    method getStatus (line 34) | @Override
    method setStatus (line 40) | @Override
    method setProtocolVersion (line 46) | @Override
    method toString (line 52) | @Override
    method status (line 57) | @Override

FILE: src/main/java/com/twitter/http2/HttpRstStreamFrame.java
  type HttpRstStreamFrame (line 21) | public interface HttpRstStreamFrame extends HttpFrame {
    method getStreamId (line 26) | int getStreamId();
    method setStreamId (line 31) | HttpRstStreamFrame setStreamId(int streamId);
    method getErrorCode (line 36) | HttpErrorCode getErrorCode();
    method setErrorCode (line 41) | HttpRstStreamFrame setErrorCode(HttpErrorCode errorCode);

FILE: src/main/java/com/twitter/http2/HttpSettingsFrame.java
  type HttpSettingsFrame (line 23) | public interface HttpSettingsFrame extends HttpFrame {
    method getIds (line 36) | Set<Integer> getIds();
    method isSet (line 41) | boolean isSet(int id);
    method getValue (line 47) | int getValue(int id);
    method setValue (line 53) | HttpSettingsFrame setValue(int id, int value);
    method removeValue (line 58) | HttpSettingsFrame removeValue(int id);
    method isAck (line 63) | boolean isAck();
    method setAck (line 68) | HttpSettingsFrame setAck(boolean ack);

FILE: src/main/java/com/twitter/http2/HttpStreamDecoder.java
  class HttpStreamDecoder (line 38) | public class HttpStreamDecoder extends MessageToMessageDecoder<Object> {
    method decode (line 48) | @Override
    method createHttpRequest (line 188) | private StreamedHttpRequest createHttpRequest(HttpHeadersFrame httpHea...
    method createHttpResponse (line 234) | private StreamedHttpResponse createHttpResponse(HttpHeadersFrame httpH...

FILE: src/main/java/com/twitter/http2/HttpStreamEncoder.java
  class HttpStreamEncoder (line 39) | public class HttpStreamEncoder extends ChannelOutboundHandlerAdapter {
    method write (line 45) | @Override
    method getMessageFuture (line 89) | private ChannelPromise getMessageFuture(
    class ChunkListener (line 129) | private class ChunkListener implements FutureListener<HttpContent> {
      method ChunkListener (line 135) | ChunkListener(
      method operationComplete (line 147) | @Override
    method writeChunk (line 200) | protected void writeChunk(
    method getFrameFuture (line 245) | private static ChannelPromise getFrameFuture(
    class HttpFrameWriter (line 256) | private static class HttpFrameWriter implements ChannelFutureListener {
      method HttpFrameWriter (line 262) | HttpFrameWriter(ChannelHandlerContext ctx, ChannelPromise promise, O...
      method operationComplete (line 268) | public void operationComplete(ChannelFuture future) throws Exception {
    method createHttpHeadersFrame (line 281) | private HttpHeadersFrame createHttpHeadersFrame(HttpRequest httpRequest)
    method createHttpHeadersFrame (line 314) | private HttpHeadersFrame createHttpHeadersFrame(HttpResponse httpRespo...
    method createHttpDataFrames (line 340) | private HttpDataFrame[] createHttpDataFrames(int streamId, ByteBuf con...

FILE: src/main/java/com/twitter/http2/HttpStreamFrame.java
  type HttpStreamFrame (line 21) | public interface HttpStreamFrame extends HttpFrame {
    method getStreamId (line 26) | int getStreamId();
    method setStreamId (line 31) | HttpStreamFrame setStreamId(int streamId);

FILE: src/main/java/com/twitter/http2/HttpWindowUpdateFrame.java
  type HttpWindowUpdateFrame (line 21) | public interface HttpWindowUpdateFrame extends HttpFrame {
    method getStreamId (line 26) | int getStreamId();
    method setStreamId (line 31) | HttpWindowUpdateFrame setStreamId(int streamId);
    method getWindowSizeIncrement (line 36) | int getWindowSizeIncrement();
    method setWindowSizeIncrement (line 42) | HttpWindowUpdateFrame setWindowSizeIncrement(int deltaWindowSize);

FILE: src/main/java/com/twitter/http2/Pipe.java
  class Pipe (line 34) | public class Pipe<T> {
    class Node (line 51) | private final class Node {
      method Node (line 55) | Node(T message, Promise<Void> promise) {
    method Pipe (line 67) | public Pipe() {
    method send (line 84) | public Future<Void> send(T message) {
    method receive (line 111) | public Future<T> receive() {
    method close (line 135) | public void close() {
    method isClosed (line 153) | public synchronized boolean isClosed() {

FILE: src/main/java/com/twitter/http2/StreamedHttpMessage.java
  type StreamedHttpMessage (line 27) | public interface StreamedHttpMessage extends HttpMessage {
    method getContent (line 37) | Pipe<HttpContent> getContent();
    method addContent (line 50) | Future<Void> addContent(HttpContent content);

FILE: src/main/java/com/twitter/http2/StreamedHttpRequest.java
  class StreamedHttpRequest (line 29) | public class StreamedHttpRequest extends HttpRequestProxy implements Str...
    method StreamedHttpRequest (line 33) | public StreamedHttpRequest(HttpVersion version, HttpMethod method, Str...
    method StreamedHttpRequest (line 37) | public StreamedHttpRequest(HttpRequest request) {
    method getContent (line 41) | @Override
    method addContent (line 46) | @Override

FILE: src/main/java/com/twitter/http2/StreamedHttpResponse.java
  class StreamedHttpResponse (line 29) | public class StreamedHttpResponse extends HttpResponseProxy implements S...
    method StreamedHttpResponse (line 33) | public StreamedHttpResponse(HttpVersion version, HttpResponseStatus st...
    method StreamedHttpResponse (line 37) | public StreamedHttpResponse(HttpResponse response) {
    method getContent (line 41) | @Override
    method addContent (line 46) | @Override

FILE: src/test/java/com/twitter/http2/HttpFrameDecoderTest.java
  class HttpFrameDecoderTest (line 38) | public class HttpFrameDecoderTest {
    method createHandler (line 46) | @Before
    method testClientConnectionPreface (line 52) | @Test
    method testInvalidClientConnectionPreface (line 71) | @Test
    method testHttpDataFrame (line 86) | @Test
    method testEmptyHttpDataFrame (line 106) | @Test
    method testLastHttpDataFrame (line 120) | @Test
    method testLastSegmentHttpDataFrame (line 134) | @Test
    method testPaddedHttpDataFrame (line 148) | @Test
    method testHttpDataFrameReservedBits (line 176) | @Test
    method testIllegalHttpDataFrameStreamId (line 191) | @Test
    method testIllegalHttpDataFrameLength (line 204) | @Test
    method testIllegalHttpDataFramePaddingLength (line 217) | @Test
    method testHttpHeadersFrame (line 231) | @Test
    method testLastHttpHeadersFrame (line 255) | @Test
    method testLastSegmentHttpHeadersFrame (line 271) | @Test
    method testHttpHeadersFrameReservedBits (line 287) | @Test
    method testInvalidHttpHeadersFrame (line 304) | @Test
    method testIllegalHttpHeadersFrame (line 317) | @Test
    method testContinuedHttpHeadersFrame (line 330) | @Test
    method testHttpHeadersFrameEmptyContinuation (line 356) | @Test
    method testHttpHeadersFrameMultipleContinuations (line 376) | @Test
    method testHttpHeadersFrameContinuationReservedFlags (line 403) | @Test
    method testHttpHeadersFrameContinuationIllegalStreamId (line 422) | @Test
    method testHttpHeadersFrameMissingContinuation (line 443) | @Test
    method testHttpPriorityFrame (line 464) | @Test
    method testHttpPriorityFrameReservedBits (line 481) | @Test
    method testInvalidHttpPriorityFrame (line 499) | @Test
    method testIllegalHttpPriorityFrame (line 516) | @Test
    method testHttpRstStreamFrame (line 533) | @Test
    method testHttpRstStreamFrameReservedBits (line 548) | @Test
    method testInvalidHttpRstStreamFrame (line 564) | @Test
    method testIllegalHttpRstStreamFrame (line 579) | @Test
    method testHttpSettingsFrame (line 594) | @Test
    method testHttpSettingsAckFrame (line 614) | @Test
    method testHttpSettingsFrameWithMultiples (line 629) | @Test
    method testHttpSettingsFrameReservedBits (line 653) | @Test
    method testInvalidHttpSettingsFrame (line 674) | @Test
    method testInvalidHttpSettingsAckFrame (line 691) | @Test
    method testIllegalHttpSettingsFrame (line 708) | @Test
    method testIllegalHttpSettingsAckFrame (line 725) | @Test
    method testHttpPushPromiseFrame (line 738) | @Test
    method testHttpPushPromiseFrameReservedBits (line 759) | @Test
    method testIllegalHttpPushPromiseFrame (line 777) | @Test
    method testContinuedHttpPushPromiseFrame (line 792) | @Test
    method testHttpPushPromiseFrameEmptyContinuation (line 815) | @Test
    method testHttpPushPromiseFrameMultipleContinuations (line 837) | @Test
    method testHttpPushPromiseFrameContinuationReservedFlags (line 861) | @Test
    method testHttpPushPromiseFrameContinuationIllegalStreamId (line 883) | @Test
    method testHttpPushPromiseFrameMissingContinuation (line 906) | @Test
    method testHttpPingFrame (line 924) | @Test
    method testHttpPongFrame (line 939) | @Test
    method testHttpPingFrameReservedBits (line 954) | @Test
    method testInvalidHttpPingFrame (line 969) | @Test
    method testIllegalHttpPingFrame (line 984) | @Test
    method testHttpGoAwayFrame (line 999) | @Test
    method testHttpGoAwayFrameReservedBits (line 1016) | @Test
    method testHttpGoAwayFrameWithDebugData (line 1034) | @Test
    method testInvalidHttpGoAwayFrame (line 1053) | @Test
    method testIllegalHttpGoAwayFrame (line 1068) | @Test
    method testHttpWindowUpdateFrame (line 1085) | @Test
    method testHttpWindowUpdateFrameReservedBits (line 1100) | @Test
    method testInvalidHttpWindowUpdateFrame (line 1116) | @Test
    method testIllegalHttpWindowUpdateFrame (line 1131) | @Test
    method testIllegalContinuationFrame (line 1146) | @Test
    method testUnknownFrame (line 1159) | @Test
    method frame (line 1172) | private ByteBuf frame(int length, int type, byte flags, int streamId) {
    method dataFrame (line 1183) | private ByteBuf dataFrame(int length, byte flags, int streamId) {
    method headersFrame (line 1188) | private ByteBuf headersFrame(int length, byte flags, int streamId) {
    method priorityFrame (line 1193) | private ByteBuf priorityFrame(int length, byte flags, int streamId) {
    method rstStreamFrame (line 1198) | private ByteBuf rstStreamFrame(int length, byte flags, int streamId) {
    method settingsFrame (line 1203) | private ByteBuf settingsFrame(int length, byte flags, int streamId) {
    method pushPromiseFrame (line 1208) | private ByteBuf pushPromiseFrame(int length, byte flags, int streamId) {
    method pingFrame (line 1213) | private ByteBuf pingFrame(int length, byte flags, int streamId) {
    method goAwayFrame (line 1218) | private ByteBuf goAwayFrame(int length, byte flags, int streamId) {
    method windowUpdateFrame (line 1223) | private ByteBuf windowUpdateFrame(int length, byte flags, int streamId) {
    method continuationFrame (line 1228) | private ByteBuf continuationFrame(int length, byte flags, int streamId) {
    method setReservedBits (line 1233) | private void setReservedBits(ByteBuf frame) {
    method writeRandomData (line 1237) | private void writeRandomData(ByteBuf frame, int length) {
    method writePriorityFields (line 1243) | private void writePriorityFields(ByteBuf frame, boolean exclusive, int...

FILE: src/test/java/com/twitter/http2/HttpFrameEncoderTest.java
  class HttpFrameEncoderTest (line 33) | public class HttpFrameEncoderTest {
    method testHttpDataFrame (line 39) | @Test
    method testEmptyHttpDataFrame (line 52) | @Test
    method testLastHttpDataFrame (line 61) | @Test
    method testHttpHeadersFrame (line 70) | @Test
    method testEmptyHttpHeadersFrame (line 87) | @Test
    method testLastHttpHeadersFrame (line 101) | @Test
    method testContinuedHttpHeadersFrame (line 115) | @Test
    method testHttpPriorityFrame (line 133) | @Test
    method testHttpRstStreamFrame (line 145) | @Test
    method testHttpSettingsFrame (line 155) | @Test
    method testHttpSettingsAckFrame (line 167) | @Test
    method testHttpPushPromiseFrame (line 177) | @Test
    method testEmptyHttpPushPromiseFrame (line 191) | @Test
    method testContinuedHttpPushPromiseFrame (line 201) | @Test
    method testHttpPingFrame (line 215) | @Test
    method testHttpPongFrame (line 224) | @Test
    method testHttpGoAwayFrame (line 233) | @Test
    method testHttpWindowUpdateFrame (line 243) | @Test
    method assertDataFrame (line 253) | private static void assertDataFrame(ByteBuf frame, int streamId, boole...
    method assertHeadersFrame (line 267) | private static void assertHeadersFrame(
    method assertPriorityFrame (line 320) | private static void assertPriorityFrame(
    method assertRstStreamFrame (line 335) | private static void assertRstStreamFrame(ByteBuf frame, int streamId, ...
    method assertSettingsFrame (line 343) | private static void assertSettingsFrame(ByteBuf frame, boolean ack, in...
    method assertPushPromiseFrame (line 357) | private static void assertPushPromiseFrame(
    method assertPingFrame (line 388) | private static void assertPingFrame(ByteBuf frame, boolean pong, long ...
    method assertGoAwayFrame (line 399) | private static void assertGoAwayFrame(ByteBuf frame, int lastStreamId,...
    method assertWindowUpdateFrame (line 408) | private static void assertWindowUpdateFrame(
    method assertFrameHeader (line 419) | private static int assertFrameHeader(ByteBuf frame, byte type, byte fl...

FILE: src/test/java/com/twitter/http2/HttpHeaderCompressionTest.java
  class HttpHeaderCompressionTest (line 41) | public class HttpHeaderCompressionTest {
    method testHttpHeadersFrame (line 43) | @Test
    method testHeaderEcho (line 50) | private void testHeaderEcho(HttpHeaderBlockFrame frame) throws Throwab...
    class EchoHandler (line 111) | private static class EchoHandler extends ChannelInboundHandlerAdapter {
      method channelRead (line 114) | @Override
      method exceptionCaught (line 119) | @Override
    class TestHandler (line 127) | private static class TestHandler extends ChannelInboundHandlerAdapter {
      method TestHandler (line 136) | public TestHandler(HttpHeaderBlockFrame frame) {
      method channelActive (line 140) | @Override
      method channelRead (line 146) | @Override
      method exceptionCaught (line 163) | @Override

FILE: src/test/java/com/twitter/http2/HttpRequestProxyTest.java
  class HttpRequestProxyTest (line 27) | public class HttpRequestProxyTest {
    method testHttpRequest (line 33) | @Test
    method testGetMethod (line 38) | @Test
    method testSetMethod (line 43) | @Test
    method testGetUri (line 49) | @Test
    method testSetUri (line 54) | @Test
    method testToString (line 60) | @Test
    method testGetProtocolVersion (line 65) | @Test
    method testSetProtocolVersion (line 70) | @Test
    method testHeaders (line 76) | @Test

FILE: src/test/java/com/twitter/http2/HttpResponseProxyTest.java
  class HttpResponseProxyTest (line 27) | public class HttpResponseProxyTest {
    method testSetStatus (line 33) | @Test
    method testGetStatus (line 39) | @Test
    method testToString (line 44) | @Test
    method testGetProtocolVersion (line 49) | @Test
    method testSetProtocolVersion (line 54) | @Test
    method testHeaders (line 60) | @Test

FILE: src/test/java/com/twitter/http2/PipeTest.java
  class PipeTest (line 28) | public class PipeTest {
    method createPipe (line 51) | @Before
    method testS (line 56) | @Test
    method testR (line 62) | @Test
    method testSR (line 68) | @Test
    method testRS (line 78) | @Test
    method testSS (line 88) | @Test
    method testRR (line 98) | @Test
    method testSRR (line 107) | @Test
    method testSSR (line 122) | @Test
    method testSRS (line 137) | @Test
    method testRSR (line 152) | @Test
    method testRSS (line 167) | @Test
    method testRRS (line 182) | @Test
    method testSSRR (line 197) | @Test
    method testRRSS (line 218) | @Test
    method testRSSR (line 239) | @Test
    method testSRRS (line 260) | @Test
    method testSSRRR (line 281) | @Test
    method testCS (line 308) | @Test
    method testSC (line 316) | @Test
    method testCR (line 325) | @Test
    method testRC (line 333) | @Test
    method testCSR (line 342) | @Test
    method testSCR (line 354) | @Test
    method testSRCS (line 367) | @Test
    method testSRCR (line 382) | @Test
    method testCRS (line 398) | @Test
    method testRCS (line 410) | @Test
    method testRSCR (line 423) | @Test
    method testRSCS (line 440) | @Test
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (324K chars).
[
  {
    "path": ".gitignore",
    "chars": 19,
    "preview": "target\n.idea\n*.iml\n"
  },
  {
    "path": ".travis.yml",
    "chars": 114,
    "preview": "language: java\nsudo: false\njdk:\n  - openjdk7\nafter_success:\n  - mvn clean cobertura:cobertura coveralls:cobertura\n"
  },
  {
    "path": "LICENSE",
    "chars": 11325,
    "preview": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licens"
  },
  {
    "path": "README.md",
    "chars": 251,
    "preview": "# netty-http2 [![Build Status](https://travis-ci.org/twitter/netty-http2.svg)](https://travis-ci.org/twitter/netty-http2"
  },
  {
    "path": "pom.xml",
    "chars": 7386,
    "preview": "<project xmlns=\"https://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"https://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLoc"
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttp2Headers.java",
    "chars": 1995,
    "preview": "package com.twitter.http2;\n\nimport io.netty.handler.codec.DefaultHeaders.NameValidator;\nimport io.netty.handler.codec.ht"
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpDataFrame.java",
    "chars": 4200,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpGoAwayFrame.java",
    "chars": 2560,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpHeaderBlockFrame.java",
    "chars": 2040,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpHeadersFrame.java",
    "chars": 3768,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpPingFrame.java",
    "chars": 1772,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpPriorityFrame.java",
    "chars": 3356,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpPushPromiseFrame.java",
    "chars": 2656,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpRstStreamFrame.java",
    "chars": 2510,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpSettingsFrame.java",
    "chars": 2821,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpStreamFrame.java",
    "chars": 1362,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/DefaultHttpWindowUpdateFrame.java",
    "chars": 2542,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpCodecUtil.java",
    "chars": 4031,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpConnection.java",
    "chars": 14207,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpConnectionHandler.java",
    "chars": 46013,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpDataFrame.java",
    "chars": 1574,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpErrorCode.java",
    "chars": 4923,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpFrame.java",
    "chars": 704,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpFrameDecoder.java",
    "chars": 20769,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpFrameDecoderDelegate.java",
    "chars": 3095,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpFrameEncoder.java",
    "chars": 9376,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpGoAwayFrame.java",
    "chars": 1179,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpHeaderBlockDecoder.java",
    "chars": 5093,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpHeaderBlockEncoder.java",
    "chars": 5033,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpHeaderBlockFrame.java",
    "chars": 1349,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpHeadersFrame.java",
    "chars": 1921,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpMessageProxy.java",
    "chars": 1853,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpPingFrame.java",
    "chars": 1110,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpPriorityFrame.java",
    "chars": 1728,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpProtocolException.java",
    "chars": 1200,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpPushPromiseFrame.java",
    "chars": 1191,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpRequestProxy.java",
    "chars": 1970,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpResponseProxy.java",
    "chars": 1667,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpRstStreamFrame.java",
    "chars": 1187,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpSettingsFrame.java",
    "chars": 1872,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpStreamDecoder.java",
    "chars": 11481,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpStreamEncoder.java",
    "chars": 14263,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpStreamFrame.java",
    "chars": 998,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/HttpWindowUpdateFrame.java",
    "chars": 1280,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/Pipe.java",
    "chars": 4774,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/StreamedHttpMessage.java",
    "chars": 2067,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/StreamedHttpRequest.java",
    "chars": 1744,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/main/java/com/twitter/http2/StreamedHttpResponse.java",
    "chars": 1754,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/HttpFrameDecoderTest.java",
    "chars": 47329,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/HttpFrameEncoderTest.java",
    "chars": 16214,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/HttpHeaderCompressionTest.java",
    "chars": 6371,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/HttpRequestProxyTest.java",
    "chars": 2595,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/HttpResponseProxyTest.java",
    "chars": 2216,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "src/test/java/com/twitter/http2/PipeTest.java",
    "chars": 13813,
    "preview": "/*\n * Copyright 2015 Twitter, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  }
]

About this extraction

This page contains the full source code of the twitter/netty-http2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (303.3 KB), approximately 67.2k tokens, and a symbol index with 589 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!