Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp InboundHttp2ToHttpAdapter builder API #4582

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,132 @@
/*
* 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.handler.codec.TooLongFrameException;

/**
* A skeletal builder implementation of {@link InboundHttp2ToHttpAdapter} and its subtypes.
*/
public abstract class AbstractInboundHttp2ToHttpAdapterBuilder<
T extends InboundHttp2ToHttpAdapter, B extends AbstractInboundHttp2ToHttpAdapterBuilder<T, B>> {

private final Http2Connection connection;
private int maxContentLength;
private boolean validateHttpHeaders;
private boolean propagateSettings;

/**
* Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}.
*
* @param connection the object which will provide connection notification events
* for the current connection
*/
protected AbstractInboundHttp2ToHttpAdapterBuilder(Http2Connection connection) {
this.connection = connection;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check for null as this should never be null.

}

@SuppressWarnings("unchecked")
protected final B self() {
return (B) this;
}

/**
* Returns the {@link Http2Connection}.
*/
protected Http2Connection connection() {
return connection;
}

/**
* Returns the maximum length of the message content.
*/
protected int maxContentLength() {
return maxContentLength;
}

/**
* Specifies the maximum length of the message content.
*
* @param maxContentLength the maximum length of the message content. If the length of the message content
* exceeds this value, a {@link TooLongFrameException} will be raised
* @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
protected B maxContentLength(int maxContentLength) {
this.maxContentLength = maxContentLength;
return self();
}

/**
* Return {@code true} if HTTP header validation should be performed.
*/
protected boolean isValidateHttpHeaders() {
return validateHttpHeaders;
}

/**
* Specifies whether validation of HTTP headers should be performed.
*
* @param validate
* <ul>
* <li>{@code true} to validate HTTP headers in the http-codec</li>
* <li>{@code false} not to validate HTTP headers in the http-codec</li>
* </ul>
* @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
protected B validateHttpHeaders(boolean validate) {
validateHttpHeaders = validate;
return self();
}

/**
* Returns {@code true} if a read settings frame should be propagated along the channel pipeline.
*/
protected boolean isPropagateSettings() {
return propagateSettings;
}

/**
* Specifies whether a read settings frame should be propagated along the channel pipeline.
*
* @param propagate if {@code true} read settings will be passed along the pipeline. This can be useful
* to clients that need hold off sending data until they have received the settings.
* @return {@link AbstractInboundHttp2ToHttpAdapterBuilder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
protected B propagateSettings(boolean propagate) {
propagateSettings = propagate;
return self();
}

/**
* Builds/creates a new {@link InboundHttp2ToHttpAdapter} instance using this builder's current settings.
*/
protected T build() {
final T instance;
try {
instance = build(connection(), maxContentLength(),
isValidateHttpHeaders(), isPropagateSettings());
} catch (Throwable t) {
throw new IllegalStateException("failed to create a new InboundHttp2ToHttpAdapter", t);
}
connection.addListener(instance);
return instance;
}

/**
* Creates a new {@link InboundHttp2ToHttpAdapter} with the specified properties.
*/
protected abstract T build(Http2Connection connection, int maxContentLength,
boolean validateHttpHeaders, boolean propagateSettings) throws Exception;
}
Expand Up @@ -14,22 +14,22 @@
*/
package io.netty.handler.codec.http2;

import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpStatusClass;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;

import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.connectionError;
import static io.netty.util.internal.ObjectUtil.checkNotNull;

/**
* This adapter provides just header/data events from the HTTP message flow defined
* here <a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-8.1.">HTTP/2 Spec Message Flow</a>.
Expand Down Expand Up @@ -67,81 +67,17 @@ public FullHttpMessage copyIfNeeded(FullHttpMessage msg) {
protected final IntObjectMap<FullHttpMessage> messageMap;
private final boolean propagateSettings;

public static class Builder {

private final Http2Connection connection;
private int maxContentLength;
private boolean validateHttpHeaders;
private boolean propagateSettings;

/**
* Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}.
*
* @param connection The object which will provide connection notification events for the current connection
*/
public Builder(Http2Connection connection) {
this.connection = connection;
}

/**
* Specifies the maximum length of the message content.
*
* @param maxContentLength the maximum length of the message content. If the length of the message content
* exceeds this value, a {@link TooLongFrameException} will be raised
* @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
public Builder maxContentLength(int maxContentLength) {
this.maxContentLength = maxContentLength;
return this;
}

/**
* Specifies whether validation of HTTP headers should be performed.
*
* @param validate
* <ul>
* <li>{@code true} to validate HTTP headers in the http-codec</li>
* <li>{@code false} not to validate HTTP headers in the http-codec</li>
* </ul>
* @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
public Builder validateHttpHeaders(boolean validate) {
validateHttpHeaders = validate;
return this;
}

/**
* Specifies whether a read settings frame should be propagated alone the channel pipeline.
*
* @param propagate if {@code true} read settings will be passed along the pipeline. This can be useful
* to clients that need hold off sending data until they have received the settings.
* @return {@link Builder} the builder for the {@link InboundHttp2ToHttpAdapter}
*/
public Builder propagateSettings(boolean propagate) {
propagateSettings = propagate;
return this;
}

/**
* Builds/creates a new {@link InboundHttp2ToHttpAdapter} instance using this builders current settings.
*/
public InboundHttp2ToHttpAdapter build() {
InboundHttp2ToHttpAdapter instance = new InboundHttp2ToHttpAdapter(this);
connection.addListener(instance);
return instance;
}
}
protected InboundHttp2ToHttpAdapter(Http2Connection connection, int maxContentLength,
boolean validateHttpHeaders, boolean propagateSettings) {

protected InboundHttp2ToHttpAdapter(Builder builder) {
checkNotNull(builder.connection, "connection");
if (builder.maxContentLength <= 0) {
throw new IllegalArgumentException("maxContentLength must be a positive integer: "
+ builder.maxContentLength);
checkNotNull(connection, "connection");
if (maxContentLength <= 0) {
throw new IllegalArgumentException("maxContentLength: " + maxContentLength + " (expected: > 0)");
}
connection = builder.connection;
maxContentLength = builder.maxContentLength;
validateHttpHeaders = builder.validateHttpHeaders;
propagateSettings = builder.propagateSettings;
this.connection = connection;
this.maxContentLength = maxContentLength;
this.validateHttpHeaders = validateHttpHeaders;
this.propagateSettings = propagateSettings;
sendDetector = DEFAULT_SEND_DETECTOR;
messageMap = new IntObjectHashMap<FullHttpMessage>();
}
Expand Down Expand Up @@ -264,7 +200,7 @@ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int
throws Http2Exception {
FullHttpMessage msg = messageMap.get(streamId);
if (msg == null) {
throw connectionError(PROTOCOL_ERROR, "Data Frame recieved for unknown stream id %d", streamId);
throw connectionError(PROTOCOL_ERROR, "Data Frame received for unknown stream id %d", streamId);
}

ByteBuf content = msg.content();
Expand Down Expand Up @@ -316,7 +252,7 @@ public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promi
// A push promise should not be allowed to add headers to an existing stream
FullHttpMessage msg = processHeadersBegin(ctx, promisedStreamId, headers, false, false, false);
if (msg == null) {
throw connectionError(PROTOCOL_ERROR, "Push Promise Frame recieved for pre-existing stream id %d",
throw connectionError(PROTOCOL_ERROR, "Push Promise Frame received for pre-existing stream id %d",
promisedStreamId);
}

Expand Down
@@ -0,0 +1,62 @@
/*
* 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;

/**
* Builds an {@link InboundHttp2ToHttpAdapter}.
*/
public final class InboundHttp2ToHttpAdapterBuilder
extends AbstractInboundHttp2ToHttpAdapterBuilder<InboundHttp2ToHttpAdapter, InboundHttp2ToHttpAdapterBuilder> {

/**
* Creates a new {@link InboundHttp2ToHttpAdapter} builder for the specified {@link Http2Connection}.
*
* @param connection the object which will provide connection notification events
* for the current connection
*/
public InboundHttp2ToHttpAdapterBuilder(Http2Connection connection) {
super(connection);
}

@Override
public InboundHttp2ToHttpAdapterBuilder maxContentLength(int maxContentLength) {
return super.maxContentLength(maxContentLength);
}

@Override
public InboundHttp2ToHttpAdapterBuilder validateHttpHeaders(boolean validate) {
return super.validateHttpHeaders(validate);
}

@Override
public InboundHttp2ToHttpAdapterBuilder propagateSettings(boolean propagate) {
return super.propagateSettings(propagate);
}

@Override
public InboundHttp2ToHttpAdapter build() {
return super.build();
}

@Override
protected InboundHttp2ToHttpAdapter build(Http2Connection connection,
int maxContentLength,
boolean validateHttpHeaders,
boolean propagateSettings) throws Exception {

return new InboundHttp2ToHttpAdapter(connection, maxContentLength,
validateHttpHeaders, propagateSettings);
}
}
Expand Up @@ -43,27 +43,11 @@ public final class InboundHttp2ToHttpPriorityAdapter extends InboundHttp2ToHttpA
HttpConversionUtil.OUT_OF_MESSAGE_SEQUENCE_RETURN_CODE.toString());
private final IntObjectMap<HttpHeaders> outOfMessageFlowHeaders;

public static final class Builder extends InboundHttp2ToHttpAdapter.Builder {

/**
* Creates a new {@link InboundHttp2ToHttpPriorityAdapter} builder for the specified {@link Http2Connection}.
*
* @param connection The object which will provide connection notification events for the current connection
*/
public Builder(Http2Connection connection) {
super(connection);
}

@Override
public InboundHttp2ToHttpPriorityAdapter build() {
final InboundHttp2ToHttpPriorityAdapter instance = new InboundHttp2ToHttpPriorityAdapter(this);
instance.connection.addListener(instance);
return instance;
}
}
InboundHttp2ToHttpPriorityAdapter(Http2Connection connection, int maxContentLength,
boolean validateHttpHeaders,
boolean propagateSettings) {

InboundHttp2ToHttpPriorityAdapter(Builder builder) {
super(builder);
super(connection, maxContentLength, validateHttpHeaders, propagateSettings);
outOfMessageFlowHeaders = new IntObjectHashMap<HttpHeaders>();
}

Expand Down