Http2 draft 17
Motivation: There was a new draft for HTTP/2. We should support the new draft. Modifications: - Review the HTTP/2 draft 17 specification, and update code to reflect changes. Result: Support for HTTP/2 draft 17.
This commit is contained in:
parent
b0e7992c16
commit
6dfa1f2d92
@ -19,6 +19,7 @@ import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
|||||||
import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED;
|
import static io.netty.handler.codec.http2.Http2Error.STREAM_CLOSED;
|
||||||
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
||||||
import static io.netty.handler.codec.http2.Http2Exception.streamError;
|
import static io.netty.handler.codec.http2.Http2Exception.streamError;
|
||||||
|
import static io.netty.handler.codec.http2.Http2PromisedRequestVerifier.ALWAYS_VERIFY;
|
||||||
import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED;
|
import static io.netty.handler.codec.http2.Http2Stream.State.CLOSED;
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
@ -42,6 +43,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
private final Http2ConnectionEncoder encoder;
|
private final Http2ConnectionEncoder encoder;
|
||||||
private final Http2FrameReader frameReader;
|
private final Http2FrameReader frameReader;
|
||||||
private final Http2FrameListener listener;
|
private final Http2FrameListener listener;
|
||||||
|
private final Http2PromisedRequestVerifier requestVerifier;
|
||||||
private boolean prefaceReceived;
|
private boolean prefaceReceived;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +55,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
private Http2ConnectionEncoder encoder;
|
private Http2ConnectionEncoder encoder;
|
||||||
private Http2FrameReader frameReader;
|
private Http2FrameReader frameReader;
|
||||||
private Http2FrameListener listener;
|
private Http2FrameListener listener;
|
||||||
|
private Http2PromisedRequestVerifier requestVerifier = ALWAYS_VERIFY;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Builder connection(Http2Connection connection) {
|
public Builder connection(Http2Connection connection) {
|
||||||
@ -89,6 +92,12 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Http2ConnectionDecoder.Builder requestVerifier(Http2PromisedRequestVerifier requestVerifier) {
|
||||||
|
this.requestVerifier = requestVerifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Http2ConnectionDecoder build() {
|
public Http2ConnectionDecoder build() {
|
||||||
return new DefaultHttp2ConnectionDecoder(this);
|
return new DefaultHttp2ConnectionDecoder(this);
|
||||||
@ -105,6 +114,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
lifecycleManager = checkNotNull(builder.lifecycleManager, "lifecycleManager");
|
lifecycleManager = checkNotNull(builder.lifecycleManager, "lifecycleManager");
|
||||||
encoder = checkNotNull(builder.encoder, "encoder");
|
encoder = checkNotNull(builder.encoder, "encoder");
|
||||||
listener = checkNotNull(builder.listener, "listener");
|
listener = checkNotNull(builder.listener, "listener");
|
||||||
|
requestVerifier = checkNotNull(builder.requestVerifier, "requestVerifier");
|
||||||
if (connection.local().flowController() == null) {
|
if (connection.local().flowController() == null) {
|
||||||
connection.local().flowController(
|
connection.local().flowController(
|
||||||
new DefaultHttp2LocalFlowController(connection, encoder.frameWriter()));
|
new DefaultHttp2LocalFlowController(connection, encoder.frameWriter()));
|
||||||
@ -508,6 +518,22 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
parentStream.id(), parentStream.state());
|
parentStream.id(), parentStream.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!requestVerifier.isAuthoritative(ctx, headers)) {
|
||||||
|
throw streamError(promisedStreamId, PROTOCOL_ERROR,
|
||||||
|
"Promised request on stream %d for promised stream %d is not authoritative",
|
||||||
|
streamId, promisedStreamId);
|
||||||
|
}
|
||||||
|
if (!requestVerifier.isCacheable(headers)) {
|
||||||
|
throw streamError(promisedStreamId, PROTOCOL_ERROR,
|
||||||
|
"Promised request on stream %d for promised stream %d is not known to be cacheable",
|
||||||
|
streamId, promisedStreamId);
|
||||||
|
}
|
||||||
|
if (!requestVerifier.isSafe(headers)) {
|
||||||
|
throw streamError(promisedStreamId, PROTOCOL_ERROR,
|
||||||
|
"Promised request on stream %d for promised stream %d is not known to be safe",
|
||||||
|
streamId, promisedStreamId);
|
||||||
|
}
|
||||||
|
|
||||||
// Reserve the push stream based with a priority based on the current stream's priority.
|
// Reserve the push stream based with a priority based on the current stream's priority.
|
||||||
connection.remote().reservePushStream(promisedStreamId, parentStream);
|
connection.remote().reservePushStream(promisedStreamId, parentStream);
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2Hea
|
|||||||
// Default handler for any other types of errors that may have occurred. For example,
|
// Default handler for any other types of errors that may have occurred. For example,
|
||||||
// the the Header builder throws IllegalArgumentException if the key or value was invalid
|
// the the Header builder throws IllegalArgumentException if the key or value was invalid
|
||||||
// for any reason (e.g. the key was an invalid pseudo-header).
|
// for any reason (e.g. the key was an invalid pseudo-header).
|
||||||
throw connectionError(PROTOCOL_ERROR, e, e.getMessage());
|
throw connectionError(COMPRESSION_ERROR, e, e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
in.close();
|
in.close();
|
||||||
|
@ -37,8 +37,8 @@ public final class Http2CodecUtil {
|
|||||||
public static final int CONNECTION_STREAM_ID = 0;
|
public static final int CONNECTION_STREAM_ID = 0;
|
||||||
public static final int HTTP_UPGRADE_STREAM_ID = 1;
|
public static final int HTTP_UPGRADE_STREAM_ID = 1;
|
||||||
public static final String HTTP_UPGRADE_SETTINGS_HEADER = "HTTP2-Settings";
|
public static final String HTTP_UPGRADE_SETTINGS_HEADER = "HTTP2-Settings";
|
||||||
public static final String HTTP_UPGRADE_PROTOCOL_NAME = "h2c-16";
|
public static final String HTTP_UPGRADE_PROTOCOL_NAME = "h2c-17";
|
||||||
public static final String TLS_UPGRADE_PROTOCOL_NAME = "h2-16";
|
public static final String TLS_UPGRADE_PROTOCOL_NAME = "h2-17";
|
||||||
|
|
||||||
public static final int PING_FRAME_PAYLOAD_LENGTH = 8;
|
public static final int PING_FRAME_PAYLOAD_LENGTH = 8;
|
||||||
public static final short MAX_UNSIGNED_BYTE = 0xFF;
|
public static final short MAX_UNSIGNED_BYTE = 0xFF;
|
||||||
|
@ -32,7 +32,6 @@ public interface Http2ConnectionDecoder extends Closeable {
|
|||||||
* Builder for new instances of {@link Http2ConnectionDecoder}.
|
* Builder for new instances of {@link Http2ConnectionDecoder}.
|
||||||
*/
|
*/
|
||||||
interface Builder {
|
interface Builder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link Http2Connection} to be used when building the decoder.
|
* Sets the {@link Http2Connection} to be used when building the decoder.
|
||||||
*/
|
*/
|
||||||
@ -63,6 +62,11 @@ public interface Http2ConnectionDecoder extends Closeable {
|
|||||||
*/
|
*/
|
||||||
Builder encoder(Http2ConnectionEncoder encoder);
|
Builder encoder(Http2ConnectionEncoder encoder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Http2PromisedRequestVerifier} used when building the decoder.
|
||||||
|
*/
|
||||||
|
Builder requestVerifier(Http2PromisedRequestVerifier requestVerifier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder instance.
|
* Creates a new decoder instance.
|
||||||
*/
|
*/
|
||||||
|
@ -22,7 +22,6 @@ import io.netty.channel.ChannelHandlerContext;
|
|||||||
* An listener of HTTP/2 frames.
|
* An listener of HTTP/2 frames.
|
||||||
*/
|
*/
|
||||||
public interface Http2FrameListener {
|
public interface Http2FrameListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles an inbound {@code DATA} frame.
|
* Handles an inbound {@code DATA} frame.
|
||||||
*
|
*
|
||||||
@ -157,11 +156,8 @@ public interface Http2FrameListener {
|
|||||||
/**
|
/**
|
||||||
* Handles an inbound PUSH_PROMISE frame. Only called if END_HEADERS encountered.
|
* Handles an inbound PUSH_PROMISE frame. Only called if END_HEADERS encountered.
|
||||||
* <p>
|
* <p>
|
||||||
* Promised requests MUST be cacheable
|
* Promised requests MUST be authoritative, cacheable, and safe.
|
||||||
* (see <a href="https://tools.ietf.org/html/rfc7231#section-4.2.3">[RFC7231], Section 4.2.3</a>) and
|
* See <a href="https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.2">[RFC http2], Seciton 8.2</a>.
|
||||||
* MUST be safe (see <a href="https://tools.ietf.org/html/rfc7231#section-4.2.1">[RFC7231], Section 4.2.1</a>).
|
|
||||||
* If these conditions do not hold the application MUST throw a {@link Http2Exception.StreamException} with
|
|
||||||
* error type {@link Http2Error#PROTOCOL_ERROR}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Only one of the following methods will be called for each HEADERS frame sequence.
|
* Only one of the following methods will be called for each HEADERS frame sequence.
|
||||||
* One will be called when the END_HEADERS flag has been received.
|
* One will be called when the END_HEADERS flag has been received.
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
||||||
|
* copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||||
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||||
|
* or implied. See the License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an extensibility point for users to define the validity of push requests.
|
||||||
|
* @see <a href="https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-8.2">[RFC http2], Section 8.2</a>.
|
||||||
|
*/
|
||||||
|
public interface Http2PromisedRequestVerifier {
|
||||||
|
/**
|
||||||
|
* Determine if a {@link Http2Headers} are authoritative for a particular {@link ChannelHandlerContext}.
|
||||||
|
* @param ctx The context on which the {@code headers} where received on.
|
||||||
|
* @param headers The headers to be verified.
|
||||||
|
* @return {@code true} if the {@code ctx} is authoritative for the {@code headers}, {@code false} otherwise.
|
||||||
|
* @see
|
||||||
|
* <a href="https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-10.1">[RFC http2], Section 10.1</a>.
|
||||||
|
*/
|
||||||
|
boolean isAuthoritative(ChannelHandlerContext ctx, Http2Headers headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a request is cacheable.
|
||||||
|
* @param headers The headers for a push request.
|
||||||
|
* @return {@code true} if the request associated with {@code headers} is known to be cacheable,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
* @see <a href="https://tools.ietf.org/html/rfc7231#section-4.2.3">[RFC 7231], Section 4.2.3</a>.
|
||||||
|
*/
|
||||||
|
boolean isCacheable(Http2Headers headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a request is safe.
|
||||||
|
* @param headers The headers for a push request.
|
||||||
|
* @return {@code true} if the request associated with {@code headers} is known to be safe,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
* @see <a href="https://tools.ietf.org/html/rfc7231#section-4.2.1">[RFC 7231], Section 4.2.1</a>.
|
||||||
|
*/
|
||||||
|
boolean isSafe(Http2Headers headers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default implementation of {@link Http2PromisedRequestVerifier} which always returns positive responses for
|
||||||
|
* all verification challenges.
|
||||||
|
*/
|
||||||
|
Http2PromisedRequestVerifier ALWAYS_VERIFY = new Http2PromisedRequestVerifier() {
|
||||||
|
@Override
|
||||||
|
public boolean isAuthoritative(ChannelHandlerContext ctx, Http2Headers headers) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheable(Http2Headers headers) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSafe(Http2Headers headers) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user