Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@ public class H2Config {
private final int maxHeaderListSize;
private final boolean compressionEnabled;
private final int maxContinuations;
private final boolean originFrameEnabled;
private final int maxOriginSetSize;

H2Config(final int headerTableSize, final boolean pushEnabled, final int maxConcurrentStreams,
final int initialWindowSize, final int maxFrameSize, final int maxHeaderListSize,
final boolean compressionEnabled, final int maxContinuations) {
final boolean compressionEnabled, final int maxContinuations,
final boolean originFrameEnabled, final int maxOriginSetSize) {
super();
this.headerTableSize = headerTableSize;
this.pushEnabled = pushEnabled;
Expand All @@ -64,6 +67,8 @@ public class H2Config {
this.maxHeaderListSize = maxHeaderListSize;
this.compressionEnabled = compressionEnabled;
this.maxContinuations = maxContinuations;
this.originFrameEnabled = originFrameEnabled;
this.maxOriginSetSize = maxOriginSetSize;
}

public int getHeaderTableSize() {
Expand Down Expand Up @@ -98,6 +103,27 @@ public int getMaxContinuations() {
return maxContinuations;
}

/**
* Tests whether ORIGIN frames are enabled. Proxy clients that receive
* HTTP/2 frames directly from a proxy must disable this option.
*
* @return {@code true} if ORIGIN frames are enabled.
* @since 5.5
*/
public boolean isOriginFrameEnabled() {
return originFrameEnabled;
}

/**
* Returns the maximum number of origins retained for a connection. A value of
* {@code 0} means unlimited.
*
* @since 5.5
*/
public int getMaxOriginSetSize() {
return maxOriginSetSize;
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
Expand All @@ -109,6 +135,8 @@ public String toString() {
.append(", maxHeaderListSize=").append(this.maxHeaderListSize)
.append(", compressionEnabled=").append(this.compressionEnabled)
.append(", maxContinuations=").append(this.maxContinuations)
.append(", originFrameEnabled=").append(this.originFrameEnabled)
.append(", maxOriginSetSize=").append(this.maxOriginSetSize)
.append("]");
return builder.toString();
}
Expand Down Expand Up @@ -142,7 +170,10 @@ public static H2Config.Builder copy(final H2Config config) {
.setInitialWindowSize(config.getInitialWindowSize())
.setMaxFrameSize(config.getMaxFrameSize())
.setMaxHeaderListSize(config.getMaxHeaderListSize())
.setCompressionEnabled(config.isCompressionEnabled());
.setCompressionEnabled(config.isCompressionEnabled())
.setMaxContinuations(config.getMaxContinuations())
.setOriginFrameEnabled(config.isOriginFrameEnabled())
.setMaxOriginSetSize(config.getMaxOriginSetSize());
}

public static class Builder {
Expand All @@ -155,6 +186,8 @@ public static class Builder {
private int maxHeaderListSize;
private boolean compressionEnabled;
private int maxContinuations;
private boolean originFrameEnabled;
private int maxOriginSetSize;

Builder() {
this.headerTableSize = INIT_HEADER_TABLE_SIZE * 2;
Expand All @@ -165,6 +198,8 @@ public static class Builder {
this.maxHeaderListSize = FrameConsts.MAX_FRAME_SIZE;
this.compressionEnabled = true;
this.maxContinuations = 100;
this.originFrameEnabled = true;
this.maxOriginSetSize = 1000;
}

public Builder setHeaderTableSize(final int headerTableSize) {
Expand Down Expand Up @@ -216,6 +251,30 @@ public Builder setMaxContinuations(final int maxContinuations) {
return this;
}

/**
* Enables or disables ORIGIN frame processing. This should be set
* to {@code false} by clients that receive HTTP/2 frames directly from a
* proxy, which must ignore any ORIGIN frames received from it.
*
* @since 5.5
*/
public Builder setOriginFrameEnabled(final boolean originFrameEnabled) {
this.originFrameEnabled = originFrameEnabled;
return this;
}

/**
* Sets the maximum number of origins retained for one connection. A value
* of {@code 0} disables the limit. Exceeding a positive limit terminates
* the connection with {@code ENHANCE_YOUR_CALM}.
*
* @since 5.5
*/
public Builder setMaxOriginSetSize(final int maxOriginSetSize) {
this.maxOriginSetSize = Args.notNegative(maxOriginSetSize, "Max Origin Set size");
return this;
}

public H2Config build() {
return new H2Config(
headerTableSize,
Expand All @@ -225,7 +284,9 @@ public H2Config build() {
maxFrameSize,
maxHeaderListSize,
compressionEnabled,
maxContinuations);
maxContinuations,
originFrameEnabled,
maxOriginSetSize);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,16 @@ public RawFrame createPriorityUpdate(final ByteBuffer payload) {
return new RawFrame(FrameType.PRIORITY_UPDATE.getValue(), 0, 0, payload);
}

/**
* Creates an ORIGIN frame.
*
* @param payload the encoded sequence of Origin-Entry values, or {@code null}
* for an empty Origin Set advertisement.
* @return the ORIGIN frame.
* @since 5.5
*/
public RawFrame createOrigin(final ByteBuffer payload) {
return new RawFrame(FrameType.ORIGIN.getValue(), 0, 0, payload);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum FrameType {
GOAWAY(0x07),
WINDOW_UPDATE(0x08),
CONTINUATION(0x09),
ORIGIN(0x0c),
PRIORITY_UPDATE(0x10); // 16

final int value;
Expand Down Expand Up @@ -73,7 +74,7 @@ public static FrameType valueOf(final int value) {
if (value < 0 || value >= LOOKUP_TABLE.length) {
return null;
}
return LOOKUP_TABLE[value]; // may be null for gaps (e.g., 0x0A..0x0F)
return LOOKUP_TABLE[value]; // may be null for gaps (e.g., 0x0A)
}

public static String toString(final int value) {
Expand All @@ -88,4 +89,4 @@ public static String toString(final int value) {
public boolean same(final int rawType) {
return this.value == rawType;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,18 @@ HttpProcessor getHttpProcessor() {
return httpProcessor;
}

final H2Config getLocalConfig() {
return localConfig;
}

final FrameFactory getFrameFactory() {
return frameFactory;
}

final int getMaxFramePayloadSize() {
return Math.min(remoteConfig.getMaxFrameSize(), outputBuffer.getMaxFramePayloadSize());
}

void submitCommand(final Command command) {
ioSession.enqueue(command, Command.Priority.NORMAL);
}
Expand Down Expand Up @@ -261,6 +273,14 @@ abstract H2StreamHandler outgoingPushPromise(H2StreamChannel channel,

abstract boolean allowGracefulAbort(H2Stream stream);

/** Called after the local SETTINGS frame has been queued. */
void onConnectComplete() throws HttpException, IOException {
}

/** Handles an ORIGIN frame. The server-side default is to ignore it. */
void consumeOriginFrame(final RawFrame frame) throws HttpException, IOException {
}

private int updateWindow(final AtomicInteger window, final int delta) throws ArithmeticException {
for (;;) {
final int current = window.get();
Expand Down Expand Up @@ -324,6 +344,10 @@ private void commitFrame(final RawFrame frame) throws IOException {
updateLastActivity();
}

final void commitConnectionFrame(final RawFrame frame) throws IOException {
commitFrame(frame);
}

private void commitHeaders(
final int streamId, final List<? extends Header> headers, final boolean endStream) throws IOException {
if (streamListener != null) {
Expand Down Expand Up @@ -462,6 +486,7 @@ public final void onConnect() throws HttpException, IOException {

commitFrame(settingsFrame);
localSettingState = SettingsHandshake.TRANSMITTED;
onConnectComplete();
maximizeWindow(0, connInputWindow);

if (streamListener != null) {
Expand Down Expand Up @@ -1015,6 +1040,9 @@ private void consumeFrame(final RawFrame frame) throws HttpException, IOExceptio
break;
case PRIORITY:
break;
case ORIGIN:
consumeOriginFrame(frame);
break;
case PUSH_PROMISE: {
acceptPushFrame();
if (streamId == 0) {
Expand Down Expand Up @@ -1768,4 +1796,4 @@ private void validateStreamTimeouts() throws IOException {
private void updateLastActivity() {
this.lastActivityNanos = System.nanoTime();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
Expand Down Expand Up @@ -73,10 +74,12 @@ class ClientH2StreamHandler implements H2StreamHandler {
private final AtomicBoolean requestCommitted;
private final AtomicBoolean failed;
private final AtomicBoolean done;
private final H2OriginSet originSet;

private volatile String method = null;
private volatile long declaredContentLen = -1;
private volatile long actualContentLen = 0;
private volatile HttpHost requestOrigin;

ClientH2StreamHandler(
final H2StreamChannel outputChannel,
Expand All @@ -85,6 +88,17 @@ class ClientH2StreamHandler implements H2StreamHandler {
final AsyncClientExchangeHandler exchangeHandler,
final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
final HttpCoreContext context) {
this(outputChannel, httpProcessor, connMetrics, exchangeHandler, pushHandlerFactory, context, null);
}

ClientH2StreamHandler(
final H2StreamChannel outputChannel,
final HttpProcessor httpProcessor,
final BasicHttpConnectionMetrics connMetrics,
final AsyncClientExchangeHandler exchangeHandler,
final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
final HttpCoreContext context,
final H2OriginSet originSet) {
this.outputChannel = outputChannel;
this.dataChannel = new DataStreamChannel() {

Expand Down Expand Up @@ -116,6 +130,7 @@ public void endStream() throws IOException {
this.exchangeHandler = exchangeHandler;
this.pushHandlerFactory = pushHandlerFactory;
this.context = context;
this.originSet = originSet;
this.requestCommitted = new AtomicBoolean();
this.failed = new AtomicBoolean();
this.done = new AtomicBoolean();
Expand Down Expand Up @@ -147,6 +162,11 @@ private void commitRequest(final HttpRequest request, final EntityDetails entity

httpProcessor.process(request, entityDetails, context);

requestOrigin = H2OriginFrameCodec.fromRequest(request);
if (originSet != null) {
originSet.ensureAllowed(requestOrigin);
}

method = request.getMethod();

final List<Header> headers = DefaultH2RequestConverter.INSTANCE.convert(request);
Expand Down Expand Up @@ -215,6 +235,10 @@ public void consumeHeader(final List<Header> headers, final boolean endStream) t
return;
}

if (status == HttpStatus.SC_MISDIRECTED_REQUEST && originSet != null) {
originSet.remove(requestOrigin);
}

if (!Method.HEAD.isSame(method) && MessageSupport.canResponseHaveBody(method, response)) {
declaredContentLen = MessageSupport.getContentLength(response);
if (endStream) {
Expand Down Expand Up @@ -303,4 +327,3 @@ public String toString() {
}

}

Loading
Loading