├── .gitignore
├── .travis.yml
├── README.md
├── src
├── main
│ └── java
│ │ └── com
│ │ └── twitter
│ │ └── http2
│ │ ├── HttpFrame.java
│ │ ├── HttpStreamFrame.java
│ │ ├── HttpPingFrame.java
│ │ ├── HttpGoAwayFrame.java
│ │ ├── HttpRstStreamFrame.java
│ │ ├── HttpPushPromiseFrame.java
│ │ ├── HttpProtocolException.java
│ │ ├── HttpWindowUpdateFrame.java
│ │ ├── HttpHeaderBlockFrame.java
│ │ ├── DefaultHttpStreamFrame.java
│ │ ├── HttpDataFrame.java
│ │ ├── HttpResponseProxy.java
│ │ ├── StreamedHttpRequest.java
│ │ ├── StreamedHttpResponse.java
│ │ ├── HttpPriorityFrame.java
│ │ ├── DefaultHttpPingFrame.java
│ │ ├── HttpMessageProxy.java
│ │ ├── HttpSettingsFrame.java
│ │ ├── HttpHeadersFrame.java
│ │ ├── DefaultHttp2Headers.java
│ │ ├── StreamedHttpMessage.java
│ │ ├── HttpRequestProxy.java
│ │ ├── DefaultHttpHeaderBlockFrame.java
│ │ ├── DefaultHttpRstStreamFrame.java
│ │ ├── DefaultHttpWindowUpdateFrame.java
│ │ ├── DefaultHttpGoAwayFrame.java
│ │ ├── DefaultHttpPushPromiseFrame.java
│ │ ├── DefaultHttpSettingsFrame.java
│ │ ├── HttpFrameDecoderDelegate.java
│ │ ├── DefaultHttpPriorityFrame.java
│ │ ├── DefaultHttpHeadersFrame.java
│ │ ├── HttpCodecUtil.java
│ │ ├── DefaultHttpDataFrame.java
│ │ ├── Pipe.java
│ │ ├── HttpHeaderBlockEncoder.java
│ │ ├── HttpHeaderBlockDecoder.java
│ │ ├── HttpErrorCode.java
│ │ ├── HttpFrameEncoder.java
│ │ ├── HttpStreamDecoder.java
│ │ ├── HttpStreamEncoder.java
│ │ └── HttpConnection.java
└── test
│ └── java
│ └── com
│ └── twitter
│ └── http2
│ ├── HttpResponseProxyTest.java
│ ├── HttpRequestProxyTest.java
│ ├── HttpHeaderCompressionTest.java
│ ├── PipeTest.java
│ └── HttpFrameEncoderTest.java
├── pom.xml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | .idea
3 | *.iml
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | sudo: false
3 | jdk:
4 | - openjdk7
5 | after_success:
6 | - mvn clean cobertura:cobertura coveralls:cobertura
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # netty-http2 [](https://travis-ci.org/twitter/netty-http2)
2 |
3 | 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
4 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 Frame
20 | */
21 | public interface HttpFrame {
22 | // Tag interface
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpStreamFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 Frame that is associated with an individual stream
20 | */
21 | public interface HttpStreamFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the stream identifier of this frame.
25 | */
26 | int getStreamId();
27 |
28 | /**
29 | * Sets the stream identifier of this frame. The stream identifier must be positive.
30 | */
31 | HttpStreamFrame setStreamId(int streamId);
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpPingFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 PING Frame
20 | */
21 | public interface HttpPingFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the data payload of this frame.
25 | */
26 | long getData();
27 |
28 | /**
29 | * Sets the data payload of this frame.
30 | */
31 | HttpPingFrame setData(long data);
32 |
33 | /**
34 | * Returns {@code true} if this frame is in response to a PING.
35 | */
36 | boolean isPong();
37 |
38 | /**
39 | * Sets if this frame is in response to a PING.
40 | */
41 | HttpPingFrame setPong(boolean pong);
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpGoAwayFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 GOAWAY Frame
20 | */
21 | public interface HttpGoAwayFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the Last-Stream-ID of this frame.
25 | */
26 | int getLastStreamId();
27 |
28 | /**
29 | * Sets the Last-Stream-ID of this frame. The Last-Stream-ID cannot be negative.
30 | */
31 | HttpGoAwayFrame setLastStreamId(int lastStreamId);
32 |
33 | /**
34 | * Returns the error code of this frame.
35 | */
36 | HttpErrorCode getErrorCode();
37 |
38 | /**
39 | * Sets the error code of this frame.
40 | */
41 | HttpGoAwayFrame setErrorCode(HttpErrorCode errorCode);
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpRstStreamFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 RST_STREAM Frame
20 | */
21 | public interface HttpRstStreamFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the stream identifier of this frame.
25 | */
26 | int getStreamId();
27 |
28 | /**
29 | * Sets the stream identifier of this frame. The stream identifier must be positive.
30 | */
31 | HttpRstStreamFrame setStreamId(int streamId);
32 |
33 | /**
34 | * Returns the error code of this frame.
35 | */
36 | HttpErrorCode getErrorCode();
37 |
38 | /**
39 | * Sets the error code of this frame.
40 | */
41 | HttpRstStreamFrame setErrorCode(HttpErrorCode errorCode);
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpPushPromiseFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 PUSH_PROMISE Frame
20 | */
21 | public interface HttpPushPromiseFrame extends HttpHeaderBlockFrame {
22 |
23 | /**
24 | * Returns the Promised-Stream-ID of this frame.
25 | */
26 | int getPromisedStreamId();
27 |
28 | /**
29 | * Sets the Promised-Stream-ID of this frame. The Promised-Stream-ID must be positive.
30 | */
31 | HttpPushPromiseFrame setPromisedStreamId(int promisedStreamId);
32 |
33 | @Override
34 | HttpPushPromiseFrame setStreamId(int streamId);
35 |
36 | @Override
37 | HttpPushPromiseFrame setInvalid();
38 |
39 | @Override
40 | HttpPushPromiseFrame setTruncated();
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpProtocolException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | public class HttpProtocolException extends Exception {
19 |
20 | /**
21 | * Creates a new instance.
22 | */
23 | public HttpProtocolException() {
24 | super();
25 | }
26 |
27 | /**
28 | * Creates a new instance.
29 | */
30 | public HttpProtocolException(String message, Throwable cause) {
31 | super(message, cause);
32 | }
33 |
34 | /**
35 | * Creates a new instance.
36 | */
37 | public HttpProtocolException(String message) {
38 | super(message);
39 | }
40 |
41 | /**
42 | * Creates a new instance.
43 | */
44 | public HttpProtocolException(Throwable cause) {
45 | super(cause);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpWindowUpdateFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 WINDOW_UPDATE Frame
20 | */
21 | public interface HttpWindowUpdateFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the stream identifier of this frame.
25 | */
26 | int getStreamId();
27 |
28 | /**
29 | * Sets the stream identifier of this frame. The stream identifier cannot be negative.
30 | */
31 | HttpWindowUpdateFrame setStreamId(int streamId);
32 |
33 | /**
34 | * Returns the Window-Size-Increment of this frame.
35 | */
36 | int getWindowSizeIncrement();
37 |
38 | /**
39 | * Sets the Window-Size-Increment of this frame.
40 | * The Window-Size-Increment must be positive.
41 | */
42 | HttpWindowUpdateFrame setWindowSizeIncrement(int deltaWindowSize);
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpHeaderBlockFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.HttpHeaders;
19 |
20 | public interface HttpHeaderBlockFrame extends HttpStreamFrame {
21 |
22 | /**
23 | * Returns {@code true} if this header block is invalid.
24 | * A RST_STREAM frame with code PROTOCOL_ERROR should be sent.
25 | */
26 | boolean isInvalid();
27 |
28 | /**
29 | * Marks this header block as invalid.
30 | */
31 | HttpHeaderBlockFrame setInvalid();
32 |
33 | /**
34 | * Returns {@code true} if this header block has been truncated due to length restrictions.
35 | */
36 | boolean isTruncated();
37 |
38 | /**
39 | * Mark this header block as truncated.
40 | */
41 | HttpHeaderBlockFrame setTruncated();
42 |
43 | /**
44 | * Returns the {@link HttpHeaders} of this frame.
45 | */
46 | HttpHeaders headers();
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpStreamFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * The default {@link HttpStreamFrame} implementation.
20 | */
21 | public abstract class DefaultHttpStreamFrame implements HttpStreamFrame {
22 |
23 | private int streamId;
24 |
25 | /**
26 | * Creates a new instance.
27 | *
28 | * @param streamId the stream identifier of this frame
29 | */
30 | protected DefaultHttpStreamFrame(int streamId) {
31 | setStreamId(streamId);
32 | }
33 |
34 | @Override
35 | public int getStreamId() {
36 | return streamId;
37 | }
38 |
39 | @Override
40 | public HttpStreamFrame setStreamId(int streamId) {
41 | if (streamId <= 0) {
42 | throw new IllegalArgumentException(
43 | "Stream identifier must be positive: " + streamId);
44 | }
45 | this.streamId = streamId;
46 | return this;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpDataFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.buffer.ByteBuf;
19 | import io.netty.buffer.ByteBufHolder;
20 |
21 | /**
22 | * An HTTP/2 DATA Frame
23 | */
24 | public interface HttpDataFrame extends ByteBufHolder, HttpStreamFrame {
25 |
26 | /**
27 | * Returns {@code true} if this frame is the last frame to be transmitted on the stream.
28 | */
29 | boolean isLast();
30 |
31 | /**
32 | * Sets if this frame is the last frame to be transmitted on the stream.
33 | */
34 | HttpDataFrame setLast(boolean last);
35 |
36 | @Override
37 | HttpDataFrame setStreamId(int streamId);
38 |
39 | /**
40 | * Returns the data payload of this frame. If there is no data payload
41 | * {@link io.netty.buffer.Unpooled#EMPTY_BUFFER} is returned.
42 | *
43 | * The data payload cannot exceed 16384 bytes.
44 | */
45 | @Override
46 | ByteBuf content();
47 |
48 | @Override
49 | HttpDataFrame copy();
50 |
51 | @Override
52 | HttpDataFrame duplicate();
53 |
54 | @Override
55 | HttpDataFrame retain();
56 |
57 | @Override
58 | HttpDataFrame retain(int increment);
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpResponseProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.HttpResponse;
19 | import io.netty.handler.codec.http.HttpResponseStatus;
20 | import io.netty.handler.codec.http.HttpVersion;
21 |
22 | /**
23 | * An {@link HttpResponse} decorator.
24 | */
25 | public class HttpResponseProxy extends HttpMessageProxy implements HttpResponse {
26 |
27 | private final HttpResponse response;
28 |
29 | public HttpResponseProxy(HttpResponse response) {
30 | super(response);
31 | this.response = response;
32 | }
33 |
34 | @Override
35 | @Deprecated
36 | public HttpResponseStatus getStatus() {
37 | return response.getStatus();
38 | }
39 |
40 | @Override
41 | public HttpResponse setStatus(HttpResponseStatus status) {
42 | response.setStatus(status);
43 | return this;
44 | }
45 |
46 | @Override
47 | public HttpResponse setProtocolVersion(HttpVersion version) {
48 | response.setProtocolVersion(version);
49 | return this;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return response.toString();
55 | }
56 |
57 | @Override
58 | public HttpResponseStatus status() {
59 | return response.status();
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/StreamedHttpRequest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.DefaultHttpRequest;
19 | import io.netty.handler.codec.http.HttpContent;
20 | import io.netty.handler.codec.http.HttpMethod;
21 | import io.netty.handler.codec.http.HttpRequest;
22 | import io.netty.handler.codec.http.HttpVersion;
23 | import io.netty.handler.codec.http.LastHttpContent;
24 | import io.netty.util.concurrent.Future;
25 |
26 | /**
27 | * An {@link HttpRequest} that adds content streaming.
28 | */
29 | public class StreamedHttpRequest extends HttpRequestProxy implements StreamedHttpMessage {
30 |
31 | private Pipe pipe = new Pipe();
32 |
33 | public StreamedHttpRequest(HttpVersion version, HttpMethod method, String uri) {
34 | this(new DefaultHttpRequest(version, method, uri));
35 | }
36 |
37 | public StreamedHttpRequest(HttpRequest request) {
38 | super(request);
39 | }
40 |
41 | @Override
42 | public Pipe getContent() {
43 | return pipe;
44 | }
45 |
46 | @Override
47 | public Future addContent(HttpContent content) {
48 | Future future = pipe.send(content);
49 | if (content instanceof LastHttpContent) {
50 | pipe.close();
51 | }
52 | return future;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/StreamedHttpResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.DefaultHttpResponse;
19 | import io.netty.handler.codec.http.HttpContent;
20 | import io.netty.handler.codec.http.HttpResponse;
21 | import io.netty.handler.codec.http.HttpResponseStatus;
22 | import io.netty.handler.codec.http.HttpVersion;
23 | import io.netty.handler.codec.http.LastHttpContent;
24 | import io.netty.util.concurrent.Future;
25 |
26 | /**
27 | * An {@link HttpResponse} that adds content streaming.
28 | */
29 | public class StreamedHttpResponse extends HttpResponseProxy implements StreamedHttpMessage {
30 |
31 | private Pipe pipe = new Pipe();
32 |
33 | public StreamedHttpResponse(HttpVersion version, HttpResponseStatus status) {
34 | this(new DefaultHttpResponse(version, status));
35 | }
36 |
37 | public StreamedHttpResponse(HttpResponse response) {
38 | super(response);
39 | }
40 |
41 | @Override
42 | public Pipe getContent() {
43 | return pipe;
44 | }
45 |
46 | @Override
47 | public Future addContent(HttpContent content) {
48 | Future future = pipe.send(content);
49 | if (content instanceof LastHttpContent) {
50 | pipe.close();
51 | }
52 | return future;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpPriorityFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 PRIORITY Frame
20 | */
21 | public interface HttpPriorityFrame extends HttpFrame {
22 |
23 | /**
24 | * Returns the stream identifier of this frame.
25 | */
26 | int getStreamId();
27 |
28 | /**
29 | * Sets the stream identifier of this frame. The stream identifier must be positive.
30 | */
31 | HttpPriorityFrame setStreamId(int streamId);
32 |
33 | /**
34 | * Returns {@code true} if the dependency of the stream is exclusive.
35 | */
36 | boolean isExclusive();
37 |
38 | /**
39 | * Sets if the dependency of the stream is exclusive.
40 | */
41 | HttpPriorityFrame setExclusive(boolean exclusive);
42 |
43 | /**
44 | * Returns the dependency of the stream.
45 | */
46 | int getDependency();
47 |
48 | /**
49 | * Sets the dependency of the stream. The dependency cannot be negative.
50 | */
51 | HttpPriorityFrame setDependency(int dependency);
52 |
53 | /**
54 | * Returns the weight of the dependency of the stream.
55 | */
56 | int getWeight();
57 |
58 | /**
59 | * Sets the weight of the dependency of the stream.
60 | * The weight must be positive and cannot exceed 256 bytes.
61 | */
62 | HttpPriorityFrame setWeight(int weight);
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpPingFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpPingFrame} implementation.
22 | */
23 | public class DefaultHttpPingFrame implements HttpPingFrame {
24 |
25 | private long data;
26 | private boolean pong;
27 |
28 | /**
29 | * Creates a new instance.
30 | *
31 | * @param data the data payload of this frame
32 | */
33 | public DefaultHttpPingFrame(long data) {
34 | setData(data);
35 | }
36 |
37 | @Override
38 | public long getData() {
39 | return data;
40 | }
41 |
42 | @Override
43 | public HttpPingFrame setData(long data) {
44 | this.data = data;
45 | return this;
46 | }
47 |
48 | @Override
49 | public boolean isPong() {
50 | return pong;
51 | }
52 |
53 | @Override
54 | public HttpPingFrame setPong(boolean pong) {
55 | this.pong = pong;
56 | return this;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | StringBuilder buf = new StringBuilder();
62 | buf.append(StringUtil.simpleClassName(this));
63 | buf.append("(pong: ");
64 | buf.append(isPong());
65 | buf.append(')');
66 | buf.append(StringUtil.NEWLINE);
67 | buf.append("--> Data = ");
68 | buf.append(getData());
69 | return buf.toString();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpMessageProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.DecoderResult;
19 | import io.netty.handler.codec.http.HttpHeaders;
20 | import io.netty.handler.codec.http.HttpMessage;
21 | import io.netty.handler.codec.http.HttpVersion;
22 |
23 | /**
24 | * An {@link HttpMessage} decorator.
25 | */
26 | class HttpMessageProxy implements HttpMessage {
27 |
28 | private final HttpMessage message;
29 |
30 | protected HttpMessageProxy(HttpMessage message) {
31 | this.message = message;
32 | }
33 |
34 | @Override
35 | public HttpVersion getProtocolVersion() {
36 | return message.getProtocolVersion();
37 | }
38 |
39 | @Override
40 | public HttpMessage setProtocolVersion(HttpVersion version) {
41 | message.setProtocolVersion(version);
42 | return this;
43 | }
44 |
45 | @Override
46 | public HttpHeaders headers() {
47 | return message.headers();
48 | }
49 |
50 | @Override
51 | @Deprecated
52 | public DecoderResult getDecoderResult() {
53 | return message.getDecoderResult();
54 | }
55 |
56 | @Override
57 | public void setDecoderResult(DecoderResult result) {
58 | message.setDecoderResult(result);
59 | }
60 |
61 | @Override
62 | public HttpVersion protocolVersion() {
63 | return message.protocolVersion();
64 | }
65 |
66 | @Override
67 | public DecoderResult decoderResult() {
68 | return message.decoderResult();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpSettingsFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.util.Set;
19 |
20 | /**
21 | * An HTTP/2 SETTINGS Frame
22 | */
23 | public interface HttpSettingsFrame extends HttpFrame {
24 |
25 | int SETTINGS_HEADER_TABLE_SIZE = 1;
26 | int SETTINGS_ENABLE_PUSH = 2;
27 | int SETTINGS_MAX_CONCURRENT_STREAMS = 3;
28 | int SETTINGS_INITIAL_WINDOW_SIZE = 4;
29 | int SETTINGS_MAX_FRAME_SIZE = 5;
30 | int SETTINGS_MAX_HEADER_LIST_SIZE = 6;
31 |
32 | /**
33 | * Returns a {@code Set} of the setting IDs.
34 | * The set's iterator will return the IDs in ascending order.
35 | */
36 | Set getIds();
37 |
38 | /**
39 | * Returns {@code true} if the setting ID has a value.
40 | */
41 | boolean isSet(int id);
42 |
43 | /**
44 | * Returns the value of the setting ID.
45 | * Returns -1 if the setting ID is not set.
46 | */
47 | int getValue(int id);
48 |
49 | /**
50 | * Sets the value of the setting ID.
51 | * The ID cannot be negative and cannot exceed 65535.
52 | */
53 | HttpSettingsFrame setValue(int id, int value);
54 |
55 | /**
56 | * Removes the value of the setting ID.
57 | */
58 | HttpSettingsFrame removeValue(int id);
59 |
60 | /**
61 | * Returns {@code true} if this frame is an acknowledgement frame.
62 | */
63 | boolean isAck();
64 |
65 | /**
66 | * Sets if this frame is acknowledgement frame.
67 | */
68 | HttpSettingsFrame setAck(boolean ack);
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpHeadersFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | /**
19 | * An HTTP/2 HEADERS Frame
20 | */
21 | public interface HttpHeadersFrame extends HttpHeaderBlockFrame {
22 |
23 | /**
24 | * Returns {@code true} if this frame is the last frame to be transmitted on the stream.
25 | */
26 | boolean isLast();
27 |
28 | /**
29 | * Sets if this frame is the last frame to be transmitted on the stream.
30 | */
31 | HttpHeadersFrame setLast(boolean last);
32 |
33 | /**
34 | * Returns {@code true} if the dependency of the stream is exclusive.
35 | */
36 | boolean isExclusive();
37 |
38 | /**
39 | * Sets if the dependency of the stream is exclusive.
40 | */
41 | HttpHeadersFrame setExclusive(boolean exclusive);
42 |
43 | /**
44 | * Returns the dependency of the stream.
45 | */
46 | int getDependency();
47 |
48 | /**
49 | * Sets the dependency of the stream. The dependency cannot be negative.
50 | */
51 | HttpHeadersFrame setDependency(int dependency);
52 |
53 | /**
54 | * Returns the weight of the dependency of the stream.
55 | */
56 | int getWeight();
57 |
58 | /**
59 | * Sets the weight of the dependency of the stream.
60 | * The weight must be positive and cannot exceed 256 bytes.
61 | */
62 | HttpHeadersFrame setWeight(int weight);
63 |
64 | @Override
65 | HttpHeadersFrame setStreamId(int streamId);
66 |
67 | @Override
68 | HttpHeadersFrame setInvalid();
69 |
70 | @Override
71 | HttpHeadersFrame setTruncated();
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttp2Headers.java:
--------------------------------------------------------------------------------
1 | package com.twitter.http2;
2 |
3 | import io.netty.handler.codec.DefaultHeaders.NameValidator;
4 | import io.netty.handler.codec.http.DefaultHttpHeaders;
5 | import io.netty.util.AsciiString;
6 | import io.netty.util.ByteProcessor;
7 | import io.netty.util.internal.PlatformDependent;
8 |
9 | import static io.netty.util.internal.ObjectUtil.checkNotNull;
10 |
11 | public class DefaultHttp2Headers extends DefaultHttpHeaders {
12 | private static final ByteProcessor HEADER_NAME_VALIDATOR = new ByteProcessor() {
13 | @Override
14 | public boolean process(byte value) throws Exception {
15 | validateChar((char) (value & 0xFF));
16 | return true;
17 | }
18 | };
19 |
20 | private static void validateChar(char character) {
21 | switch (character) {
22 | case '\t':
23 | case '\n':
24 | case 0x0b:
25 | case '\f':
26 | case '\r':
27 | case ' ':
28 | case ',':
29 | case ';':
30 | case '=':
31 | throw new IllegalArgumentException(
32 | "a header name cannot contain the following prohibited characters: =,; \\t\\r\\n\\v\\f: " +
33 | character);
34 | default:
35 | // Check to see if the character is not an ASCII character, or invalid
36 | if (character > 127) {
37 | throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " +
38 | character);
39 | }
40 | }
41 | }
42 |
43 | static final NameValidator Http2NameValidator = new NameValidator() {
44 | @Override
45 | public void validateName(CharSequence name) {
46 | if (name instanceof AsciiString) {
47 | try {
48 | ((AsciiString) name).forEachByte(HEADER_NAME_VALIDATOR);
49 | } catch (Exception e) {
50 | PlatformDependent.throwException(e);
51 | }
52 | } else {
53 | checkNotNull(name, "name");
54 | // Go through each character in the name
55 | for (int index = 0; index < name.length(); ++index) {
56 | validateChar(name.charAt(index));
57 | }
58 | }
59 | }
60 | };
61 |
62 | public DefaultHttp2Headers() {
63 | super(true, Http2NameValidator);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/StreamedHttpMessage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.HttpContent;
19 | import io.netty.handler.codec.http.HttpMessage;
20 | import io.netty.handler.codec.http.LastHttpContent;
21 | import io.netty.util.concurrent.Future;
22 |
23 | /**
24 | * An {@link HttpMessage} that adds support for streaming content using {@linkplain Pipe pipes}.
25 | * @see Pipe
26 | */
27 | public interface StreamedHttpMessage extends HttpMessage {
28 |
29 | /**
30 | * Gets the pipe for sending the message body as {@linkplain HttpContent} objects.
31 | * A {@linkplain LastHttpContent last http content} will indicate the end of the body.
32 | *
33 | * @return the message body pipe.
34 | * @see RFC 2616, 3.6.1 Chunked Transfer Coding
35 | * @see RFC 2616, 4.3 Message Body
36 | */
37 | Pipe getContent();
38 |
39 | /**
40 | * Adds content to the pipe. This returns a future for determining whether the message was received.
41 | * If the chunk is the last chunk then the pipe will be closed.
42 | *
43 | * An {@link LastHttpContent} can be used for the trailer part of a chunked-encoded request.
44 | *
45 | * This method is preferable to {@code getContent().send()} because it does additional checks.
46 | *
47 | * @param content the content to send
48 | * @return a future indicating when the message was received.
49 | */
50 | Future addContent(HttpContent content);
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpRequestProxy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.HttpMethod;
19 | import io.netty.handler.codec.http.HttpRequest;
20 | import io.netty.handler.codec.http.HttpVersion;
21 |
22 | /**
23 | * An {@link HttpRequest} decorator.
24 | */
25 | class HttpRequestProxy extends HttpMessageProxy implements HttpRequest {
26 |
27 | private final HttpRequest request;
28 |
29 | public HttpRequestProxy(HttpRequest request) {
30 | super(request);
31 | this.request = request;
32 | }
33 |
34 | public HttpRequest httpRequest() {
35 | return request;
36 | }
37 |
38 | @Override
39 | @Deprecated
40 | public HttpMethod getMethod() {
41 | return request.getMethod();
42 | }
43 |
44 | @Override
45 | public HttpRequest setMethod(HttpMethod method) {
46 | request.setMethod(method);
47 | return this;
48 | }
49 |
50 | @Override
51 | @Deprecated
52 | public String getUri() {
53 | return request.getUri();
54 | }
55 |
56 | @Override
57 | public HttpRequest setUri(String uri) {
58 | request.setUri(uri);
59 | return this;
60 | }
61 |
62 | @Override
63 | public HttpRequest setProtocolVersion(HttpVersion version) {
64 | request.setProtocolVersion(version);
65 | return this;
66 | }
67 |
68 | @Override
69 | public String toString() {
70 | return request.toString();
71 | }
72 |
73 | @Override
74 | public HttpMethod method() {
75 | return request.method();
76 | }
77 |
78 | @Override
79 | public String uri() {
80 | return request.uri();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpHeaderBlockFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.HttpHeaders;
19 | import io.netty.util.internal.StringUtil;
20 |
21 | import java.util.Map;
22 |
23 | /**
24 | * The default {@link HttpHeaderBlockFrame} implementation.
25 | */
26 | public abstract class DefaultHttpHeaderBlockFrame extends DefaultHttpStreamFrame
27 | implements HttpHeaderBlockFrame {
28 |
29 | private boolean invalid;
30 | private boolean truncated;
31 | private final HttpHeaders headers = new DefaultHttp2Headers();
32 |
33 | /**
34 | * Creates a new instance.
35 | *
36 | * @param streamId the stream identifier of this frame
37 | */
38 | protected DefaultHttpHeaderBlockFrame(int streamId) {
39 | super(streamId);
40 | }
41 |
42 | @Override
43 | public boolean isInvalid() {
44 | return invalid;
45 | }
46 |
47 | @Override
48 | public HttpHeaderBlockFrame setInvalid() {
49 | invalid = true;
50 | return this;
51 | }
52 |
53 | @Override
54 | public boolean isTruncated() {
55 | return truncated;
56 | }
57 |
58 | @Override
59 | public HttpHeaderBlockFrame setTruncated() {
60 | truncated = true;
61 | return this;
62 | }
63 |
64 | @Override
65 | public HttpHeaders headers() {
66 | return headers;
67 | }
68 |
69 | protected void appendHeaders(StringBuilder buf) {
70 | for (Map.Entry e : headers()) {
71 | buf.append(" ");
72 | buf.append(e.getKey());
73 | buf.append(": ");
74 | buf.append(e.getValue());
75 | buf.append(StringUtil.NEWLINE);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/com/twitter/http2/HttpResponseProxyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.DefaultHttpResponse;
19 | import io.netty.handler.codec.http.HttpResponse;
20 | import io.netty.handler.codec.http.HttpResponseStatus;
21 | import io.netty.handler.codec.http.HttpVersion;
22 | import org.junit.Test;
23 |
24 | import static org.junit.Assert.assertEquals;
25 | import static org.junit.Assert.assertSame;
26 |
27 | public class HttpResponseProxyTest {
28 |
29 | private HttpResponse httpResponse = new DefaultHttpResponse(
30 | HttpVersion.HTTP_1_0, HttpResponseStatus.OK);
31 | private HttpResponseProxy httpResponseProxy = new HttpResponseProxy(httpResponse);
32 |
33 | @Test
34 | public void testSetStatus() {
35 | assertSame(httpResponseProxy, httpResponseProxy.setStatus(HttpResponseStatus.NOT_FOUND));
36 | assertEquals(httpResponse.getStatus(), httpResponseProxy.getStatus());
37 | }
38 |
39 | @Test
40 | public void testGetStatus() {
41 | assertEquals(httpResponse.getStatus(), httpResponseProxy.getStatus());
42 | }
43 |
44 | @Test
45 | public void testToString() {
46 | assertEquals(httpResponse.toString(), httpResponseProxy.toString());
47 | }
48 |
49 | @Test
50 | public void testGetProtocolVersion() {
51 | assertEquals(httpResponse.getProtocolVersion(), httpResponseProxy.getProtocolVersion());
52 | }
53 |
54 | @Test
55 | public void testSetProtocolVersion() {
56 | assertSame(httpResponseProxy, httpResponseProxy.setProtocolVersion(HttpVersion.HTTP_1_1));
57 | assertEquals(httpResponse.getProtocolVersion(), httpResponseProxy.getProtocolVersion());
58 | }
59 |
60 | @Test
61 | public void testHeaders() {
62 | assertSame(httpResponse.headers(), httpResponseProxy.headers());
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpRstStreamFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpRstStreamFrame} implementation.
22 | */
23 | public class DefaultHttpRstStreamFrame implements HttpRstStreamFrame {
24 |
25 | private int streamId;
26 | private HttpErrorCode errorCode;
27 |
28 | /**
29 | * Creates a new instance.
30 | *
31 | * @param streamId the stream identifier of this frame
32 | * @param code the error code of this frame
33 | */
34 | public DefaultHttpRstStreamFrame(int streamId, int code) {
35 | this(streamId, HttpErrorCode.valueOf(code));
36 | }
37 |
38 | /**
39 | * Creates a new instance.
40 | *
41 | * @param streamId the stream identifier of this frame
42 | * @param errorCode the error code of this frame
43 | */
44 | public DefaultHttpRstStreamFrame(int streamId, HttpErrorCode errorCode) {
45 | setStreamId(streamId);
46 | setErrorCode(errorCode);
47 | }
48 |
49 | @Override
50 | public int getStreamId() {
51 | return streamId;
52 | }
53 |
54 | @Override
55 | public HttpRstStreamFrame setStreamId(int streamId) {
56 | if (streamId <= 0) {
57 | throw new IllegalArgumentException(
58 | "Stream identifier must be positive: " + streamId);
59 | }
60 | this.streamId = streamId;
61 | return this;
62 | }
63 |
64 | @Override
65 | public HttpErrorCode getErrorCode() {
66 | return errorCode;
67 | }
68 |
69 | @Override
70 | public HttpRstStreamFrame setErrorCode(HttpErrorCode errorCode) {
71 | this.errorCode = errorCode;
72 | return this;
73 | }
74 |
75 | @Override
76 | public String toString() {
77 | StringBuilder buf = new StringBuilder();
78 | buf.append(StringUtil.simpleClassName(this));
79 | buf.append(StringUtil.NEWLINE);
80 | buf.append("--> Stream-ID = ");
81 | buf.append(getStreamId());
82 | buf.append(StringUtil.NEWLINE);
83 | buf.append("--> Error Code: ");
84 | buf.append(getErrorCode().toString());
85 | return buf.toString();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpWindowUpdateFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpWindowUpdateFrame} implementation.
22 | */
23 | public class DefaultHttpWindowUpdateFrame implements HttpWindowUpdateFrame {
24 |
25 | private int streamId;
26 | private int windowSizeIncrement;
27 |
28 | /**
29 | * Creates a new instance.
30 | *
31 | * @param streamId the stream identifier of this frame
32 | * @param windowSizeIncrement the Window-Size-Increment of this frame
33 | */
34 | public DefaultHttpWindowUpdateFrame(int streamId, int windowSizeIncrement) {
35 | setStreamId(streamId);
36 | setWindowSizeIncrement(windowSizeIncrement);
37 | }
38 |
39 | @Override
40 | public int getStreamId() {
41 | return streamId;
42 | }
43 |
44 | @Override
45 | public DefaultHttpWindowUpdateFrame setStreamId(int streamId) {
46 | if (streamId < 0) {
47 | throw new IllegalArgumentException(
48 | "Stream identifier cannot be negative: " + streamId);
49 | }
50 | this.streamId = streamId;
51 | return this;
52 | }
53 |
54 | @Override
55 | public int getWindowSizeIncrement() {
56 | return windowSizeIncrement;
57 | }
58 |
59 | @Override
60 | public DefaultHttpWindowUpdateFrame setWindowSizeIncrement(int windowSizeIncrement) {
61 | if (windowSizeIncrement <= 0) {
62 | throw new IllegalArgumentException(
63 | "Window-Size-Increment must be positive: " + windowSizeIncrement);
64 | }
65 | this.windowSizeIncrement = windowSizeIncrement;
66 | return this;
67 | }
68 |
69 | @Override
70 | public String toString() {
71 | StringBuilder buf = new StringBuilder();
72 | buf.append(StringUtil.simpleClassName(this));
73 | buf.append(StringUtil.NEWLINE);
74 | buf.append("--> Stream-ID = ");
75 | buf.append(getStreamId());
76 | buf.append(StringUtil.NEWLINE);
77 | buf.append("--> Window-Size-Increment = ");
78 | buf.append(getWindowSizeIncrement());
79 | return buf.toString();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/test/java/com/twitter/http2/HttpRequestProxyTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.handler.codec.http.DefaultHttpRequest;
19 | import io.netty.handler.codec.http.HttpMethod;
20 | import io.netty.handler.codec.http.HttpRequest;
21 | import io.netty.handler.codec.http.HttpVersion;
22 | import org.junit.Test;
23 |
24 | import static org.junit.Assert.assertEquals;
25 | import static org.junit.Assert.assertSame;
26 |
27 | public class HttpRequestProxyTest {
28 |
29 | private HttpRequest httpRequest = new DefaultHttpRequest(
30 | HttpVersion.HTTP_1_0, HttpMethod.GET, "/");
31 | private HttpRequestProxy httpRequestProxy = new HttpRequestProxy(httpRequest);
32 |
33 | @Test
34 | public void testHttpRequest() {
35 | assertSame(httpRequest, httpRequestProxy.httpRequest());
36 | }
37 |
38 | @Test
39 | public void testGetMethod() {
40 | assertEquals(httpRequest.getMethod(), httpRequestProxy.getMethod());
41 | }
42 |
43 | @Test
44 | public void testSetMethod() {
45 | assertSame(httpRequestProxy, httpRequestProxy.setMethod(HttpMethod.POST));
46 | assertEquals(httpRequest.getMethod(), httpRequestProxy.getMethod());
47 | }
48 |
49 | @Test
50 | public void testGetUri() {
51 | assertEquals(httpRequest.getUri(), httpRequestProxy.getUri());
52 | }
53 |
54 | @Test
55 | public void testSetUri() {
56 | assertSame(httpRequestProxy, httpRequestProxy.setUri("/path"));
57 | assertEquals(httpRequest.getUri(), httpRequestProxy.getUri());
58 | }
59 |
60 | @Test
61 | public void testToString() {
62 | assertEquals(httpRequest.toString(), httpRequestProxy.toString());
63 | }
64 |
65 | @Test
66 | public void testGetProtocolVersion() {
67 | assertEquals(httpRequest.getProtocolVersion(), httpRequestProxy.getProtocolVersion());
68 | }
69 |
70 | @Test
71 | public void testSetProtocolVersion() {
72 | assertSame(httpRequestProxy, httpRequestProxy.setProtocolVersion(HttpVersion.HTTP_1_1));
73 | assertEquals(httpRequest.getProtocolVersion(), httpRequestProxy.getProtocolVersion());
74 | }
75 |
76 | @Test
77 | public void testHeaders() {
78 | assertSame(httpRequest.headers(), httpRequestProxy.headers());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpGoAwayFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpGoAwayFrame} implementation.
22 | */
23 | public class DefaultHttpGoAwayFrame implements HttpGoAwayFrame {
24 |
25 | private int lastStreamId;
26 | private HttpErrorCode errorCode;
27 |
28 | /**
29 | * Creates a new instance.
30 | *
31 | * @param lastStreamId the Last-Stream-ID of this frame
32 | * @param code the error code of this frame
33 | */
34 | public DefaultHttpGoAwayFrame(int lastStreamId, int code) {
35 | this(lastStreamId, HttpErrorCode.valueOf(code));
36 | }
37 |
38 | /**
39 | * Creates a new instance.
40 | *
41 | * @param lastStreamId the Last-Stream-ID of this frame
42 | * @param errorCode the error code of this frame
43 | */
44 | public DefaultHttpGoAwayFrame(int lastStreamId, HttpErrorCode errorCode) {
45 | setLastStreamId(lastStreamId);
46 | setErrorCode(errorCode);
47 | }
48 |
49 | @Override
50 | public int getLastStreamId() {
51 | return lastStreamId;
52 | }
53 |
54 | @Override
55 | public HttpGoAwayFrame setLastStreamId(int lastStreamId) {
56 | if (lastStreamId < 0) {
57 | throw new IllegalArgumentException(
58 | "Last-Stream-ID cannot be negative: " + lastStreamId);
59 | }
60 | this.lastStreamId = lastStreamId;
61 | return this;
62 | }
63 |
64 | @Override
65 | public HttpErrorCode getErrorCode() {
66 | return errorCode;
67 | }
68 |
69 | @Override
70 | public HttpGoAwayFrame setErrorCode(HttpErrorCode errorCode) {
71 | this.errorCode = errorCode;
72 | return this;
73 | }
74 |
75 | @Override
76 | public String toString() {
77 | StringBuilder buf = new StringBuilder();
78 | buf.append(StringUtil.simpleClassName(this));
79 | buf.append(StringUtil.NEWLINE);
80 | buf.append("--> Last-Stream-ID = ");
81 | buf.append(getLastStreamId());
82 | buf.append(StringUtil.NEWLINE);
83 | buf.append("--> Error Code: ");
84 | buf.append(getErrorCode().toString());
85 | return buf.toString();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpPushPromiseFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpPushPromiseFrame} implementation.
22 | */
23 | public class DefaultHttpPushPromiseFrame extends DefaultHttpHeaderBlockFrame
24 | implements HttpPushPromiseFrame {
25 |
26 | private int promisedStreamId;
27 |
28 | /**
29 | * Creates a new instance.
30 | *
31 | * @param streamId the stream identifier of this frame
32 | */
33 | public DefaultHttpPushPromiseFrame(int streamId, int promisedStreamId) {
34 | super(streamId);
35 | setPromisedStreamId(promisedStreamId);
36 | }
37 |
38 | @Override
39 | public int getPromisedStreamId() {
40 | return promisedStreamId;
41 | }
42 |
43 | @Override
44 | public HttpPushPromiseFrame setPromisedStreamId(int promisedStreamId) {
45 | if (promisedStreamId <= 0) {
46 | throw new IllegalArgumentException(
47 | "Promised-Stream-ID must be positive: " + promisedStreamId);
48 | }
49 | this.promisedStreamId = promisedStreamId;
50 | return this;
51 | }
52 |
53 | @Override
54 | public HttpPushPromiseFrame setStreamId(int streamId) {
55 | super.setStreamId(streamId);
56 | return this;
57 | }
58 |
59 | @Override
60 | public HttpPushPromiseFrame setInvalid() {
61 | super.setInvalid();
62 | return this;
63 | }
64 |
65 | @Override
66 | public HttpPushPromiseFrame setTruncated() {
67 | super.setTruncated();
68 | return this;
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | StringBuilder buf = new StringBuilder();
74 | buf.append(StringUtil.simpleClassName(this));
75 | buf.append(StringUtil.NEWLINE);
76 | buf.append("--> Stream-ID = ");
77 | buf.append(getStreamId());
78 | buf.append(StringUtil.NEWLINE);
79 | buf.append("--> Promised-Stream-ID = ");
80 | buf.append(getPromisedStreamId());
81 | buf.append(StringUtil.NEWLINE);
82 | buf.append("--> Headers:");
83 | buf.append(StringUtil.NEWLINE);
84 | appendHeaders(buf);
85 |
86 | // Remove the last newline.
87 | buf.setLength(buf.length() - StringUtil.NEWLINE.length());
88 | return buf.toString();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpSettingsFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.util.Map;
19 | import java.util.Set;
20 | import java.util.TreeMap;
21 |
22 | import io.netty.util.internal.StringUtil;
23 |
24 | /**
25 | * The default {@link HttpSettingsFrame} implementation.
26 | */
27 | public class DefaultHttpSettingsFrame implements HttpSettingsFrame {
28 |
29 | private final Map settingsMap = new TreeMap();
30 | private boolean ack;
31 |
32 | @Override
33 | public Set getIds() {
34 | return settingsMap.keySet();
35 | }
36 |
37 | @Override
38 | public boolean isSet(int id) {
39 | return settingsMap.containsKey(id);
40 | }
41 |
42 | @Override
43 | public int getValue(int id) {
44 | if (settingsMap.containsKey(id)) {
45 | return settingsMap.get(id);
46 | } else {
47 | return -1;
48 | }
49 | }
50 |
51 | @Override
52 | public HttpSettingsFrame setValue(int id, int value) {
53 | if (id < 0 || id > HttpCodecUtil.HTTP_SETTINGS_MAX_ID) {
54 | throw new IllegalArgumentException("Setting ID is not valid: " + id);
55 | }
56 | settingsMap.put(id, value);
57 | return this;
58 | }
59 |
60 | @Override
61 | public HttpSettingsFrame removeValue(int id) {
62 | settingsMap.remove(id);
63 | return this;
64 | }
65 |
66 | @Override
67 | public boolean isAck() {
68 | return ack;
69 | }
70 |
71 | @Override
72 | public HttpSettingsFrame setAck(boolean ack) {
73 | this.ack = ack;
74 | return this;
75 | }
76 |
77 | private Set> getSettings() {
78 | return settingsMap.entrySet();
79 | }
80 |
81 | private void appendSettings(StringBuilder buf) {
82 | for (Map.Entry e : getSettings()) {
83 | buf.append("--> ");
84 | buf.append(e.getKey().toString());
85 | buf.append(':');
86 | buf.append(e.getValue().toString());
87 | buf.append(StringUtil.NEWLINE);
88 | }
89 | }
90 |
91 | @Override
92 | public String toString() {
93 | StringBuilder buf = new StringBuilder();
94 | buf.append(StringUtil.simpleClassName(this));
95 | buf.append("(ack: ");
96 | buf.append(isAck());
97 | buf.append(')');
98 | buf.append(StringUtil.NEWLINE);
99 | appendSettings(buf);
100 | buf.setLength(buf.length() - StringUtil.NEWLINE.length());
101 | return buf.toString();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpFrameDecoderDelegate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.buffer.ByteBuf;
19 |
20 | /**
21 | * Callback interface for {@link HttpFrameDecoder}.
22 | */
23 | public interface HttpFrameDecoderDelegate {
24 |
25 | /**
26 | * Called when a DATA frame is received.
27 | */
28 | void readDataFramePadding(int streamId, boolean endStream, int padding);
29 |
30 | /**
31 | * Called when a DATA frame is received.
32 | */
33 | void readDataFrame(int streamId, boolean endStream, boolean endSegment, ByteBuf data);
34 |
35 | /**
36 | * Called when a HEADERS frame is received.
37 | * The Header Block Fragment is not included. See readHeaderBlock().
38 | */
39 | void readHeadersFrame(
40 | int streamId,
41 | boolean endStream,
42 | boolean endSegment,
43 | boolean exclusive,
44 | int dependency,
45 | int weight
46 | );
47 |
48 | /**
49 | * Called when a PRIORITY frame is received.
50 | */
51 | void readPriorityFrame(int streamId, boolean exclusive, int dependency, int weight);
52 |
53 | /**
54 | * Called when a RST_STREAM frame is received.
55 | */
56 | void readRstStreamFrame(int streamId, int errorCode);
57 |
58 | /**
59 | * Called when a SETTINGS frame is received.
60 | * Settings are not included. See readSetting().
61 | */
62 | void readSettingsFrame(boolean ack);
63 |
64 | /**
65 | * Called when an individual setting within a SETTINGS frame is received.
66 | */
67 | void readSetting(int id, int value);
68 |
69 | /**
70 | * Called when the entire SETTINGS frame has been received.
71 | */
72 | void readSettingsEnd();
73 |
74 | /**
75 | * Called when a PUSH_PROMISE frame is received.
76 | * The Header Block Fragment is not included. See readHeaderBlock().
77 | */
78 | void readPushPromiseFrame(int streamId, int promisedStreamId);
79 |
80 | /**
81 | * Called when a PING frame is received.
82 | */
83 | void readPingFrame(long data, boolean ack);
84 |
85 | /**
86 | * Called when a GOAWAY frame is received.
87 | */
88 | void readGoAwayFrame(int lastStreamId, int errorCode);
89 |
90 | /**
91 | * Called when a WINDOW_UPDATE frame is received.
92 | */
93 | void readWindowUpdateFrame(int streamId, int windowSizeIncrement);
94 |
95 | /**
96 | * Called when the header block fragment within a HEADERS,
97 | * PUSH_PROMISE, or CONTINUATION frame is received.
98 | */
99 | void readHeaderBlock(ByteBuf headerBlockFragment);
100 |
101 | /**
102 | * Called when an entire header block has been received.
103 | */
104 | void readHeaderBlockEnd();
105 |
106 | /**
107 | * Called when an unrecoverable connection error has occurred.
108 | */
109 | void readFrameError(String message);
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpPriorityFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | /**
21 | * The default {@link HttpPriorityFrame} implementation.
22 | */
23 | public class DefaultHttpPriorityFrame implements HttpPriorityFrame {
24 |
25 | private int streamId;
26 | private boolean exclusive;
27 | private int dependency;
28 | private int weight;
29 |
30 | /**
31 | * Creates a new instance.
32 | *
33 | * @param streamId the stream identifier of this frame
34 | * @param exclusive if the dependency of the stream is exclusive
35 | * @param dependency the dependency of the stream
36 | * @param weight the weight of the dependency of the stream
37 | */
38 | public DefaultHttpPriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
39 | setStreamId(streamId);
40 | setExclusive(exclusive);
41 | setDependency(dependency);
42 | setWeight(weight);
43 | }
44 |
45 | @Override
46 | public int getStreamId() {
47 | return streamId;
48 | }
49 |
50 | @Override
51 | public HttpPriorityFrame setStreamId(int streamId) {
52 | if (streamId <= 0) {
53 | throw new IllegalArgumentException(
54 | "Stream identifier must be positive: " + streamId);
55 | }
56 | this.streamId = streamId;
57 | return this;
58 | }
59 |
60 | @Override
61 | public boolean isExclusive() {
62 | return exclusive;
63 | }
64 |
65 | @Override
66 | public HttpPriorityFrame setExclusive(boolean exclusive) {
67 | this.exclusive = exclusive;
68 | return this;
69 | }
70 |
71 | @Override
72 | public int getDependency() {
73 | return dependency;
74 | }
75 |
76 | @Override
77 | public HttpPriorityFrame setDependency(int dependency) {
78 | if (dependency < 0) {
79 | throw new IllegalArgumentException(
80 | "Dependency cannot be negative: " + dependency);
81 | }
82 | this.dependency = dependency;
83 | return this;
84 | }
85 |
86 | @Override
87 | public int getWeight() {
88 | return weight;
89 | }
90 |
91 | @Override
92 | public HttpPriorityFrame setWeight(int weight) {
93 | if (weight <= 0 || weight > 256) {
94 | throw new IllegalArgumentException(
95 | "Illegal weight: " + weight);
96 | }
97 | this.weight = weight;
98 | return this;
99 | }
100 |
101 | @Override
102 | public String toString() {
103 | StringBuilder buf = new StringBuilder();
104 | buf.append(StringUtil.simpleClassName(this));
105 | buf.append(StringUtil.NEWLINE);
106 | buf.append("--> Stream-ID = ");
107 | buf.append(getStreamId());
108 | buf.append(StringUtil.NEWLINE);
109 | buf.append("--> Dependency = ");
110 | buf.append(getDependency());
111 | buf.append(" (exclusive: ");
112 | buf.append(isExclusive());
113 | buf.append(", weight: ");
114 | buf.append(getWeight());
115 | buf.append(')');
116 | return buf.toString();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpHeadersFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.util.internal.StringUtil;
19 |
20 | import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_DEPENDENCY;
21 | import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;
22 |
23 | /**
24 | * The default {@link HttpHeadersFrame} implementation.
25 | */
26 | public class DefaultHttpHeadersFrame extends DefaultHttpHeaderBlockFrame
27 | implements HttpHeadersFrame {
28 |
29 | private boolean last;
30 | private boolean exclusive = false;
31 | private int dependency = HTTP_DEFAULT_DEPENDENCY;
32 | private int weight = HTTP_DEFAULT_WEIGHT;
33 |
34 | /**
35 | * Creates a new instance.
36 | *
37 | * @param streamId the stream identifier of this frame
38 | */
39 | public DefaultHttpHeadersFrame(int streamId) {
40 | super(streamId);
41 | }
42 |
43 | @Override
44 | public boolean isLast() {
45 | return last;
46 | }
47 |
48 | @Override
49 | public HttpHeadersFrame setLast(boolean last) {
50 | this.last = last;
51 | return this;
52 | }
53 |
54 | @Override
55 | public boolean isExclusive() {
56 | return exclusive;
57 | }
58 |
59 | @Override
60 | public HttpHeadersFrame setExclusive(boolean exclusive) {
61 | this.exclusive = exclusive;
62 | return this;
63 | }
64 |
65 | @Override
66 | public int getDependency() {
67 | return dependency;
68 | }
69 |
70 | @Override
71 | public HttpHeadersFrame setDependency(int dependency) {
72 | if (dependency < 0) {
73 | throw new IllegalArgumentException(
74 | "Dependency cannot be negative: " + dependency);
75 | }
76 | this.dependency = dependency;
77 | return this;
78 | }
79 |
80 | @Override
81 | public int getWeight() {
82 | return weight;
83 | }
84 |
85 | @Override
86 | public HttpHeadersFrame setWeight(int weight) {
87 | if (weight <= 0 || weight > 256) {
88 | throw new IllegalArgumentException(
89 | "Illegal weight: " + weight);
90 | }
91 | this.weight = weight;
92 | return this;
93 | }
94 |
95 | @Override
96 | public HttpHeadersFrame setStreamId(int streamId) {
97 | super.setStreamId(streamId);
98 | return this;
99 | }
100 |
101 | @Override
102 | public HttpHeadersFrame setInvalid() {
103 | super.setInvalid();
104 | return this;
105 | }
106 |
107 | @Override
108 | public HttpHeadersFrame setTruncated() {
109 | super.setTruncated();
110 | return this;
111 | }
112 |
113 | @Override
114 | public String toString() {
115 | StringBuilder buf = new StringBuilder();
116 | buf.append(StringUtil.simpleClassName(this));
117 | buf.append("(last: ");
118 | buf.append(isLast());
119 | buf.append(')');
120 | buf.append(StringUtil.NEWLINE);
121 | buf.append("--> Stream-ID = ");
122 | buf.append(getStreamId());
123 | buf.append(StringUtil.NEWLINE);
124 | buf.append("--> Dependency = ");
125 | buf.append(getDependency());
126 | buf.append(" (exclusive: ");
127 | buf.append(isExclusive());
128 | buf.append(", weight: ");
129 | buf.append(getWeight());
130 | buf.append(')');
131 | buf.append(StringUtil.NEWLINE);
132 | buf.append("--> Headers:");
133 | buf.append(StringUtil.NEWLINE);
134 | appendHeaders(buf);
135 |
136 | // Remove the last newline.
137 | buf.setLength(buf.length() - StringUtil.NEWLINE.length());
138 | return buf.toString();
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpCodecUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.buffer.ByteBuf;
19 |
20 | final class HttpCodecUtil {
21 |
22 | static final int HTTP_FRAME_HEADER_SIZE = 9;
23 | static final int HTTP_MAX_LENGTH = 0x4000; // Initial MAX_FRAME_SIZE value is 2^14
24 |
25 | static final int HTTP_DATA_FRAME = 0x00;
26 | static final int HTTP_HEADERS_FRAME = 0x01;
27 | static final int HTTP_PRIORITY_FRAME = 0x02;
28 | static final int HTTP_RST_STREAM_FRAME = 0x03;
29 | static final int HTTP_SETTINGS_FRAME = 0x04;
30 | static final int HTTP_PUSH_PROMISE_FRAME = 0x05;
31 | static final int HTTP_PING_FRAME = 0x06;
32 | static final int HTTP_GOAWAY_FRAME = 0x07;
33 | static final int HTTP_WINDOW_UPDATE_FRAME = 0x08;
34 | static final int HTTP_CONTINUATION_FRAME = 0x09;
35 |
36 | static final byte HTTP_FLAG_ACK = 0x01;
37 | static final byte HTTP_FLAG_END_STREAM = 0x01;
38 | static final byte HTTP_FLAG_END_SEGMENT = 0x02;
39 | static final byte HTTP_FLAG_END_HEADERS = 0x04;
40 | static final byte HTTP_FLAG_PADDED = 0x08;
41 | static final byte HTTP_FLAG_PRIORITY = 0x20;
42 |
43 | static final int HTTP_DEFAULT_WEIGHT = 16;
44 | static final int HTTP_DEFAULT_DEPENDENCY = 0;
45 |
46 | static final int HTTP_SETTINGS_MAX_ID = 0xFFFF; // Identifier is a 16-bit field
47 |
48 | static final int HTTP_CONNECTION_STREAM_ID = 0;
49 |
50 | /**
51 | * Reads a big-endian unsigned short integer from the buffer.
52 | */
53 | static int getUnsignedShort(ByteBuf buf, int offset) {
54 | return (buf.getByte(offset) & 0xFF) << 8
55 | | buf.getByte(offset + 1) & 0xFF;
56 | }
57 |
58 | /**
59 | * Reads a big-endian unsigned medium integer from the buffer.
60 | */
61 | static int getUnsignedMedium(ByteBuf buf, int offset) {
62 | return (buf.getByte(offset) & 0xFF) << 16
63 | | (buf.getByte(offset + 1) & 0xFF) << 8
64 | | buf.getByte(offset + 2) & 0xFF;
65 | }
66 |
67 | /**
68 | * Reads a big-endian (31-bit) integer from the buffer.
69 | */
70 | static int getUnsignedInt(ByteBuf buf, int offset) {
71 | return (buf.getByte(offset) & 0x7F) << 24
72 | | (buf.getByte(offset + 1) & 0xFF) << 16
73 | | (buf.getByte(offset + 2) & 0xFF) << 8
74 | | buf.getByte(offset + 3) & 0xFF;
75 | }
76 |
77 | /**
78 | * Reads a big-endian signed integer from the buffer.
79 | */
80 | static int getSignedInt(ByteBuf buf, int offset) {
81 | return (buf.getByte(offset) & 0xFF) << 24
82 | | (buf.getByte(offset + 1) & 0xFF) << 16
83 | | (buf.getByte(offset + 2) & 0xFF) << 8
84 | | buf.getByte(offset + 3) & 0xFF;
85 | }
86 |
87 | /**
88 | * Reads a big-endian signed long from the buffer.
89 | */
90 | static long getSignedLong(ByteBuf buf, int offset) {
91 | return ((long) buf.getByte(offset) & 0xFF) << 56
92 | | ((long) buf.getByte(offset + 1) & 0xFF) << 48
93 | | ((long) buf.getByte(offset + 2) & 0xFF) << 40
94 | | ((long) buf.getByte(offset + 3) & 0xFF) << 32
95 | | ((long) buf.getByte(offset + 4) & 0xFF) << 24
96 | | ((long) buf.getByte(offset + 5) & 0xFF) << 16
97 | | ((long) buf.getByte(offset + 6) & 0xFF) << 8
98 | | (long) buf.getByte(offset + 7) & 0xFF;
99 | }
100 |
101 | /**
102 | * Returns {@code true} if the stream identifier is for a server initiated stream.
103 | */
104 | static boolean isServerId(int streamId) {
105 | // Server initiated streams have even stream identifiers
106 | return streamId % 2 == 0;
107 | }
108 |
109 | private HttpCodecUtil() {
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/DefaultHttpDataFrame.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import io.netty.buffer.ByteBuf;
19 | import io.netty.buffer.ByteBufHolder;
20 | import io.netty.buffer.Unpooled;
21 | import io.netty.util.IllegalReferenceCountException;
22 | import io.netty.util.internal.StringUtil;
23 |
24 | /**
25 | * The default {@link HttpDataFrame} implementation.
26 | */
27 | public class DefaultHttpDataFrame extends DefaultHttpStreamFrame implements HttpDataFrame {
28 |
29 | private final ByteBuf data;
30 | private boolean last;
31 |
32 | /**
33 | * Creates a new instance.
34 | *
35 | * @param streamId the stream identifier of this frame
36 | */
37 | public DefaultHttpDataFrame(int streamId) {
38 | this(streamId, Unpooled.buffer(0));
39 | }
40 |
41 | /**
42 | * Creates a new instance.
43 | *
44 | * @param streamId the stream identifier of this frame
45 | * @param data the payload of the frame. Can not exceed {@link HttpCodecUtil#HTTP_MAX_LENGTH}
46 | */
47 | public DefaultHttpDataFrame(int streamId, ByteBuf data) {
48 | super(streamId);
49 | if (data == null) {
50 | throw new NullPointerException("data");
51 | }
52 | this.data = validate(data);
53 | }
54 |
55 | private static ByteBuf validate(ByteBuf data) {
56 | if (data.readableBytes() > HttpCodecUtil.HTTP_MAX_LENGTH) {
57 | throw new IllegalArgumentException("data payload cannot exceed "
58 | + HttpCodecUtil.HTTP_MAX_LENGTH + " bytes");
59 | }
60 | return data;
61 | }
62 |
63 | @Override
64 | public boolean isLast() {
65 | return last;
66 | }
67 |
68 | @Override
69 | public HttpDataFrame setLast(boolean last) {
70 | this.last = last;
71 | return this;
72 | }
73 |
74 | @Override
75 | public HttpDataFrame setStreamId(int streamId) {
76 | super.setStreamId(streamId);
77 | return this;
78 | }
79 |
80 | @Override
81 | public ByteBuf content() {
82 | if (data.refCnt() <= 0) {
83 | throw new IllegalReferenceCountException(data.refCnt());
84 | }
85 | return data;
86 | }
87 |
88 | @Override
89 | public HttpDataFrame copy() {
90 | HttpDataFrame frame = new DefaultHttpDataFrame(getStreamId(), content().copy());
91 | frame.setLast(isLast());
92 | return frame;
93 | }
94 |
95 | @Override
96 | public HttpDataFrame duplicate() {
97 | HttpDataFrame frame = new DefaultHttpDataFrame(getStreamId(), content().duplicate());
98 | frame.setLast(isLast());
99 | return frame;
100 | }
101 |
102 | @Override
103 | public int refCnt() {
104 | return data.refCnt();
105 | }
106 |
107 | @Override
108 | public HttpDataFrame retain() {
109 | data.retain();
110 | return this;
111 | }
112 |
113 | @Override
114 | public HttpDataFrame retain(int increment) {
115 | data.retain(increment);
116 | return this;
117 | }
118 |
119 | @Override
120 | public boolean release() {
121 | return data.release();
122 | }
123 |
124 | @Override
125 | public boolean release(int decrement) {
126 | return data.release(decrement);
127 | }
128 |
129 | @Override
130 | public String toString() {
131 | StringBuilder buf = new StringBuilder();
132 | buf.append(StringUtil.simpleClassName(this));
133 | buf.append("(last: ");
134 | buf.append(isLast());
135 | buf.append(')');
136 | buf.append(StringUtil.NEWLINE);
137 | buf.append("--> Stream-ID = ");
138 | buf.append(getStreamId());
139 | buf.append(StringUtil.NEWLINE);
140 | buf.append("--> Size = ");
141 | if (refCnt() == 0) {
142 | buf.append("(freed)");
143 | } else {
144 | buf.append(content().readableBytes());
145 | }
146 | return buf.toString();
147 | }
148 |
149 | @Override
150 | public ByteBufHolder touch() {
151 | data.touch();
152 | return this;
153 | }
154 |
155 | @Override
156 | public ByteBufHolder touch(Object o) {
157 | data.touch(o);
158 | return this;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/Pipe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.util.LinkedList;
19 | import java.util.Objects;
20 | import java.util.Queue;
21 | import java.util.concurrent.ConcurrentLinkedQueue;
22 |
23 | import io.netty.channel.ChannelException;
24 | import io.netty.util.concurrent.Future;
25 | import io.netty.util.concurrent.ImmediateEventExecutor;
26 | import io.netty.util.concurrent.Promise;
27 |
28 | /**
29 | * Implements a stream that pipes objects between its two ends.
30 | * Futures are used to communicate when messages are sent and received.
31 | *
32 | * @param the type of objects to send along the pipe
33 | */
34 | public class Pipe {
35 |
36 | private static final ChannelException PIPE_CLOSED = new ChannelException("pipe closed");
37 |
38 | private static final Future SENT_FUTURE =
39 | ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
40 | private static final Future CLOSED_FUTURE =
41 | ImmediateEventExecutor.INSTANCE.newFailedFuture(PIPE_CLOSED);
42 |
43 | private Queue sendQueue = new LinkedList();
44 | private Queue> receiveQueue = new ConcurrentLinkedQueue>();
45 |
46 | private boolean closed;
47 |
48 | /**
49 | * Holds a message and an associated future.
50 | */
51 | private final class Node {
52 | public T message;
53 | public Promise promise;
54 |
55 | Node(T message, Promise promise) {
56 | this.message = message;
57 | this.promise = promise;
58 | }
59 | }
60 |
61 | /**
62 | * Creates a new pipe and uses instances of {@link ImmediateEventExecutor}
63 | * for the send and receive executors.
64 | *
65 | * @see ImmediateEventExecutor#INSTANCE
66 | */
67 | public Pipe() {
68 | super();
69 | }
70 |
71 | /**
72 | * Sends a message to this pipe. Returns a {@link Future} that is completed
73 | * when the message is received.
74 | *
75 | * If the pipe is closed then this will return a failed future.
76 | *
77 | * @param message the message to send to the pipe
78 | * @return a {@link Future} that is satisfied when the message is received,
79 | * or a failed future if the pipe is closed.
80 | * @throws NullPointerException if the message is {@code null}.
81 | * @throws IllegalStateException if the message could not be added to the queue for some reason.
82 | * @see #receive()
83 | */
84 | public Future send(T message) {
85 | Objects.requireNonNull(message, "msg");
86 |
87 | Promise receivePromise;
88 |
89 | synchronized (this) {
90 | if (closed) {
91 | return CLOSED_FUTURE;
92 | }
93 |
94 | receivePromise = receiveQueue.poll();
95 | if (receivePromise == null) {
96 | Promise sendPromise = ImmediateEventExecutor.INSTANCE.newPromise();
97 | sendQueue.add(new Node(message, sendPromise));
98 | return sendPromise;
99 | }
100 | }
101 |
102 | receivePromise.setSuccess(message);
103 | return SENT_FUTURE;
104 | }
105 |
106 | /**
107 | * Receives a message from this pipe.
108 | *
109 | * If the pipe is closed then this will return a failed future.
110 | */
111 | public Future receive() {
112 | Node node;
113 |
114 | synchronized (this) {
115 | node = sendQueue.poll();
116 | if (node == null) {
117 | if (closed) {
118 | return ImmediateEventExecutor.INSTANCE.newFailedFuture(PIPE_CLOSED);
119 | }
120 |
121 | Promise promise = ImmediateEventExecutor.INSTANCE.newPromise();
122 | receiveQueue.add(promise);
123 | return promise;
124 | }
125 | }
126 |
127 | node.promise.setSuccess(null);
128 | return ImmediateEventExecutor.INSTANCE.newSucceededFuture(node.message);
129 | }
130 |
131 | /**
132 | * Closes this pipe. This fails all outstanding receive futures.
133 | * This does nothing if the pipe is already closed.
134 | */
135 | public void close() {
136 | synchronized (this) {
137 | if (closed) {
138 | return;
139 | }
140 | closed = true;
141 | }
142 |
143 | while (!receiveQueue.isEmpty()) {
144 | receiveQueue.poll().setFailure(PIPE_CLOSED);
145 | }
146 | }
147 |
148 | /**
149 | * Checks if this pipe is closed.
150 | *
151 | * @return whether this pipe is closed.
152 | */
153 | public synchronized boolean isClosed() {
154 | return closed;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpHeaderBlockEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.io.IOException;
19 | import java.nio.charset.StandardCharsets;
20 | import java.util.List;
21 | import java.util.Locale;
22 |
23 | import io.netty.buffer.ByteBuf;
24 | import io.netty.buffer.ByteBufOutputStream;
25 | import io.netty.buffer.Unpooled;
26 | import io.netty.channel.ChannelHandlerContext;
27 |
28 | import com.twitter.hpack.Encoder;
29 |
30 | public class HttpHeaderBlockEncoder {
31 |
32 | private static final byte[] COOKIE = {'c', 'o', 'o', 'k', 'i', 'e'};
33 | private static final byte[] EMPTY = {};
34 |
35 | private int encoderMaxHeaderTableSize;
36 | private int decoderMaxHeaderTableSize;
37 | private int maxHeaderTableSize;
38 | private Encoder encoder;
39 |
40 | /**
41 | * Create a new instance.
42 | */
43 | public HttpHeaderBlockEncoder(int maxHeaderTableSize) {
44 | encoderMaxHeaderTableSize = maxHeaderTableSize;
45 | decoderMaxHeaderTableSize = maxHeaderTableSize;
46 | this.maxHeaderTableSize = maxHeaderTableSize;
47 | encoder = new Encoder(maxHeaderTableSize);
48 | }
49 |
50 | /**
51 | * Set the maximum header table size allowed by the encoder.
52 | *
53 | * @param encoderMaxHeaderTableSize the maximum header table size allowed by the encoder
54 | */
55 | public void setEncoderMaxHeaderTableSize(int encoderMaxHeaderTableSize) {
56 | this.encoderMaxHeaderTableSize = encoderMaxHeaderTableSize;
57 | if (encoderMaxHeaderTableSize < maxHeaderTableSize) {
58 | maxHeaderTableSize = encoderMaxHeaderTableSize;
59 | }
60 | }
61 |
62 | /**
63 | * Set the maximum header table size allowed by the peer's encoder.
64 | * This is the value of SETTINGS_HEADER_TABLE_SIZE received from the peer.
65 | *
66 | * @param decoderMaxHeaderTableSize the maximum header table size allowed by the decoder
67 | */
68 | public void setDecoderMaxHeaderTableSize(int decoderMaxHeaderTableSize) {
69 | this.decoderMaxHeaderTableSize = decoderMaxHeaderTableSize;
70 | if (decoderMaxHeaderTableSize < maxHeaderTableSize) {
71 | maxHeaderTableSize = decoderMaxHeaderTableSize;
72 | }
73 | }
74 |
75 | /**
76 | * Encode the header block frame.
77 | */
78 | public ByteBuf encode(ChannelHandlerContext ctx, HttpHeaderBlockFrame frame) throws IOException {
79 | ByteBuf buf = Unpooled.buffer();
80 | ByteBufOutputStream out = new ByteBufOutputStream(buf);
81 |
82 | // The current allowable max header table size is the
83 | // minimum of the encoder and decoder allowable sizes
84 | int allowableHeaderTableSize = Math.min(encoderMaxHeaderTableSize, decoderMaxHeaderTableSize);
85 |
86 | // maxHeaderTableSize will hold the smallest size seen the
87 | // last call to encode. This might be smaller than the
88 | // current allowable max header table size
89 | if (maxHeaderTableSize < allowableHeaderTableSize) {
90 | encoder.setMaxHeaderTableSize(out, maxHeaderTableSize);
91 | }
92 |
93 | // Check if the current allowable size is equal to the encoder's
94 | // capacity and set the new size if necessary
95 | if (allowableHeaderTableSize != encoder.getMaxHeaderTableSize()) {
96 | encoder.setMaxHeaderTableSize(out, allowableHeaderTableSize);
97 | }
98 |
99 | // Store the current allowable size for the next call
100 | maxHeaderTableSize = allowableHeaderTableSize;
101 |
102 | // Now we can encode headers
103 | for (String name : frame.headers().names()) {
104 | if ("cookie".equalsIgnoreCase(name)) {
105 | // Sec. 8.1.3.4. Cookie Header Field
106 | for (String value : frame.headers().getAll(name)) {
107 | for (String crumb : value.split(";")) {
108 | byte[] valueBytes = crumb.trim().getBytes(StandardCharsets.UTF_8);
109 | encoder.encodeHeader(out, COOKIE, valueBytes, true);
110 | }
111 | }
112 | } else {
113 | byte[] nameBytes = name.toLowerCase(Locale.ENGLISH).getBytes(StandardCharsets.UTF_8);
114 | // Sec. 8.1.3.3. Header Field Ordering
115 | List values = frame.headers().getAll(name);
116 | if (values.size() == 0) {
117 | encoder.encodeHeader(out, nameBytes, EMPTY, false);
118 | } else {
119 | for (String value : values) {
120 | byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
121 | encoder.encodeHeader(out, nameBytes, valueBytes, false);
122 | }
123 | }
124 | }
125 | }
126 |
127 | return buf;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpHeaderBlockDecoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.io.IOException;
19 | import java.nio.charset.StandardCharsets;
20 |
21 | import io.netty.buffer.ByteBuf;
22 | import io.netty.buffer.ByteBufInputStream;
23 | import io.netty.buffer.Unpooled;
24 | import io.netty.handler.codec.http.HttpHeaders;
25 |
26 | import com.twitter.hpack.Decoder;
27 | import com.twitter.hpack.HeaderListener;
28 |
29 | final class HttpHeaderBlockDecoder {
30 |
31 | private static final HeaderListener NULL_HEADER_LISTENER = new NullHeaderListener();
32 |
33 | private Decoder decoder;
34 | private ByteBuf cumulation;
35 |
36 | public HttpHeaderBlockDecoder(int maxHeaderSize, int maxHeaderTableSize) {
37 | decoder = new Decoder(maxHeaderSize, maxHeaderTableSize);
38 | }
39 |
40 | /**
41 | * Set the maximum header table size allowed by the decoder.
42 | * This is the value of SETTINGS_HEADER_TABLE_SIZE sent to the peer.
43 | *
44 | * @param maxHeaderTableSize the maximum header table size allowed by the decoder
45 | */
46 | public void setMaxHeaderTableSize(int maxHeaderTableSize) {
47 | decoder.setMaxHeaderTableSize(maxHeaderTableSize);
48 | }
49 |
50 | public void decode(ByteBuf headerBlock, final HttpHeaderBlockFrame frame) throws IOException {
51 | HeaderListener headerListener = NULL_HEADER_LISTENER;
52 | if (frame != null) {
53 | headerListener = new HeaderListenerImpl(frame.headers());
54 | }
55 |
56 | if (cumulation == null) {
57 | decoder.decode(new ByteBufInputStream(headerBlock), headerListener);
58 | if (headerBlock.isReadable()) {
59 | cumulation = Unpooled.buffer(headerBlock.readableBytes());
60 | cumulation.writeBytes(headerBlock);
61 | }
62 | } else {
63 | cumulation.writeBytes(headerBlock);
64 | decoder.decode(new ByteBufInputStream(cumulation), headerListener);
65 | if (cumulation.isReadable()) {
66 | cumulation.discardReadBytes();
67 | } else {
68 | cumulation.release();
69 | cumulation = null;
70 | }
71 | }
72 | }
73 |
74 | public void endHeaderBlock(final HttpHeaderBlockFrame frame) {
75 | if (cumulation != null) {
76 | if (cumulation.isReadable() && frame != null) {
77 | frame.setInvalid();
78 | }
79 | cumulation.release();
80 | cumulation = null;
81 | }
82 |
83 | boolean truncated = decoder.endHeaderBlock();
84 |
85 | if (truncated && frame != null) {
86 | frame.setTruncated();
87 | }
88 | }
89 |
90 | private static final class NullHeaderListener implements HeaderListener {
91 | @Override
92 | public void addHeader(byte[] name, byte[] value, boolean sensitive) {
93 | // No Op
94 | }
95 | }
96 |
97 | private static final class HeaderListenerImpl implements HeaderListener {
98 |
99 | private final HttpHeaders headers;
100 |
101 | HeaderListenerImpl(HttpHeaders headers) {
102 | this.headers = headers;
103 | }
104 |
105 | @Override
106 | public void addHeader(byte[] name, byte[] value, boolean sensitive) {
107 | String nameStr = new String(name, StandardCharsets.UTF_8);
108 |
109 | // check for empty value
110 | if (value.length == 0) {
111 | addHeader(nameStr, "");
112 | return;
113 | }
114 |
115 | // Sec. 8.1.3.3. Header Field Ordering
116 | int index = 0;
117 | int offset = 0;
118 | while (index < value.length) {
119 | while (index < value.length && value[index] != (byte) 0) {
120 | index++;
121 | }
122 | if (index - offset == 0) {
123 | addHeader(nameStr, "");
124 | } else {
125 | String valueStr = new String(value, offset, index - offset, StandardCharsets.UTF_8);
126 | addHeader(nameStr, valueStr);
127 | }
128 | index++;
129 | offset = index;
130 | }
131 | }
132 |
133 | private void addHeader(String name, String value) {
134 | boolean crumb = "cookie".equalsIgnoreCase(name);
135 | if (value.length() == 0) {
136 | if (crumb || headers.contains(name)) {
137 | return;
138 | }
139 | }
140 | if (crumb) {
141 | // Sec. 8.1.3.4. Cookie Header Field
142 | String cookie = headers.get(name);
143 | if (cookie == null) {
144 | headers.set(name, value);
145 | } else {
146 | headers.set(name, cookie + "; " + value);
147 | }
148 | } else {
149 | headers.add(name, value);
150 | }
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpErrorCode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | public class HttpErrorCode implements Comparable {
19 |
20 | /**
21 | * 0 No Error
22 | */
23 | public static final HttpErrorCode NO_ERROR =
24 | new HttpErrorCode(0, "NO_ERROR");
25 |
26 | /**
27 | * 1 Protocol Error
28 | */
29 | public static final HttpErrorCode PROTOCOL_ERROR =
30 | new HttpErrorCode(1, "PROTOCOL_ERROR");
31 |
32 | /**
33 | * 2 Internal Error
34 | */
35 | public static final HttpErrorCode INTERNAL_ERROR =
36 | new HttpErrorCode(2, "INTERNAL_ERROR");
37 |
38 | /**
39 | * 3 Flow Control Error
40 | */
41 | public static final HttpErrorCode FLOW_CONTROL_ERROR =
42 | new HttpErrorCode(3, "FLOW_CONTROL_ERROR");
43 |
44 | /**
45 | * 4 Settings Timeout
46 | */
47 | public static final HttpErrorCode SETTINGS_TIMEOUT =
48 | new HttpErrorCode(4, "SETTINGS_TIMEOUT");
49 |
50 | /**
51 | * 5 Stream Closed
52 | */
53 | public static final HttpErrorCode STREAM_CLOSED =
54 | new HttpErrorCode(5, "STREAM_CLOSED");
55 |
56 | /**
57 | * 6 Frame Size Error
58 | */
59 | public static final HttpErrorCode FRAME_SIZE_ERROR =
60 | new HttpErrorCode(6, "FRAME_SIZE_ERROR");
61 |
62 | /**
63 | * 7 Refused Stream
64 | */
65 | public static final HttpErrorCode REFUSED_STREAM =
66 | new HttpErrorCode(7, "REFUSED_STREAM");
67 |
68 | /**
69 | * 8 Cancel
70 | */
71 | public static final HttpErrorCode CANCEL =
72 | new HttpErrorCode(8, "CANCEL");
73 |
74 | /**
75 | * 9 Compression Error
76 | */
77 | public static final HttpErrorCode COMPRESSION_ERROR =
78 | new HttpErrorCode(9, "COMPRESSION_ERROR");
79 |
80 | /**
81 | * 10 Connect Error
82 | */
83 | public static final HttpErrorCode CONNECT_ERROR =
84 | new HttpErrorCode(10, "CONNECT_ERROR");
85 |
86 | /**
87 | * 11 Enhance Your Calm (420)
88 | */
89 | public static final HttpErrorCode ENHANCE_YOUR_CALM =
90 | new HttpErrorCode(420, "ENHANCE_YOUR_CALM");
91 |
92 | /**
93 | * 12 Inadequate Security
94 | */
95 | public static final HttpErrorCode INADEQUATE_SECURITY =
96 | new HttpErrorCode(12, "INADEQUATE_SECURITY");
97 |
98 | /**
99 | * Returns the {@link HttpErrorCode} represented by the specified code.
100 | * If the specified code is a defined HTTP error code, a cached instance
101 | * will be returned. Otherwise, a new instance will be returned.
102 | */
103 | public static HttpErrorCode valueOf(int code) {
104 | switch (code) {
105 | case 0:
106 | return NO_ERROR;
107 | case 1:
108 | return PROTOCOL_ERROR;
109 | case 2:
110 | return INTERNAL_ERROR;
111 | case 3:
112 | return FLOW_CONTROL_ERROR;
113 | case 4:
114 | return SETTINGS_TIMEOUT;
115 | case 5:
116 | return STREAM_CLOSED;
117 | case 6:
118 | return FRAME_SIZE_ERROR;
119 | case 7:
120 | return REFUSED_STREAM;
121 | case 8:
122 | return CANCEL;
123 | case 9:
124 | return COMPRESSION_ERROR;
125 | case 10:
126 | return CONNECT_ERROR;
127 | case 11:
128 | return ENHANCE_YOUR_CALM;
129 | case 12:
130 | return INADEQUATE_SECURITY;
131 | case 420:
132 | return ENHANCE_YOUR_CALM;
133 | default:
134 | return new HttpErrorCode(code, "UNKNOWN (" + code + ')');
135 | }
136 | }
137 |
138 | private final int code;
139 |
140 | private final String statusPhrase;
141 |
142 | /**
143 | * Creates a new instance with the specified {@code code} and its
144 | * {@code statusPhrase}.
145 | */
146 | public HttpErrorCode(int code, String statusPhrase) {
147 | if (statusPhrase == null) {
148 | throw new NullPointerException("statusPhrase");
149 | }
150 |
151 | this.code = code;
152 | this.statusPhrase = statusPhrase;
153 | }
154 |
155 | /**
156 | * Returns the code of this status.
157 | */
158 | public int getCode() {
159 | return code;
160 | }
161 |
162 | /**
163 | * Returns the status phrase of this status.
164 | */
165 | public String getStatusPhrase() {
166 | return statusPhrase;
167 | }
168 |
169 | @Override
170 | public int hashCode() {
171 | return getCode();
172 | }
173 |
174 | @Override
175 | public boolean equals(Object o) {
176 | if (!(o instanceof HttpErrorCode)) {
177 | return false;
178 | }
179 |
180 | return getCode() == ((HttpErrorCode) o).getCode();
181 | }
182 |
183 | @Override
184 | public String toString() {
185 | return getStatusPhrase();
186 | }
187 |
188 | @Override
189 | public int compareTo(HttpErrorCode o) {
190 | return getCode() - o.getCode();
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/test/java/com/twitter/http2/HttpHeaderCompressionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.io.IOException;
19 | import java.nio.charset.StandardCharsets;
20 | import java.util.ArrayList;
21 | import java.util.List;
22 | import java.util.concurrent.atomic.AtomicReference;
23 |
24 | import io.netty.bootstrap.Bootstrap;
25 | import io.netty.bootstrap.ServerBootstrap;
26 | import io.netty.buffer.Unpooled;
27 | import io.netty.channel.Channel;
28 | import io.netty.channel.ChannelFuture;
29 | import io.netty.channel.ChannelHandlerContext;
30 | import io.netty.channel.ChannelInboundHandlerAdapter;
31 | import io.netty.channel.ChannelInitializer;
32 | import io.netty.channel.local.LocalAddress;
33 | import io.netty.channel.local.LocalChannel;
34 | import io.netty.channel.local.LocalEventLoopGroup;
35 | import io.netty.channel.local.LocalServerChannel;
36 | import io.netty.handler.codec.http.HttpHeaders;
37 | import org.junit.Test;
38 |
39 | import static org.junit.Assert.assertTrue;
40 |
41 | public class HttpHeaderCompressionTest {
42 |
43 | @Test
44 | public void testHttpHeadersFrame() throws Throwable {
45 | HttpHeadersFrame httpHeadersFrame = new DefaultHttpHeadersFrame(1);
46 | httpHeadersFrame.headers().add("name", "value");
47 | testHeaderEcho(httpHeadersFrame);
48 | }
49 |
50 | private void testHeaderEcho(HttpHeaderBlockFrame frame) throws Throwable {
51 | final EchoHandler sh = new EchoHandler();
52 | final TestHandler ch = new TestHandler(frame);
53 |
54 | ServerBootstrap sb = new ServerBootstrap()
55 | .group(new LocalEventLoopGroup())
56 | .channel(LocalServerChannel.class)
57 | .childHandler(new ChannelInitializer() {
58 | @Override
59 | public void initChannel(LocalChannel channel) throws Exception {
60 | channel.pipeline().addLast(new HttpConnectionHandler(true), sh);
61 | }
62 | });
63 | Bootstrap cb = new Bootstrap()
64 | .group(new LocalEventLoopGroup())
65 | .channel(LocalChannel.class)
66 | .handler(new ChannelInitializer() {
67 | @Override
68 | public void initChannel(LocalChannel channel) throws Exception {
69 | channel.pipeline().addLast(new HttpConnectionHandler(false), ch);
70 | }
71 | });
72 |
73 | LocalAddress localAddress = new LocalAddress("HttpHeaderCompressionTest");
74 | Channel sc = sb.bind(localAddress).sync().channel();
75 | ChannelFuture ccf = cb.connect(localAddress);
76 | assertTrue(ccf.awaitUninterruptibly().isSuccess());
77 |
78 | while (!ch.success) {
79 | if (sh.exception.get() != null) {
80 | break;
81 | }
82 | if (ch.exception.get() != null) {
83 | break;
84 | }
85 |
86 | try {
87 | Thread.sleep(1);
88 | } catch (InterruptedException e) {
89 | // Ignore.
90 | }
91 | }
92 |
93 | sc.close().awaitUninterruptibly();
94 | cb.group().shutdownGracefully();
95 | sb.group().shutdownGracefully();
96 |
97 | if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
98 | throw sh.exception.get();
99 | }
100 | if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) {
101 | throw ch.exception.get();
102 | }
103 | if (sh.exception.get() != null) {
104 | throw sh.exception.get();
105 | }
106 | if (ch.exception.get() != null) {
107 | throw ch.exception.get();
108 | }
109 | }
110 |
111 | private static class EchoHandler extends ChannelInboundHandlerAdapter {
112 | public final AtomicReference exception = new AtomicReference();
113 |
114 | @Override
115 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
116 | ctx.writeAndFlush(msg);
117 | }
118 |
119 | @Override
120 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
121 | if (exception.compareAndSet(null, cause)) {
122 | ctx.close();
123 | }
124 | }
125 | }
126 |
127 | private static class TestHandler extends ChannelInboundHandlerAdapter {
128 | private static final byte[] CONNECTION_HEADER =
129 | "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.US_ASCII);
130 |
131 | public final AtomicReference exception = new AtomicReference();
132 | public final HttpHeaderBlockFrame frame;
133 |
134 | public volatile boolean success;
135 |
136 | public TestHandler(HttpHeaderBlockFrame frame) {
137 | this.frame = frame;
138 | }
139 |
140 | @Override
141 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
142 | ctx.write(Unpooled.wrappedBuffer(CONNECTION_HEADER));
143 | ctx.writeAndFlush(frame);
144 | }
145 |
146 | @Override
147 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
148 | assertTrue(msg instanceof HttpHeaderBlockFrame);
149 | HttpHeaders actual = ((HttpHeaderBlockFrame) msg).headers();
150 | HttpHeaders expected = frame.headers();
151 | for (String name : expected.names()) {
152 | List expectedValues = new ArrayList(expected.getAll(name));
153 | List actualValues = new ArrayList(actual.getAll(name));
154 | assertTrue(actualValues.containsAll(expectedValues));
155 | actualValues.removeAll(expectedValues);
156 | assertTrue(actualValues.isEmpty());
157 | actual.remove(name);
158 | }
159 | assertTrue(actual.isEmpty());
160 | success = true;
161 | }
162 |
163 | @Override
164 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
165 | if (exception.compareAndSet(null, cause)) {
166 | ctx.close();
167 | }
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 | com.twitter
5 | netty-http2
6 | 0.17.12-SNAPSHOT
7 | HTTP/2
8 | http://github.com/twitter/netty-http2
9 | HTTP/2 for Netty
10 |
11 |
12 | scm:git:git@github.com:twitter/netty-http2.git
13 | scm:git:git@github.com:twitter/netty-http2.git
14 | scm:git:git@github.com:twitter/netty-http2.git
15 |
16 |
17 |
18 |
19 | The Apache Software License, Version 2.0
20 | http://www.apache.org/licenses/LICENSE-2.0.txt
21 |
22 |
23 |
24 |
25 |
26 | Jeff Pinner
27 | jpinner@twitter.com
28 |
29 |
30 |
31 |
32 | 1.6
33 | 1.6
34 | UTF-8
35 | UTF-8
36 |
37 |
38 |
39 |
40 | sonatype-nexus-snapshots
41 | Sonatype OSS
42 | https://oss.sonatype.org/content/repositories/snapshots
43 |
44 |
45 | sonatype-nexus-staging
46 | Nexus Release Repository
47 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
48 |
49 |
50 |
51 |
52 |
53 | com.twitter
54 | hpack
55 | 1.0.2
56 |
57 |
58 | io.netty
59 | netty-codec-http
60 | 4.1.0.CR4
61 |
62 |
63 |
64 | junit
65 | junit
66 | 4.12
67 | test
68 |
69 |
70 | org.mockito
71 | mockito-core
72 | 1.9.5
73 | test
74 |
75 |
76 |
77 |
78 |
79 | sonatype-nexus-snapshots
80 | https://oss.sonatype.org/content/repositories/snapshots
81 |
82 | false
83 |
84 |
85 | true
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | org.apache.maven.plugins
95 | maven-release-plugin
96 | 2.1
97 |
98 | forked-path
99 | false
100 | -Psonatype-oss-release
101 |
102 |
103 |
104 |
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-compiler-plugin
109 | 2.5.1
110 |
111 | 1.6
112 | 1.6
113 |
114 |
115 |
116 | org.apache.maven.plugins
117 | maven-surefire-plugin
118 | 2.12
119 |
120 | -Xmx1024m
121 | false
122 |
123 | **/Test*.java
124 | **/*Test.java
125 | **/*Spec.java
126 |
127 |
128 |
129 |
130 | org.codehaus.mojo
131 | cobertura-maven-plugin
132 | 2.5.2
133 |
134 | xml
135 | 256m
136 | true
137 |
138 |
139 |
140 | org.eluder.coveralls
141 | coveralls-maven-plugin
142 | 2.2.0
143 |
144 |
145 |
146 |
147 |
148 | sonatype-oss-release
149 |
150 |
151 |
152 | org.apache.maven.plugins
153 | maven-source-plugin
154 | 2.1.2
155 |
156 |
157 | attach-sources
158 |
159 | jar-no-fork
160 |
161 |
162 |
163 |
164 |
165 | org.apache.maven.plugins
166 | maven-javadoc-plugin
167 | 2.7
168 |
169 |
170 | attach-javadocs
171 |
172 | jar
173 |
174 |
175 |
176 |
177 |
178 | org.apache.maven.plugins
179 | maven-gpg-plugin
180 | 1.1
181 |
182 |
183 | sign-artifacts
184 | verify
185 |
186 | sign
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpFrameEncoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.util.Set;
19 |
20 | import io.netty.buffer.ByteBuf;
21 | import io.netty.buffer.Unpooled;
22 |
23 | import static com.twitter.http2.HttpCodecUtil.HTTP_CONTINUATION_FRAME;
24 | import static com.twitter.http2.HttpCodecUtil.HTTP_DATA_FRAME;
25 | import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_DEPENDENCY;
26 | import static com.twitter.http2.HttpCodecUtil.HTTP_DEFAULT_WEIGHT;
27 | import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_ACK;
28 | import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_HEADERS;
29 | import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_END_STREAM;
30 | import static com.twitter.http2.HttpCodecUtil.HTTP_FLAG_PRIORITY;
31 | import static com.twitter.http2.HttpCodecUtil.HTTP_FRAME_HEADER_SIZE;
32 | import static com.twitter.http2.HttpCodecUtil.HTTP_GOAWAY_FRAME;
33 | import static com.twitter.http2.HttpCodecUtil.HTTP_HEADERS_FRAME;
34 | import static com.twitter.http2.HttpCodecUtil.HTTP_MAX_LENGTH;
35 | import static com.twitter.http2.HttpCodecUtil.HTTP_PING_FRAME;
36 | import static com.twitter.http2.HttpCodecUtil.HTTP_PRIORITY_FRAME;
37 | import static com.twitter.http2.HttpCodecUtil.HTTP_PUSH_PROMISE_FRAME;
38 | import static com.twitter.http2.HttpCodecUtil.HTTP_RST_STREAM_FRAME;
39 | import static com.twitter.http2.HttpCodecUtil.HTTP_SETTINGS_FRAME;
40 | import static com.twitter.http2.HttpCodecUtil.HTTP_WINDOW_UPDATE_FRAME;
41 |
42 | /**
43 | * Encodes an HTTP/2 Frame into a {@link ByteBuf}.
44 | */
45 | public class HttpFrameEncoder {
46 |
47 | /**
48 | * Encode an HTTP/2 DATA Frame
49 | */
50 | public ByteBuf encodeDataFrame(int streamId, boolean endStream, ByteBuf data) {
51 | byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
52 | ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
53 | writeFrameHeader(header, data.readableBytes(), HTTP_DATA_FRAME, flags, streamId);
54 | return Unpooled.wrappedBuffer(header, data);
55 | }
56 |
57 | /**
58 | * Encode an HTTP/2 HEADERS Frame
59 | */
60 | public ByteBuf encodeHeadersFrame(
61 | int streamId,
62 | boolean endStream,
63 | boolean exclusive,
64 | int dependency,
65 | int weight,
66 | ByteBuf headerBlock
67 | ) {
68 | byte flags = endStream ? HTTP_FLAG_END_STREAM : 0;
69 | boolean hasPriority = exclusive
70 | || dependency != HTTP_DEFAULT_DEPENDENCY || weight != HTTP_DEFAULT_WEIGHT;
71 | if (hasPriority) {
72 | flags |= HTTP_FLAG_PRIORITY;
73 | }
74 | int maxLength = hasPriority ? HTTP_MAX_LENGTH - 5 : HTTP_MAX_LENGTH;
75 | boolean needsContinuations = headerBlock.readableBytes() > maxLength;
76 | if (!needsContinuations) {
77 | flags |= HTTP_FLAG_END_HEADERS;
78 | }
79 | int length = needsContinuations ? maxLength : headerBlock.readableBytes();
80 | if (hasPriority) {
81 | length += 5;
82 | }
83 | int frameLength = hasPriority ? HTTP_FRAME_HEADER_SIZE + 5 : HTTP_FRAME_HEADER_SIZE;
84 | ByteBuf header = Unpooled.buffer(frameLength);
85 | writeFrameHeader(header, length, HTTP_HEADERS_FRAME, flags, streamId);
86 | if (hasPriority) {
87 | if (exclusive) {
88 | header.writeInt(dependency | 0x80000000);
89 | } else {
90 | header.writeInt(dependency);
91 | }
92 | header.writeByte(weight - 1);
93 | length -= 5;
94 | }
95 | ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
96 | if (needsContinuations) {
97 | while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
98 | header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
99 | writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
100 | frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
101 | }
102 | header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
103 | writeFrameHeader(
104 | header,
105 | headerBlock.readableBytes(),
106 | HTTP_CONTINUATION_FRAME,
107 | HTTP_FLAG_END_HEADERS,
108 | streamId
109 | );
110 | frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
111 | }
112 | return frame;
113 | }
114 |
115 | /**
116 | * Encode an HTTP/2 PRIORITY Frame
117 | */
118 | public ByteBuf encodePriorityFrame(int streamId, boolean exclusive, int dependency, int weight) {
119 | int length = 5;
120 | byte flags = 0;
121 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
122 | writeFrameHeader(frame, length, HTTP_PRIORITY_FRAME, flags, streamId);
123 | if (exclusive) {
124 | frame.writeInt(dependency | 0x80000000);
125 | } else {
126 | frame.writeInt(dependency);
127 | }
128 | frame.writeByte(weight - 1);
129 | return frame;
130 | }
131 |
132 | /**
133 | * Encode an HTTP/2 RST_STREAM Frame
134 | */
135 | public ByteBuf encodeRstStreamFrame(int streamId, int errorCode) {
136 | int length = 4;
137 | byte flags = 0;
138 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
139 | writeFrameHeader(frame, length, HTTP_RST_STREAM_FRAME, flags, streamId);
140 | frame.writeInt(errorCode);
141 | return frame;
142 | }
143 |
144 | /**
145 | * Encode an HTTP/2 SETTINGS Frame
146 | */
147 | public ByteBuf encodeSettingsFrame(HttpSettingsFrame httpSettingsFrame) {
148 | Set ids = httpSettingsFrame.getIds();
149 | int length = ids.size() * 6;
150 | byte flags = httpSettingsFrame.isAck() ? HTTP_FLAG_ACK : 0;
151 | int streamId = 0;
152 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
153 | writeFrameHeader(frame, length, HTTP_SETTINGS_FRAME, flags, streamId);
154 | for (int id : ids) {
155 | frame.writeShort(id);
156 | frame.writeInt(httpSettingsFrame.getValue(id));
157 | }
158 | return frame;
159 | }
160 |
161 | /**
162 | * Encode an HTTP/2 PUSH_PROMISE Frame
163 | */
164 | public ByteBuf encodePushPromiseFrame(int streamId, int promisedStreamId, ByteBuf headerBlock) {
165 | boolean needsContinuations = headerBlock.readableBytes() > HTTP_MAX_LENGTH - 4;
166 | int length = needsContinuations ? HTTP_MAX_LENGTH - 4 : headerBlock.readableBytes();
167 | byte flags = needsContinuations ? 0 : HTTP_FLAG_END_HEADERS;
168 | ByteBuf header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + 4);
169 | writeFrameHeader(header, length + 4, HTTP_PUSH_PROMISE_FRAME, flags, streamId);
170 | header.writeInt(promisedStreamId);
171 | ByteBuf frame = Unpooled.wrappedBuffer(header, headerBlock.readSlice(length));
172 | if (needsContinuations) {
173 | while (headerBlock.readableBytes() > HTTP_MAX_LENGTH) {
174 | header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
175 | writeFrameHeader(header, HTTP_MAX_LENGTH, HTTP_CONTINUATION_FRAME, (byte) 0, streamId);
176 | frame = Unpooled.wrappedBuffer(frame, header, headerBlock.readSlice(HTTP_MAX_LENGTH));
177 | }
178 | header = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE);
179 | writeFrameHeader(
180 | header,
181 | headerBlock.readableBytes(),
182 | HTTP_CONTINUATION_FRAME,
183 | HTTP_FLAG_END_HEADERS,
184 | streamId
185 | );
186 | frame = Unpooled.wrappedBuffer(frame, header, headerBlock);
187 | }
188 | return frame;
189 | }
190 |
191 | /**
192 | * Encode an HTTP/2 PING Frame
193 | */
194 | public ByteBuf encodePingFrame(long data, boolean ack) {
195 | int length = 8;
196 | byte flags = ack ? HTTP_FLAG_ACK : 0;
197 | int streamId = 0;
198 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
199 | writeFrameHeader(frame, length, HTTP_PING_FRAME, flags, streamId);
200 | frame.writeLong(data);
201 | return frame;
202 | }
203 |
204 | /**
205 | * Encode an HTTP/2 GOAWAY Frame
206 | */
207 | public ByteBuf encodeGoAwayFrame(int lastStreamId, int errorCode) {
208 | int length = 8;
209 | byte flags = 0;
210 | int streamId = 0;
211 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
212 | writeFrameHeader(frame, length, HTTP_GOAWAY_FRAME, flags, streamId);
213 | frame.writeInt(lastStreamId);
214 | frame.writeInt(errorCode);
215 | return frame;
216 | }
217 |
218 | /**
219 | * Encode an HTTP/2 WINDOW_UPDATE Frame
220 | */
221 | public ByteBuf encodeWindowUpdateFrame(int streamId, int windowSizeIncrement) {
222 | int length = 4;
223 | byte flags = 0;
224 | ByteBuf frame = Unpooled.buffer(HTTP_FRAME_HEADER_SIZE + length);
225 | writeFrameHeader(frame, length, HTTP_WINDOW_UPDATE_FRAME, flags, streamId);
226 | frame.writeInt(windowSizeIncrement);
227 | return frame;
228 | }
229 |
230 | private void writeFrameHeader(ByteBuf buffer, int length, int type, byte flags, int streamId) {
231 | buffer.writeMedium(length);
232 | buffer.writeByte(type);
233 | buffer.writeByte(flags);
234 | buffer.writeInt(streamId);
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/src/main/java/com/twitter/http2/HttpStreamDecoder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Twitter, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.twitter.http2;
17 |
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.concurrent.CancellationException;
21 | import java.util.concurrent.ConcurrentHashMap;
22 |
23 | import io.netty.buffer.ByteBuf;
24 | import io.netty.channel.ChannelHandlerContext;
25 | import io.netty.handler.codec.DecoderResult;
26 | import io.netty.handler.codec.MessageToMessageDecoder;
27 | import io.netty.handler.codec.http.DefaultHttpContent;
28 | import io.netty.handler.codec.http.DefaultLastHttpContent;
29 | import io.netty.handler.codec.http.HttpHeaders;
30 | import io.netty.handler.codec.http.HttpMethod;
31 | import io.netty.handler.codec.http.HttpResponseStatus;
32 | import io.netty.handler.codec.http.HttpVersion;
33 | import io.netty.handler.codec.http.LastHttpContent;
34 |
35 | /**
36 | * Decodes {@link HttpFrame}s into {@link StreamedHttpRequest}s.
37 | */
38 | public class HttpStreamDecoder extends MessageToMessageDecoder