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 [](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
================================================
4.0.0com.twitternetty-http20.17.12-SNAPSHOTHTTP/2http://github.com/twitter/netty-http2HTTP/2 for Nettyscm:git:git@github.com:twitter/netty-http2.gitscm:git:git@github.com:twitter/netty-http2.gitscm:git:git@github.com:twitter/netty-http2.gitThe Apache Software License, Version 2.0http://www.apache.org/licenses/LICENSE-2.0.txtJeff Pinnerjpinner@twitter.com1.61.6UTF-8UTF-8sonatype-nexus-snapshotsSonatype OSShttps://oss.sonatype.org/content/repositories/snapshotssonatype-nexus-stagingNexus Release Repositoryhttps://oss.sonatype.org/service/local/staging/deploy/maven2/com.twitterhpack1.0.2io.nettynetty-codec-http4.1.0.CR4junitjunit4.12testorg.mockitomockito-core1.9.5testsonatype-nexus-snapshotshttps://oss.sonatype.org/content/repositories/snapshotsfalsetrueorg.apache.maven.pluginsmaven-release-plugin2.1forked-pathfalse-Psonatype-oss-releaseorg.apache.maven.pluginsmaven-compiler-plugin2.5.11.61.6org.apache.maven.pluginsmaven-surefire-plugin2.12-Xmx1024mfalse**/Test*.java**/*Test.java**/*Spec.javaorg.codehaus.mojocobertura-maven-plugin2.5.2xml256mtrueorg.eluder.coverallscoveralls-maven-plugin2.2.0sonatype-oss-releaseorg.apache.maven.pluginsmaven-source-plugin2.1.2attach-sourcesjar-no-forkorg.apache.maven.pluginsmaven-javadoc-plugin2.7attach-javadocsjarorg.apache.maven.pluginsmaven-gpg-plugin1.1sign-artifactsverifysign
================================================
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 Http2NameValidator = new NameValidator() {
@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 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 settingsMap = new TreeMap();
private boolean ack;
@Override
public Set 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> getSettings() {
return settingsMap.entrySet();
}
private void appendSettings(StringBuilder buf) {
for (Map.Entry 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 streams = new ConcurrentHashMap();
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 pendingWriteQueue =
new ConcurrentLinkedQueue();
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 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 children = new TreeSet(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 {
@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